2017-03-27 15:07:29 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
"github.com/gdamore/tcell"
|
2018-02-14 14:57:02 -06:00
|
|
|
)
|
2017-03-27 15:07:29 -05:00
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
// EventEngineStopRun stop the run of the engine
|
|
|
|
type EventEngineStopRun struct {
|
|
|
|
EventGame
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// NewEngine creates new engine
|
2018-02-14 14:57:02 -06:00
|
|
|
func NewEngine() {
|
|
|
|
engine = &Engine{
|
2019-12-01 15:03:27 -06:00
|
|
|
chanStop: make(chan struct{}),
|
|
|
|
chanEventKey: make(chan *tcell.EventKey, 8),
|
|
|
|
mode: engineModeGameOver,
|
|
|
|
tickTime: time.Hour,
|
|
|
|
ranking: NewRanking(),
|
|
|
|
ai: NewAi(),
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
2019-12-01 15:03:27 -06:00
|
|
|
board.Clear()
|
|
|
|
go engine.Run()
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// Run runs the engine
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) Run() {
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine Run start")
|
2017-03-27 15:07:29 -05:00
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
var event tcell.Event
|
|
|
|
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
event = screen.PollEvent()
|
|
|
|
switch eventType := event.(type) {
|
|
|
|
case *tcell.EventKey:
|
|
|
|
select {
|
|
|
|
case engine.chanEventKey <- eventType:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
case *EventEngineStopRun:
|
|
|
|
break loop
|
|
|
|
case *tcell.EventResize:
|
|
|
|
view.RefreshScreen()
|
|
|
|
default:
|
|
|
|
logger.Printf("event type %T", eventType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Println("Engine Run end")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the game
|
|
|
|
func (engine *Engine) Start() {
|
|
|
|
logger.Println("Engine Start start")
|
2017-03-27 15:07:29 -05:00
|
|
|
|
|
|
|
engine.timer = time.NewTimer(engine.tickTime)
|
|
|
|
engine.timer.Stop()
|
|
|
|
engine.aiTimer = time.NewTimer(engine.tickTime)
|
|
|
|
engine.aiTimer.Stop()
|
|
|
|
|
|
|
|
view.RefreshScreen()
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
var eventKey *tcell.EventKey
|
2017-03-27 15:07:29 -05:00
|
|
|
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-engine.chanStop:
|
|
|
|
break loop
|
|
|
|
default:
|
|
|
|
select {
|
2019-12-01 15:03:27 -06:00
|
|
|
case eventKey = <-engine.chanEventKey:
|
|
|
|
engine.ProcessEventKey(eventKey)
|
2017-03-27 15:07:29 -05:00
|
|
|
view.RefreshScreen()
|
|
|
|
case <-engine.timer.C:
|
|
|
|
engine.tick()
|
|
|
|
case <-engine.aiTimer.C:
|
2018-02-14 14:57:02 -06:00
|
|
|
engine.ai.ProcessQueue()
|
2019-01-09 20:30:45 -06:00
|
|
|
engine.aiTimer.Reset(engine.tickTime / aiTickDivider)
|
2017-03-27 15:07:29 -05:00
|
|
|
case <-engine.chanStop:
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
screen.PostEventWait(&EventEngineStopRun{})
|
|
|
|
|
|
|
|
logger.Println("Engine Start end")
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
// Stop the game
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) Stop() {
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine Stop start")
|
2017-03-27 15:07:29 -05:00
|
|
|
|
|
|
|
if !engine.stopped {
|
|
|
|
engine.stopped = true
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeStopped
|
2017-03-27 15:07:29 -05:00
|
|
|
close(engine.chanStop)
|
|
|
|
}
|
|
|
|
engine.timer.Stop()
|
|
|
|
engine.aiTimer.Stop()
|
|
|
|
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine Stop end")
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
// Pause the game
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) Pause() {
|
|
|
|
if !engine.timer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-engine.timer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !engine.aiTimer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-engine.aiTimer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModePaused
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2019-12-01 15:03:27 -06:00
|
|
|
// UnPause the game
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) UnPause() {
|
|
|
|
engine.timer.Reset(engine.tickTime)
|
|
|
|
if engine.aiEnabled {
|
2019-01-09 20:30:45 -06:00
|
|
|
engine.aiTimer.Reset(engine.tickTime / aiTickDivider)
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeRunWithAI
|
|
|
|
} else {
|
|
|
|
engine.mode = engineModeRun
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// PreviewBoard sets previewBoard to true
|
2018-02-14 14:57:02 -06:00
|
|
|
func (engine *Engine) PreviewBoard() {
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModePreview
|
2018-02-14 14:57:02 -06:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// NewGame resets board and starts a new game
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) NewGame() {
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine NewGame start")
|
2017-03-27 15:07:29 -05:00
|
|
|
|
2018-02-14 14:57:02 -06:00
|
|
|
board.Clear()
|
2017-03-27 15:07:29 -05:00
|
|
|
engine.tickTime = 480 * time.Millisecond
|
|
|
|
engine.score = 0
|
|
|
|
engine.level = 1
|
|
|
|
engine.deleteLines = 0
|
|
|
|
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
select {
|
2019-12-01 15:03:27 -06:00
|
|
|
case <-engine.chanEventKey:
|
2017-03-27 15:07:29 -05:00
|
|
|
default:
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if engine.aiEnabled {
|
|
|
|
engine.ai.GetBestQueue()
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeRunWithAI
|
|
|
|
} else {
|
|
|
|
engine.mode = engineModeRun
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
engine.UnPause()
|
|
|
|
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine NewGame end")
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// ResetTimer resets the time for lock delay or tick time
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) ResetTimer(duration time.Duration) {
|
|
|
|
if !engine.timer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-engine.timer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if duration == 0 {
|
|
|
|
// duration 0 means tick time
|
|
|
|
engine.timer.Reset(engine.tickTime)
|
|
|
|
} else {
|
|
|
|
// duration is lock delay
|
|
|
|
engine.timer.Reset(duration)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// AiGetBestQueue calls AI to get best queue
|
2018-02-14 14:57:02 -06:00
|
|
|
func (engine *Engine) AiGetBestQueue() {
|
2017-03-27 15:07:29 -05:00
|
|
|
if !engine.aiEnabled {
|
|
|
|
return
|
|
|
|
}
|
2018-02-14 14:57:02 -06:00
|
|
|
go engine.ai.GetBestQueue()
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// tick move mino down and refreshes screen
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) tick() {
|
|
|
|
board.MinoMoveDown()
|
|
|
|
view.RefreshScreen()
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// AddDeleteLines adds deleted lines to score
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) AddDeleteLines(lines int) {
|
|
|
|
engine.deleteLines += lines
|
|
|
|
if engine.deleteLines > 999999 {
|
|
|
|
engine.deleteLines = 999999
|
|
|
|
}
|
|
|
|
|
|
|
|
switch lines {
|
|
|
|
case 1:
|
|
|
|
engine.AddScore(40 * (engine.level + 1))
|
|
|
|
case 2:
|
|
|
|
engine.AddScore(100 * (engine.level + 1))
|
|
|
|
case 3:
|
|
|
|
engine.AddScore(300 * (engine.level + 1))
|
|
|
|
case 4:
|
|
|
|
engine.AddScore(1200 * (engine.level + 1))
|
|
|
|
}
|
|
|
|
|
|
|
|
if engine.level < engine.deleteLines/10 {
|
|
|
|
engine.LevelUp()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// AddScore adds to score
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) AddScore(add int) {
|
|
|
|
engine.score += add
|
2018-02-14 14:57:02 -06:00
|
|
|
if engine.score > 9999999 {
|
|
|
|
engine.score = 9999999
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// LevelUp goes up a level
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) LevelUp() {
|
|
|
|
if engine.level >= 30 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
engine.level++
|
|
|
|
switch {
|
|
|
|
case engine.level > 29:
|
|
|
|
engine.tickTime = 10 * time.Millisecond
|
|
|
|
case engine.level > 25:
|
|
|
|
engine.tickTime = 20 * time.Millisecond
|
|
|
|
case engine.level > 19:
|
|
|
|
// 50 to 30
|
|
|
|
engine.tickTime = time.Duration(10*(15-engine.level/2)) * time.Millisecond
|
|
|
|
case engine.level > 9:
|
|
|
|
// 150 to 60
|
|
|
|
engine.tickTime = time.Duration(10*(25-engine.level)) * time.Millisecond
|
|
|
|
default:
|
|
|
|
// 480 to 160
|
|
|
|
engine.tickTime = time.Duration(10*(52-4*engine.level)) * time.Millisecond
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// GameOver pauses engine and sets to game over
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) GameOver() {
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine GameOver start")
|
2017-03-27 15:07:29 -05:00
|
|
|
|
|
|
|
engine.Pause()
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeGameOver
|
2017-03-27 15:07:29 -05:00
|
|
|
|
2018-02-14 14:57:02 -06:00
|
|
|
view.ShowGameOverAnimation()
|
2017-03-27 15:07:29 -05:00
|
|
|
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
select {
|
2019-12-01 15:03:27 -06:00
|
|
|
case <-engine.chanEventKey:
|
2017-03-27 15:07:29 -05:00
|
|
|
default:
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 14:57:02 -06:00
|
|
|
engine.ranking.InsertScore(uint64(engine.score))
|
|
|
|
engine.ranking.Save()
|
|
|
|
|
2018-06-01 10:16:36 -05:00
|
|
|
logger.Println("Engine GameOver end")
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// EnabledAi enables the AI
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) EnabledAi() {
|
|
|
|
engine.aiEnabled = true
|
2018-02-14 14:57:02 -06:00
|
|
|
go engine.ai.GetBestQueue()
|
2019-01-09 20:30:45 -06:00
|
|
|
engine.aiTimer.Reset(engine.tickTime / aiTickDivider)
|
2017-03-27 15:07:29 -05:00
|
|
|
}
|
|
|
|
|
2018-10-02 20:21:08 -05:00
|
|
|
// DisableAi disables the AI
|
2017-03-27 15:07:29 -05:00
|
|
|
func (engine *Engine) DisableAi() {
|
2018-02-14 14:57:02 -06:00
|
|
|
engine.aiEnabled = false
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeRun
|
2017-03-27 15:07:29 -05:00
|
|
|
if !engine.aiTimer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-engine.aiTimer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-09 20:30:45 -06:00
|
|
|
|
|
|
|
// EnabledEditMode enables edit mode
|
|
|
|
func (engine *Engine) EnabledEditMode() {
|
|
|
|
edit.EnabledEditMode()
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModeEdit
|
2019-01-09 20:30:45 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// DisableEditMode disables edit mode
|
|
|
|
func (engine *Engine) DisableEditMode() {
|
|
|
|
edit.DisableEditMode()
|
2019-12-01 15:03:27 -06:00
|
|
|
engine.mode = engineModePreview
|
2019-01-09 20:30:45 -06:00
|
|
|
}
|