go-tetris/engine.go

246 lines
4.3 KiB
Go

package main
import (
"time"
"github.com/nsf/termbox-go"
)
func NewEngine() {
engine = &Engine{
chanStop: make(chan struct{}, 1),
gameOver: true,
tickTime: time.Hour,
ai: NewAi(),
}
}
func (engine *Engine) Run() {
logger.Info("Engine Run start")
var event *termbox.Event
engine.timer = time.NewTimer(engine.tickTime)
engine.timer.Stop()
engine.aiTimer = time.NewTimer(engine.tickTime)
engine.aiTimer.Stop()
engine.ranking = NewRanking()
board.Clear()
view.RefreshScreen()
engine.keyInput = NewKeyInput()
go engine.keyInput.Run()
loop:
for {
select {
case <-engine.chanStop:
break loop
default:
select {
case event = <-engine.keyInput.chanKeyInput:
engine.keyInput.ProcessEvent(event)
view.RefreshScreen()
case <-engine.timer.C:
engine.tick()
case <-engine.aiTimer.C:
engine.ai.ProcessQueue()
engine.aiTimer.Reset(engine.tickTime / 6)
case <-engine.chanStop:
break loop
}
}
}
logger.Info("Engine Run end")
}
func (engine *Engine) Stop() {
logger.Info("Engine Stop start")
if !engine.stopped {
engine.stopped = true
close(engine.chanStop)
}
engine.timer.Stop()
engine.aiTimer.Stop()
logger.Info("Engine Stop end")
}
func (engine *Engine) Pause() {
if !engine.timer.Stop() {
select {
case <-engine.timer.C:
default:
}
}
if !engine.aiTimer.Stop() {
select {
case <-engine.aiTimer.C:
default:
}
}
engine.paused = true
}
func (engine *Engine) UnPause() {
engine.timer.Reset(engine.tickTime)
if engine.aiEnabled {
engine.aiTimer.Reset(engine.tickTime / 6)
}
engine.paused = false
}
func (engine *Engine) PreviewBoard() {
engine.previewBoard = true
}
func (engine *Engine) NewGame() {
logger.Info("Engine NewGame start")
board.Clear()
engine.tickTime = 480 * time.Millisecond
engine.score = 0
engine.level = 1
engine.deleteLines = 0
loop:
for {
select {
case <-engine.keyInput.chanKeyInput:
default:
break loop
}
}
engine.previewBoard = false
engine.gameOver = false
if engine.aiEnabled {
engine.ai.GetBestQueue()
}
engine.UnPause()
logger.Info("Engine NewGame end")
}
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)
}
}
func (engine *Engine) AiGetBestQueue() {
if !engine.aiEnabled {
return
}
go engine.ai.GetBestQueue()
}
func (engine *Engine) tick() {
board.MinoMoveDown()
view.RefreshScreen()
}
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()
}
}
func (engine *Engine) AddScore(add int) {
engine.score += add
if engine.score > 9999999 {
engine.score = 9999999
}
}
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
}
}
func (engine *Engine) GameOver() {
logger.Info("Engine GameOver start")
engine.Pause()
engine.gameOver = true
view.ShowGameOverAnimation()
loop:
for {
select {
case <-engine.keyInput.chanKeyInput:
default:
break loop
}
}
engine.ranking.InsertScore(uint64(engine.score))
engine.ranking.Save()
logger.Info("Engine GameOver end")
}
func (engine *Engine) EnabledAi() {
engine.aiEnabled = true
go engine.ai.GetBestQueue()
engine.aiTimer.Reset(engine.tickTime / 6)
}
func (engine *Engine) DisableAi() {
engine.aiEnabled = false
if !engine.aiTimer.Stop() {
select {
case <-engine.aiTimer.C:
default:
}
}
}