From ab91e02f036b0994607d36d75eeafedea37b0f75 Mon Sep 17 00:00:00 2001 From: MichaelS11 Date: Sun, 1 Dec 2019 13:03:27 -0800 Subject: [PATCH] Converted to tcell for terminal --- ai.go | 18 +-- board.go | 90 ++++++------- boards.go | 58 ++++----- colorTest/main.go | 65 ++++++++++ edit.go | 7 +- engine.go | 90 +++++++++---- engineKeyInput.go | 161 +++++++++++++++++++++++ globals.go | 53 +++++--- keyInput.go | 185 -------------------------- mino.go | 18 +-- mino_test.go | 30 ----- minos.go | 46 +++---- tetris.go | 12 +- tetris_test.go | 42 ++++++ view.go | 324 +++++++++++++++++++++++++--------------------- 15 files changed, 667 insertions(+), 532 deletions(-) create mode 100644 colorTest/main.go create mode 100644 engineKeyInput.go delete mode 100644 keyInput.go create mode 100644 tetris_test.go diff --git a/ai.go b/ai.go index 36ae97d..a431653 100644 --- a/ai.go +++ b/ai.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/nsf/termbox-go" -) - // NewAi creates a new AI func NewAi() *Ai { ai := Ai{} @@ -54,15 +50,15 @@ func (ai *Ai) GetBestQueue() { } switch currentMino.minoRotation[0][1][1] { - case termbox.ColorCyan, termbox.ColorGreen, termbox.ColorRed: + case colorCyan, colorGreen, colorRed: rotations1 = 2 - case termbox.ColorYellow: + case colorYellow: rotations1 = 1 } switch previewMino.minoRotation[0][1][1] { - case termbox.ColorCyan, termbox.ColorGreen, termbox.ColorRed: + case colorCyan, colorGreen, colorRed: rotations2 = 2 - case termbox.ColorYellow: + case colorYellow: rotations2 = 1 } @@ -190,7 +186,7 @@ func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int for j = 0; j < board.height; j++ { board.fullLinesY[j] = true for i = 0; i < board.width; i++ { - if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { + if board.colors[i][j] == colorBlank && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { board.fullLinesY[j] = false break } @@ -210,7 +206,7 @@ func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int if board.fullLinesY[j] { fullLinesFound++ } else { - if board.colors[i][j] != blankColor || mino1.isMinoAtLocation(i, j) || mino2.isMinoAtLocation(i, j) { + if board.colors[i][j] != colorBlank || mino1.isMinoAtLocation(i, j) || mino2.isMinoAtLocation(i, j) { found = j break } @@ -229,7 +225,7 @@ func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int foundLast = found + fullLines - fullLinesFound for j++; j < board.height; j++ { - if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { + if board.colors[i][j] == colorBlank && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) { holes++ } } diff --git a/board.go b/board.go index 576f41b..7e7998b 100644 --- a/board.go +++ b/board.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // NewBoard creates a new clear board @@ -20,14 +20,14 @@ func ChangeBoardSize(width int, height int) { } newBoard := &Board{width: width, height: height, boardsIndex: board.boardsIndex} - newBoard.colors = make([][]termbox.Attribute, width) + newBoard.colors = make([][]tcell.Color, width) for i := 0; i < width; i++ { - newBoard.colors[i] = make([]termbox.Attribute, height) + newBoard.colors[i] = make([]tcell.Color, height) for j := 0; j < height; j++ { if i < board.width && j < board.height { newBoard.colors[i][j] = board.colors[i][j] } else { - newBoard.colors[i][j] = blankColor + newBoard.colors[i][j] = colorBlank } } } @@ -53,9 +53,9 @@ func ChangeBoardSize(width int, height int) { func (board *Board) Clear() { board.width = len(boards[board.boardsIndex].colors) board.height = len(boards[board.boardsIndex].colors[0]) - board.colors = make([][]termbox.Attribute, board.width) + board.colors = make([][]tcell.Color, board.width) for i := 0; i < board.width; i++ { - board.colors[i] = make([]termbox.Attribute, board.height) + board.colors[i] = make([]tcell.Color, board.height) copy(board.colors[i], boards[board.boardsIndex].colors[i]) } board.rotation = make([][]int, board.width) @@ -72,7 +72,7 @@ func (board *Board) Clear() { func (board *Board) EmptyBoard() { for i := 0; i < board.width; i++ { for j := 0; j < board.height; j++ { - board.colors[i][j] = blankColor + board.colors[i][j] = colorBlank } } for i := 0; i < board.width; i++ { @@ -272,7 +272,7 @@ func (board *Board) fullLines() []int { // isFullLine checks if line is full func (board *Board) isFullLine(j int) bool { for i := 0; i < board.width; i++ { - if board.colors[i][j] == blankColor { + if board.colors[i][j] == colorBlank { return false } } @@ -282,7 +282,7 @@ func (board *Board) isFullLine(j int) bool { // deleteLine deletes the line func (board *Board) deleteLine(line int) { for i := 0; i < board.width; i++ { - board.colors[i][line] = blankColor + board.colors[i][line] = colorBlank } for j := line; j > 0; j-- { for i := 0; i < board.width; i++ { @@ -291,12 +291,12 @@ func (board *Board) deleteLine(line int) { } } for i := 0; i < board.width; i++ { - board.colors[i][0] = blankColor + board.colors[i][0] = colorBlank } } // SetColor sets the color and rotation of board location -func (board *Board) SetColor(x int, y int, color termbox.Attribute, rotation int) { +func (board *Board) SetColor(x int, y int, color tcell.Color, rotation int) { board.colors[x][y] = color if rotation < 0 { return @@ -337,7 +337,7 @@ func (board *Board) ValidBlockLocation(x int, y int, mustBeOnBoard bool) bool { } } if y > -1 { - if board.colors[x][y] != blankColor { + if board.colors[x][y] != colorBlank { return false } } @@ -353,7 +353,7 @@ func ValidDisplayLocation(x int, y int) bool { func (board *Board) DrawBoard() { for i := 0; i < board.width; i++ { for j := 0; j < board.height; j++ { - if board.colors[i][j] != blankColor { + if board.colors[i][j] != colorBlank { view.DrawBlock(i, j, board.colors[i][j], board.rotation[i][j]) } } @@ -393,21 +393,21 @@ func (board *Board) printDebugBoard() { for j := 0; j < board.height; j++ { for i := 0; i < board.width; i++ { switch board.colors[i][j] { - case blankColor: + case colorBlank: fmt.Print(".") - case termbox.ColorBlue: + case colorBlue: fmt.Print("B") - case termbox.ColorCyan: + case colorCyan: fmt.Print("C") - case termbox.ColorGreen: + case colorGreen: fmt.Print("G") - case termbox.ColorMagenta: + case colorMagenta: fmt.Print("M") - case termbox.ColorRed: + case colorRed: fmt.Print("R") - case termbox.ColorWhite: + case colorWhite: fmt.Print("W") - case termbox.ColorYellow: + case colorYellow: fmt.Print("Y") default: fmt.Print("U") @@ -423,21 +423,21 @@ func (board *Board) getDebugBoard() []string { for j := 0; j < board.height; j++ { for i := 0; i < board.width; i++ { switch board.colors[i][j] { - case blankColor: + case colorBlank: lines[j] += "." - case termbox.ColorBlue: + case colorBlue: lines[j] += "B" - case termbox.ColorCyan: + case colorCyan: lines[j] += "C" - case termbox.ColorGreen: + case colorGreen: lines[j] += "G" - case termbox.ColorMagenta: + case colorMagenta: lines[j] += "M" - case termbox.ColorRed: + case colorRed: lines[j] += "R" - case termbox.ColorWhite: + case colorWhite: lines[j] += "W" - case termbox.ColorYellow: + case colorYellow: lines[j] += "Y" default: lines[j] += "U" @@ -453,40 +453,40 @@ func (board *Board) getDebugBoardWithMino(mino *Mino) []string { for j := 0; j < board.height; j++ { for i := 0; i < board.width; i++ { switch mino.getMinoColorAtLocation(i, j) { - case blankColor: + case colorBlank: switch board.colors[i][j] { - case blankColor: + case colorBlank: lines[j] += "." - case termbox.ColorBlue: + case colorBlue: lines[j] += "B" - case termbox.ColorCyan: + case colorCyan: lines[j] += "C" - case termbox.ColorGreen: + case colorGreen: lines[j] += "G" - case termbox.ColorMagenta: + case colorMagenta: lines[j] += "M" - case termbox.ColorRed: + case colorRed: lines[j] += "R" - case termbox.ColorWhite: + case colorWhite: lines[j] += "W" - case termbox.ColorYellow: + case colorYellow: lines[j] += "Y" default: lines[j] += "U" } - case termbox.ColorBlue: + case colorBlue: lines[j] += "b" - case termbox.ColorCyan: + case colorCyan: lines[j] += "c" - case termbox.ColorGreen: + case colorGreen: lines[j] += "g" - case termbox.ColorMagenta: + case colorMagenta: lines[j] += "m" - case termbox.ColorRed: + case colorRed: lines[j] += "r" - case termbox.ColorWhite: + case colorWhite: lines[j] += "w" - case termbox.ColorYellow: + case colorYellow: lines[j] += "y" default: lines[j] += "u" diff --git a/boards.go b/boards.go index f5c02bb..4b0e6b4 100644 --- a/boards.go +++ b/boards.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "os" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // loadBoards loads the internal boards @@ -24,29 +24,29 @@ func loadBoards() error { for boardNum, boardLoad := range boardsJSON { aBoards := Boards{name: boardLoad.Name} - aBoards.colors = make([][]termbox.Attribute, len(boardLoad.Mino)) + aBoards.colors = make([][]tcell.Color, len(boardLoad.Mino)) aBoards.rotation = boardLoad.Rotation for i := 0; i < len(boardLoad.Mino); i++ { - aBoards.colors[i] = make([]termbox.Attribute, len(boardLoad.Mino[i])) + aBoards.colors[i] = make([]tcell.Color, len(boardLoad.Mino[i])) for j := 0; j < len(boardLoad.Mino[i]); j++ { switch boardLoad.Mino[i][j] { case "b": - aBoards.colors[i][j] = blankColor + aBoards.colors[i][j] = colorBlank case "i": - aBoards.colors[i][j] = termbox.ColorCyan + aBoards.colors[i][j] = colorCyan case "j": - aBoards.colors[i][j] = termbox.ColorBlue + aBoards.colors[i][j] = colorBlue case "l": - aBoards.colors[i][j] = termbox.ColorWhite + aBoards.colors[i][j] = colorWhite case "o": - aBoards.colors[i][j] = termbox.ColorYellow + aBoards.colors[i][j] = colorYellow case "s": - aBoards.colors[i][j] = termbox.ColorGreen + aBoards.colors[i][j] = colorGreen case "t": - aBoards.colors[i][j] = termbox.ColorMagenta + aBoards.colors[i][j] = colorMagenta case "z": - aBoards.colors[i][j] = termbox.ColorRed + aBoards.colors[i][j] = colorRed } } } @@ -90,29 +90,29 @@ func loadUserBoards() error { for _, boardLoad := range settings.Boards { aBoards := Boards{name: boardLoad.Name} - aBoards.colors = make([][]termbox.Attribute, len(boardLoad.Mino)) + aBoards.colors = make([][]tcell.Color, len(boardLoad.Mino)) aBoards.rotation = boardLoad.Rotation for i := 0; i < len(boardLoad.Mino); i++ { - aBoards.colors[i] = make([]termbox.Attribute, len(boardLoad.Mino[i])) + aBoards.colors[i] = make([]tcell.Color, len(boardLoad.Mino[i])) for j := 0; j < len(boardLoad.Mino[i]); j++ { switch boardLoad.Mino[i][j] { case "b": - aBoards.colors[i][j] = blankColor + aBoards.colors[i][j] = colorBlank case "i": - aBoards.colors[i][j] = termbox.ColorCyan + aBoards.colors[i][j] = colorCyan case "j": - aBoards.colors[i][j] = termbox.ColorBlue + aBoards.colors[i][j] = colorBlue case "l": - aBoards.colors[i][j] = termbox.ColorWhite + aBoards.colors[i][j] = colorWhite case "o": - aBoards.colors[i][j] = termbox.ColorYellow + aBoards.colors[i][j] = colorYellow case "s": - aBoards.colors[i][j] = termbox.ColorGreen + aBoards.colors[i][j] = colorGreen case "t": - aBoards.colors[i][j] = termbox.ColorMagenta + aBoards.colors[i][j] = colorMagenta case "z": - aBoards.colors[i][j] = termbox.ColorRed + aBoards.colors[i][j] = colorRed } } } @@ -140,21 +140,21 @@ func saveUserBoards() error { aBoards.Mino[i] = make([]string, len(boards[x].colors[i])) for j := 0; j < len(boards[x].colors[i]); j++ { switch boards[x].colors[i][j] { - case blankColor: + case colorBlank: aBoards.Mino[i][j] = "b" - case termbox.ColorCyan: + case colorCyan: aBoards.Mino[i][j] = "i" - case termbox.ColorBlue: + case colorBlue: aBoards.Mino[i][j] = "j" - case termbox.ColorWhite: + case colorWhite: aBoards.Mino[i][j] = "l" - case termbox.ColorYellow: + case colorYellow: aBoards.Mino[i][j] = "o" - case termbox.ColorGreen: + case colorGreen: aBoards.Mino[i][j] = "s" - case termbox.ColorMagenta: + case colorMagenta: aBoards.Mino[i][j] = "t" - case termbox.ColorRed: + case colorRed: aBoards.Mino[i][j] = "z" } } diff --git a/colorTest/main.go b/colorTest/main.go new file mode 100644 index 0000000..8a1473d --- /dev/null +++ b/colorTest/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + "strconv" + + "github.com/gdamore/tcell" +) + +var ( + screen tcell.Screen + x = 0 + y = 1 +) + +func main() { + var err error + screen, err = tcell.NewScreen() + if err != nil { + fmt.Println("NewScreen error:", err) + } + err = screen.Init() + if err != nil { + fmt.Println("screen Init error:", err) + } + + screen.Clear() + + for i := 0; i < 379; i++ { + printNum(i) + style := tcell.StyleDefault.Foreground(tcell.Color(i)).Background(tcell.Color(i)).Dim(true) + screen.SetContent(x, y, '▄', nil, style) + x++ + screen.SetContent(x, y, '▄', nil, style) + x += 2 + if x > 80 { + x = 0 + y += 2 + } + if i == 15 { + i = 255 + } + } + + screen.Show() +} + +func printNum(num int) { + word := strconv.FormatInt(int64(num), 10) + ":" + if num < 10 { + word = " " + word + } else if num < 100 { + word = " " + word + } + if len(word)+x+2 > 80 { + x = 0 + y += 2 + } + style := tcell.StyleDefault.Foreground(tcell.ColorLightGray).Background(tcell.ColorBlack) + for _, char := range word { + screen.SetContent(x, y, char, nil, style) + x++ + } + +} diff --git a/edit.go b/edit.go index 7392021..a1f6c94 100644 --- a/edit.go +++ b/edit.go @@ -3,7 +3,7 @@ package main import ( "time" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // NewEdit creates a new edit mode @@ -72,6 +72,7 @@ func (edit *Edit) BoardHeightDecrement() { // ChangeBoardSize create new board func (edit *Edit) ChangeBoardSize() { ChangeBoardSize(edit.width, edit.height) + edit.saved = false edit.boardSize = false } @@ -125,7 +126,7 @@ func (edit *Edit) CursorLeft() { } // SetColor sets the board color -func (edit *Edit) SetColor(color termbox.Attribute) { +func (edit *Edit) SetColor(color tcell.Color) { if edit.moved { edit.moved = false } @@ -181,7 +182,7 @@ func (edit *Edit) SaveBoard() { // SaveBoardNew save board as new board func (edit *Edit) SaveBoardNew() { aBoards := Boards{name: time.Now().Format("Jan 2 3:4:5")} - aBoards.colors = make([][]termbox.Attribute, len(board.colors)) + aBoards.colors = make([][]tcell.Color, len(board.colors)) for i := 0; i < len(board.colors); i++ { aBoards.colors[i] = append(aBoards.colors[i], board.colors[i]...) } diff --git a/engine.go b/engine.go index 0682cd7..b5b2135 100644 --- a/engine.go +++ b/engine.go @@ -3,36 +3,67 @@ package main import ( "time" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) +// EventEngineStopRun stop the run of the engine +type EventEngineStopRun struct { + EventGame +} + // NewEngine creates new engine func NewEngine() { engine = &Engine{ - chanStop: make(chan struct{}, 1), - gameOver: true, - tickTime: time.Hour, - ai: NewAi(), + chanStop: make(chan struct{}), + chanEventKey: make(chan *tcell.EventKey, 8), + mode: engineModeGameOver, + tickTime: time.Hour, + ranking: NewRanking(), + ai: NewAi(), } + board.Clear() + go engine.Run() } // Run runs the engine func (engine *Engine) Run() { logger.Println("Engine Run start") - var event *termbox.Event + 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") 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() + var eventKey *tcell.EventKey loop: for { @@ -41,8 +72,8 @@ loop: break loop default: select { - case event = <-engine.keyInput.chanKeyInput: - engine.keyInput.ProcessEvent(event) + case eventKey = <-engine.chanEventKey: + engine.ProcessEventKey(eventKey) view.RefreshScreen() case <-engine.timer.C: engine.tick() @@ -55,15 +86,18 @@ loop: } } - logger.Println("Engine Run end") + screen.PostEventWait(&EventEngineStopRun{}) + + logger.Println("Engine Start end") } -// Stop stops the engine +// Stop the game func (engine *Engine) Stop() { logger.Println("Engine Stop start") if !engine.stopped { engine.stopped = true + engine.mode = engineModeStopped close(engine.chanStop) } engine.timer.Stop() @@ -72,7 +106,7 @@ func (engine *Engine) Stop() { logger.Println("Engine Stop end") } -// Pause pauses the engine +// Pause the game func (engine *Engine) Pause() { if !engine.timer.Stop() { select { @@ -86,21 +120,23 @@ func (engine *Engine) Pause() { default: } } - engine.paused = true + engine.mode = engineModePaused } -// UnPause resumes running the engine +// UnPause the game func (engine *Engine) UnPause() { engine.timer.Reset(engine.tickTime) if engine.aiEnabled { engine.aiTimer.Reset(engine.tickTime / aiTickDivider) + engine.mode = engineModeRunWithAI + } else { + engine.mode = engineModeRun } - engine.paused = false } // PreviewBoard sets previewBoard to true func (engine *Engine) PreviewBoard() { - engine.previewBoard = true + engine.mode = engineModePreview } // NewGame resets board and starts a new game @@ -116,16 +152,17 @@ func (engine *Engine) NewGame() { loop: for { select { - case <-engine.keyInput.chanKeyInput: + case <-engine.chanEventKey: default: break loop } } - engine.previewBoard = false - engine.gameOver = false if engine.aiEnabled { engine.ai.GetBestQueue() + engine.mode = engineModeRunWithAI + } else { + engine.mode = engineModeRun } engine.UnPause() @@ -223,14 +260,14 @@ func (engine *Engine) GameOver() { logger.Println("Engine GameOver start") engine.Pause() - engine.gameOver = true + engine.mode = engineModeGameOver view.ShowGameOverAnimation() loop: for { select { - case <-engine.keyInput.chanKeyInput: + case <-engine.chanEventKey: default: break loop } @@ -252,6 +289,7 @@ func (engine *Engine) EnabledAi() { // DisableAi disables the AI func (engine *Engine) DisableAi() { engine.aiEnabled = false + engine.mode = engineModeRun if !engine.aiTimer.Stop() { select { case <-engine.aiTimer.C: @@ -263,11 +301,11 @@ func (engine *Engine) DisableAi() { // EnabledEditMode enables edit mode func (engine *Engine) EnabledEditMode() { edit.EnabledEditMode() - engine.editMode = true + engine.mode = engineModeEdit } // DisableEditMode disables edit mode func (engine *Engine) DisableEditMode() { edit.DisableEditMode() - engine.editMode = false + engine.mode = engineModePreview } diff --git a/engineKeyInput.go b/engineKeyInput.go new file mode 100644 index 0000000..e886174 --- /dev/null +++ b/engineKeyInput.go @@ -0,0 +1,161 @@ +package main + +import ( + "runtime" + + "github.com/gdamore/tcell" +) + +// ProcessEventKey process the key input event +func (engine *Engine) ProcessEventKey(eventKey *tcell.EventKey) { + if eventKey.Key() == tcell.KeyCtrlL { + // Ctrl l (lower case L) to log stack trace + buffer := make([]byte, 1<<16) + length := runtime.Stack(buffer, true) + logger.Println("Stack trace") + logger.Println(string(buffer[:length])) + return + } + + switch engine.mode { + + // edit + case engineModeEdit: + + if edit.boardSize { + switch eventKey.Key() { + case tcell.KeyUp: + edit.BoardHeightIncrement() + case tcell.KeyDown: + edit.BoardHeightDecrement() + case tcell.KeyLeft: + edit.BoardWidthDecrement() + case tcell.KeyRight: + edit.BoardWidthIncrement() + case tcell.KeyRune: + if eventKey.Rune() == 'q' { + edit.ChangeBoardSize() + } + } + } else { + switch eventKey.Key() { + case tcell.KeyUp: + edit.CursorUp() + case tcell.KeyDown: + edit.CursorDown() + case tcell.KeyLeft: + edit.CursorLeft() + case tcell.KeyRight: + edit.CursorRight() + case tcell.KeyCtrlB: + edit.BoardSizeMode() + case tcell.KeyCtrlS: + edit.SaveBoard() + case tcell.KeyCtrlN: + edit.SaveBoardNew() + case tcell.KeyCtrlK: + edit.DeleteBoard() + case tcell.KeyCtrlO: + edit.EmptyBoard() + case tcell.KeyCtrlQ, tcell.KeyCtrlC: + engine.DisableEditMode() + case tcell.KeyRune: + switch eventKey.Rune() { + case 'c': + edit.SetColor(colorCyan) + case 'b': + edit.SetColor(colorBlue) + case 'w': + edit.SetColor(colorWhite) + case 'e': + edit.SetColor(colorYellow) + case 'g': + edit.SetColor(colorGreen) + case 'a': + edit.SetColor(colorMagenta) + case 'r': + edit.SetColor(colorRed) + case 'f': + edit.SetColor(colorBlank) + case 'z': + edit.RotateLeft() + case 'x': + edit.RotateRight() + } + } + } + + // game over + case engineModeGameOver, engineModePreview: + + switch eventKey.Key() { + case tcell.KeyCtrlC: + engine.Stop() + case tcell.KeyLeft: + board.PreviousBoard() + case tcell.KeyRight: + board.NextBoard() + case tcell.KeyCtrlE: + engine.EnabledEditMode() + case tcell.KeyRune: + switch eventKey.Rune() { + case 'q': + engine.Stop() + case ' ': + engine.NewGame() + } + } + + // paused + case engineModePaused: + + switch eventKey.Rune() { + case 'q': + engine.Stop() + case 'p': + engine.UnPause() + } + + // run with AI + case engineModeRunWithAI: + + switch eventKey.Rune() { + case 'q': + engine.Stop() + case 'p': + engine.Pause() + case 'i': + engine.DisableAi() + } + + // run + case engineModeRun: + + switch eventKey.Key() { + case tcell.KeyUp: + board.MinoDrop() + case tcell.KeyDown: + board.MinoMoveDown() + case tcell.KeyLeft: + board.MinoMoveLeft() + case tcell.KeyRight: + board.MinoMoveRight() + case tcell.KeyRune: + switch eventKey.Rune() { + case 'q': + engine.Stop() + case ' ': + board.MinoDrop() + case 'z': + board.MinoRotateLeft() + case 'x': + board.MinoRotateRight() + case 'p': + engine.Pause() + case 'i': + engine.EnabledAi() + } + } + } + +} diff --git a/globals.go b/globals.go index 04a2e84..6e8d00c 100644 --- a/globals.go +++ b/globals.go @@ -4,11 +4,10 @@ import ( "log" "time" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) const ( - blankColor = termbox.ColorBlack boardXOffset = 4 boardYOffset = 2 aiTickDivider = 8 @@ -21,13 +20,32 @@ const ( MinoCurrent = iota // MinoDrop is for the drop mino MinoDrop = iota + + colorBlank = tcell.ColorBlack + colorCyan = tcell.ColorAqua // I + colorBlue = tcell.ColorBlue // J + colorWhite = tcell.ColorWhite // L + colorYellow = tcell.ColorYellow // O + colorGreen = tcell.ColorLime // S + colorMagenta = tcell.ColorFuchsia // T + colorRed = tcell.ColorRed // Z + + engineModeRun engineMode = iota + engineModeRunWithAI + engineModeStopped + engineModeGameOver + engineModePaused + engineModePreview + engineModeEdit ) type ( + engineMode int + // MinoType is the type of mino MinoType int // MinoBlocks is the blocks of the mino - MinoBlocks [][]termbox.Attribute + MinoBlocks [][]tcell.Color // MinoRotation is the rotation of the mino MinoRotation [4]MinoBlocks @@ -52,7 +70,7 @@ type ( boardsIndex int width int height int - colors [][]termbox.Attribute + colors [][]tcell.Color rotation [][]int previewMino *Mino currentMino *Mino @@ -63,7 +81,7 @@ type ( // Boards holds all the boards Boards struct { name string - colors [][]termbox.Attribute + colors [][]tcell.Color rotation [][]int } @@ -74,13 +92,6 @@ type ( Rotation [][]int } - // KeyInput is the key input engine - KeyInput struct { - stopped bool - chanStop chan struct{} - chanKeyInput chan *termbox.Event - } - // View is the display engine View struct { } @@ -101,20 +112,17 @@ type ( Engine struct { stopped bool chanStop chan struct{} - keyInput *KeyInput + chanEventKey chan *tcell.EventKey ranking *Ranking timer *time.Timer tickTime time.Duration - paused bool - gameOver bool - previewBoard bool + mode engineMode score int level int deleteLines int ai *Ai aiEnabled bool aiTimer *time.Timer - editMode bool } // Edit is the board edit mode @@ -132,11 +140,22 @@ type ( Settings struct { Boards []BoardsJSON } + + // EventGame is an game event + EventGame struct { + when time.Time + } ) +// When returns event when +func (EventGame *EventGame) When() time.Time { + return EventGame.when +} + var ( baseDir string logger *log.Logger + screen tcell.Screen minos *Minos board *Board view *View diff --git a/keyInput.go b/keyInput.go deleted file mode 100644 index f76f6a5..0000000 --- a/keyInput.go +++ /dev/null @@ -1,185 +0,0 @@ -package main - -import ( - "runtime" - - "github.com/nsf/termbox-go" -) - -// NewKeyInput creates a new KeyInput -func NewKeyInput() *KeyInput { - return &KeyInput{ - chanStop: make(chan struct{}, 1), - chanKeyInput: make(chan *termbox.Event, 8), - } -} - -// Run starts the KeyInput engine -func (keyInput *KeyInput) Run() { - logger.Println("KeyInput Run start") - -loop: - for { - select { - case <-keyInput.chanStop: - break loop - default: - } - event := termbox.PollEvent() - if event.Type == termbox.EventKey && len(keyInput.chanKeyInput) < 8 { - select { - case <-keyInput.chanStop: - break loop - case keyInput.chanKeyInput <- &event: - } - } - } - - logger.Println("KeyInput Run end") -} - -// ProcessEvent process the key input event -func (keyInput *KeyInput) ProcessEvent(event *termbox.Event) { - if event.Key == termbox.KeyCtrlL { - // Ctrl l (lower case L) to log stack trace - buffer := make([]byte, 1<<16) - length := runtime.Stack(buffer, true) - logger.Println("Stack trace") - logger.Println(string(buffer[:length])) - return - } - - if engine.editMode { - if edit.boardSize { - switch event.Ch { - case 0: - switch event.Key { - case termbox.KeyArrowUp: - edit.BoardHeightIncrement() - case termbox.KeyArrowDown: - edit.BoardHeightDecrement() - case termbox.KeyArrowLeft: - edit.BoardWidthDecrement() - case termbox.KeyArrowRight: - edit.BoardWidthIncrement() - } - case 'q': - edit.ChangeBoardSize() - } - } else { - switch event.Ch { - case 0: - switch event.Key { - case termbox.KeyArrowUp: - edit.CursorUp() - case termbox.KeyArrowDown: - edit.CursorDown() - case termbox.KeyArrowLeft: - edit.CursorLeft() - case termbox.KeyArrowRight: - edit.CursorRight() - case termbox.KeyCtrlB: - edit.BoardSizeMode() - case termbox.KeyCtrlS: - edit.SaveBoard() - case termbox.KeyCtrlN: - edit.SaveBoardNew() - case termbox.KeyCtrlK: - edit.DeleteBoard() - case termbox.KeyCtrlO: - edit.EmptyBoard() - case termbox.KeyCtrlQ, termbox.KeyCtrlC: - engine.DisableEditMode() - } - case 'c': - edit.SetColor(termbox.ColorCyan) - case 'b': - edit.SetColor(termbox.ColorBlue) - case 'w': - edit.SetColor(termbox.ColorWhite) - case 'e': - edit.SetColor(termbox.ColorYellow) - case 'g': - edit.SetColor(termbox.ColorGreen) - case 'a': - edit.SetColor(termbox.ColorMagenta) - case 'r': - edit.SetColor(termbox.ColorRed) - case 'f': - edit.SetColor(blankColor) - case 'z': - edit.RotateLeft() - case 'x': - edit.RotateRight() - } - } - return - } - - if event.Ch == 'q' || event.Key == termbox.KeyCtrlC { - if !keyInput.stopped { - keyInput.stopped = true - close(keyInput.chanStop) - } - engine.Stop() - return - } - - if engine.gameOver { - if event.Ch == 0 { - switch event.Key { - case termbox.KeySpace: - engine.NewGame() - case termbox.KeyArrowLeft: - board.PreviousBoard() - case termbox.KeyArrowRight: - board.NextBoard() - case termbox.KeyCtrlE: - engine.EnabledEditMode() - } - } - return - } - - if engine.paused { - if event.Ch == 'p' { - engine.UnPause() - } - return - } - - if engine.aiEnabled { - switch event.Ch { - case 'p': - engine.Pause() - case 'i': - engine.DisableAi() - } - return - } - - switch event.Ch { - case 0: - switch event.Key { - case termbox.KeySpace: - board.MinoDrop() - case termbox.KeyArrowUp: - board.MinoDrop() - case termbox.KeyArrowDown: - board.MinoMoveDown() - case termbox.KeyArrowLeft: - board.MinoMoveLeft() - case termbox.KeyArrowRight: - board.MinoMoveRight() - } - case 'z': - board.MinoRotateLeft() - case 'x': - board.MinoRotateRight() - case 'p': - engine.Pause() - case 'i': - engine.EnabledAi() - } - -} diff --git a/mino.go b/mino.go index c536533..432b791 100644 --- a/mino.go +++ b/mino.go @@ -3,7 +3,7 @@ package main import ( "math/rand" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // NewMino creates a new Mino @@ -100,7 +100,7 @@ func (mino *Mino) ValidLocation(mustBeOnBoard bool) bool { minoBlocks := mino.minoRotation[mino.rotation] for i := 0; i < mino.length; i++ { for j := 0; j < mino.length; j++ { - if minoBlocks[i][j] == blankColor { + if minoBlocks[i][j] == colorBlank { continue } if !board.ValidBlockLocation(mino.x+i, mino.y+j, mustBeOnBoard) { @@ -116,7 +116,7 @@ func (mino *Mino) SetOnBoard() { minoBlocks := mino.minoRotation[mino.rotation] for i := 0; i < mino.length; i++ { for j := 0; j < mino.length; j++ { - if minoBlocks[i][j] != blankColor { + if minoBlocks[i][j] != colorBlank { board.SetColor(mino.x+i, mino.y+j, minoBlocks[i][j], mino.rotation) } } @@ -128,12 +128,12 @@ func (mino *Mino) DrawMino(minoType MinoType) { minoBlocks := mino.minoRotation[mino.rotation] for i := 0; i < mino.length; i++ { for j := 0; j < mino.length; j++ { - if minoBlocks[i][j] != blankColor { + if minoBlocks[i][j] != colorBlank { switch minoType { case MinoPreview: view.DrawPreviewMinoBlock(i, j, minoBlocks[i][j], mino.rotation, mino.length) case MinoDrop: - view.DrawBlock(mino.x+i, mino.y+j, blankColor, mino.rotation) + view.DrawBlock(mino.x+i, mino.y+j, colorBlank, mino.rotation) case MinoCurrent: if ValidDisplayLocation(mino.x+i, mino.y+j) { view.DrawBlock(mino.x+i, mino.y+j, minoBlocks[i][j], mino.rotation) @@ -149,7 +149,7 @@ func (mino *Mino) minoOverlap(mino1 *Mino) bool { minoBlocks := mino.minoRotation[mino.rotation] for i := 0; i < mino.length; i++ { for j := 0; j < mino.length; j++ { - if minoBlocks[i][j] == blankColor { + if minoBlocks[i][j] == colorBlank { continue } if mino1.isMinoAtLocation(mino.x+i, mino.y+j) { @@ -168,7 +168,7 @@ func (mino *Mino) isMinoAtLocation(x int, y int) bool { return false } - if mino.minoRotation[mino.rotation][xIndex][yIndex] != blankColor { + if mino.minoRotation[mino.rotation][xIndex][yIndex] != colorBlank { return true } @@ -176,11 +176,11 @@ func (mino *Mino) isMinoAtLocation(x int, y int) bool { } // getMinoColorAtLocation gets the mino color at a location -func (mino *Mino) getMinoColorAtLocation(x int, y int) termbox.Attribute { +func (mino *Mino) getMinoColorAtLocation(x int, y int) tcell.Color { xIndex := x - mino.x yIndex := y - mino.y if xIndex < 0 || xIndex >= mino.length || yIndex < 0 || yIndex >= mino.length { - return blankColor + return colorBlank } minoBlocks := mino.minoRotation[mino.rotation] diff --git a/mino_test.go b/mino_test.go index 0c495ea..2979c06 100644 --- a/mino_test.go +++ b/mino_test.go @@ -1,39 +1,9 @@ package main import ( - "log" - "math/rand" - "os" "testing" ) -type testMinoStruct struct { - minoRotation MinoRotation - x int - y int -} - -func TestMain(m *testing.M) { - setupForTesting() - retCode := m.Run() - os.Exit(retCode) -} - -func setupForTesting() { - logger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.LUTC|log.Llongfile) - - rand.Seed(1) - - err := loadBoards() - if err != nil { - log.Fatal("error loading boards:", err) - } - - NewMinos() - NewBoard() - NewEngine() -} - func TestMinoValidLocation(t *testing.T) { // this must be set to the blank boards for _, i := range []int{0, 3} { diff --git a/minos.go b/minos.go index a472b5d..cdd6eb4 100644 --- a/minos.go +++ b/minos.go @@ -3,45 +3,45 @@ package main import ( "math/rand" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // NewMinos creates the minos and minoBag func NewMinos() { minoI := MinoBlocks{ - []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorCyan, blankColor, blankColor}, + []tcell.Color{colorBlank, colorCyan, colorBlank, colorBlank}, + []tcell.Color{colorBlank, colorCyan, colorBlank, colorBlank}, + []tcell.Color{colorBlank, colorCyan, colorBlank, colorBlank}, + []tcell.Color{colorBlank, colorCyan, colorBlank, colorBlank}, } minoJ := MinoBlocks{ - []termbox.Attribute{termbox.ColorBlue, termbox.ColorBlue, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorBlue, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorBlue, blankColor}, + []tcell.Color{colorBlue, colorBlue, colorBlank}, + []tcell.Color{colorBlank, colorBlue, colorBlank}, + []tcell.Color{colorBlank, colorBlue, colorBlank}, } minoL := MinoBlocks{ - []termbox.Attribute{blankColor, termbox.ColorWhite, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorWhite, blankColor}, - []termbox.Attribute{termbox.ColorWhite, termbox.ColorWhite, blankColor}, + []tcell.Color{colorBlank, colorWhite, colorBlank}, + []tcell.Color{colorBlank, colorWhite, colorBlank}, + []tcell.Color{colorWhite, colorWhite, colorBlank}, } minoO := MinoBlocks{ - []termbox.Attribute{termbox.ColorYellow, termbox.ColorYellow}, - []termbox.Attribute{termbox.ColorYellow, termbox.ColorYellow}, + []tcell.Color{colorYellow, colorYellow}, + []tcell.Color{colorYellow, colorYellow}, } minoS := MinoBlocks{ - []termbox.Attribute{blankColor, termbox.ColorGreen, blankColor}, - []termbox.Attribute{termbox.ColorGreen, termbox.ColorGreen, blankColor}, - []termbox.Attribute{termbox.ColorGreen, blankColor, blankColor}, + []tcell.Color{colorBlank, colorGreen, colorBlank}, + []tcell.Color{colorGreen, colorGreen, colorBlank}, + []tcell.Color{colorGreen, colorBlank, colorBlank}, } minoT := MinoBlocks{ - []termbox.Attribute{blankColor, termbox.ColorMagenta, blankColor}, - []termbox.Attribute{termbox.ColorMagenta, termbox.ColorMagenta, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorMagenta, blankColor}, + []tcell.Color{colorBlank, colorMagenta, colorBlank}, + []tcell.Color{colorMagenta, colorMagenta, colorBlank}, + []tcell.Color{colorBlank, colorMagenta, colorBlank}, } minoZ := MinoBlocks{ - []termbox.Attribute{termbox.ColorRed, blankColor, blankColor}, - []termbox.Attribute{termbox.ColorRed, termbox.ColorRed, blankColor}, - []termbox.Attribute{blankColor, termbox.ColorRed, blankColor}, + []tcell.Color{colorRed, colorBlank, colorBlank}, + []tcell.Color{colorRed, colorRed, colorBlank}, + []tcell.Color{colorBlank, colorRed, colorBlank}, } var minoRotationI MinoRotation @@ -91,7 +91,7 @@ func minosCloneRotateRight(minoBlocks MinoBlocks) MinoBlocks { length := len(minoBlocks) newMinoBlocks := make(MinoBlocks, length, length) for i := 0; i < length; i++ { - newMinoBlocks[i] = make([]termbox.Attribute, length, length) + newMinoBlocks[i] = make([]tcell.Color, length, length) } for i := 0; i < length; i++ { diff --git a/tetris.go b/tetris.go index 8553dd2..6f47128 100644 --- a/tetris.go +++ b/tetris.go @@ -13,7 +13,7 @@ func main() { logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.LUTC|log.Llongfile) logFile, err := os.OpenFile(baseDir+"/go-tetris.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if err != nil { - logger.Fatal("error opening log file:", err) + logger.Fatal("opening log file error:", err) } defer logFile.Close() logger.SetOutput(logFile) @@ -22,21 +22,21 @@ func main() { err = loadBoards() if err != nil { - logger.Fatal("error loading internal boards:", err) + logger.Fatal("loading internal boards error:", err) } err = loadUserBoards() if err != nil { - logger.Fatal("error loading user boards:", err) + logger.Fatal("loading user boards error:", err) } + NewView() NewMinos() NewBoard() - NewView() - NewEngine() NewEdit() + NewEngine() - engine.Run() + engine.Start() view.Stop() } diff --git a/tetris_test.go b/tetris_test.go new file mode 100644 index 0000000..7888534 --- /dev/null +++ b/tetris_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "log" + "math/rand" + "os" + "testing" + + "github.com/gdamore/tcell" +) + +type testMinoStruct struct { + minoRotation MinoRotation + x int + y int +} + +func TestMain(m *testing.M) { + setupForTesting() + retCode := m.Run() + os.Exit(retCode) +} + +func setupForTesting() { + logger = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.LUTC|log.Llongfile) + + rand.Seed(1) + + err := loadBoards() + if err != nil { + log.Fatal("error loading boards:", err) + } + + screen, err = tcell.NewScreen() + if err != nil { + logger.Fatal("NewScreen error:", err) + } + + NewMinos() + NewBoard() + NewEngine() +} diff --git a/view.go b/view.go index 7804b83..430e7a3 100644 --- a/view.go +++ b/view.go @@ -5,17 +5,24 @@ import ( "math/rand" "time" - "github.com/nsf/termbox-go" + "github.com/gdamore/tcell" ) // NewView creates a new view func NewView() { - err := termbox.Init() + var err error + + screen, err = tcell.NewScreen() if err != nil { - panic(err) + logger.Fatal("NewScreen error:", err) } - termbox.SetInputMode(termbox.InputEsc) - termbox.Flush() + err = screen.Init() + if err != nil { + logger.Fatal("screen Init error:", err) + } + + screen.Clear() + view = &View{} } @@ -23,74 +30,80 @@ func NewView() { func (view *View) Stop() { logger.Println("View Stop start") - termbox.Close() + screen.Fini() logger.Println("View Stop end") } -// RefreshScreen refreshs the updated view to the screen +// RefreshScreen refreshes the updated view to the screen func (view *View) RefreshScreen() { - termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) - view.drawBoardBoarder() + switch engine.mode { - if engine.editMode { + case engineModeRun, engineModeRunWithAI: + view.drawBoardBoarder() + view.drawPreviewBoarder() + view.drawTexts() + board.DrawBoard() + board.DrawPreviewMino() + board.DrawDropMino() + board.DrawCurrentMino() + screen.Show() + + case engineModePaused: + screen.Fill(' ', tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorBlack)) + view.drawBoardBoarder() + view.drawPreviewBoarder() + view.drawTexts() + view.drawPaused() + screen.Show() + + case engineModeGameOver: + view.drawBoardBoarder() + view.drawPreviewBoarder() + view.drawTexts() + view.drawGameOver() + view.drawRankingScores() + screen.Show() + + case engineModePreview: + screen.Fill(' ', tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorBlack)) + view.drawBoardBoarder() + view.drawPreviewBoarder() + view.drawTexts() + board.DrawBoard() + view.drawGameOver() + screen.Show() + + case engineModeEdit: + screen.Fill(' ', tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorBlack)) + view.drawBoardBoarder() + board.DrawBoard() if edit.boardSize { - board.DrawBoard() view.drawEditTextsBoardSize() } else { - board.DrawBoard() edit.DrawCursor() view.drawEditTexts() } - termbox.Flush() - return + screen.Show() } - view.drawPreviewBoarder() - view.drawTexts() - - if engine.previewBoard { - board.DrawBoard() - view.drawGameOver() - termbox.Flush() - return - } - - if engine.gameOver { - view.drawGameOver() - view.drawRankingScores() - termbox.Flush() - return - } - - if engine.paused { - view.drawPaused() - termbox.Flush() - return - } - - board.DrawBoard() - board.DrawPreviewMino() - board.DrawDropMino() - board.DrawCurrentMino() - termbox.Flush() } -// drawBoard draws the board boarder +// drawBoardBoarder draws the board boarder func (view *View) drawBoardBoarder() { - // playing board xOffset := boardXOffset yOffset := boardYOffset xEnd := boardXOffset + board.width*2 + 4 yEnd := boardYOffset + board.height + 2 + styleBoarder := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorLightGray) + styleBoard := tcell.StyleDefault.Foreground(tcell.ColorLightGray).Background(tcell.ColorBlack) for x := xOffset; x < xEnd; x++ { for y := yOffset; y < yEnd; y++ { - if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || - y == yOffset || y == yEnd-1 { - termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorWhite) + if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || y == yOffset || y == yEnd-1 { + screen.SetContent(x, y, ' ', nil, styleBoarder) } else { - termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorBlack) + screen.SetContent(x, y, ' ', nil, styleBoard) } } } @@ -102,13 +115,14 @@ func (view *View) drawPreviewBoarder() { yOffset := boardYOffset xEnd := xOffset + 14 yEnd := yOffset + 6 + styleBoarder := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorLightGray) + styleBoard := tcell.StyleDefault.Foreground(tcell.ColorLightGray).Background(tcell.ColorBlack) for x := xOffset; x < xEnd; x++ { for y := yOffset; y < yEnd; y++ { - if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || - y == yOffset || y == yEnd-1 { - termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorWhite) + if x == xOffset || x == xOffset+1 || x == xEnd-1 || x == xEnd-2 || y == yOffset || y == yEnd-1 { + screen.SetContent(x, y, ' ', nil, styleBoarder) } else { - termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorBlack) + screen.SetContent(x, y, ' ', nil, styleBoard) } } } @@ -120,39 +134,39 @@ func (view *View) drawTexts() { xOffset := boardXOffset + board.width*2 + 8 yOffset := boardYOffset + 7 - view.drawText(xOffset, yOffset, "SCORE:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.score), termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "SCORE:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.score), tcell.ColorBlack, tcell.ColorLightGray) yOffset += 2 - view.drawText(xOffset, yOffset, "LINES:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.deleteLines), termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "LINES:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%7d", engine.deleteLines), tcell.ColorBlack, tcell.ColorLightGray) yOffset += 2 - view.drawText(xOffset, yOffset, "LEVEL:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, fmt.Sprintf("%4d", engine.level), termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "LEVEL:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%4d", engine.level), tcell.ColorBlack, tcell.ColorLightGray) yOffset += 2 // ascii arrow characters add extra two spaces - view.drawText(xOffset, yOffset, "← - left", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "← - left", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "→ - right", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "→ - right", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↓ - soft drop", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↓ - soft drop", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↑ - hard drop", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↑ - hard drop", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "sbar - hard drop", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "sbar - hard drop", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "z - rotate left", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "z - rotate left", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "x - rotate right", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "x - rotate right", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "p - pause", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "p - pause", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "q - quit", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "q - quit", tcell.ColorLightGray, tcell.ColorBlack) } // drawEditTexts draws the edit text @@ -160,58 +174,58 @@ func (view *View) drawEditTexts() { xOffset := boardXOffset + board.width*2 + 8 yOffset := boardYOffset - view.drawText(xOffset, yOffset, "Name:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, boards[board.boardsIndex].name, termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "Name:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, boards[board.boardsIndex].name, tcell.ColorBlack, tcell.ColorLightGray) yOffset++ - view.drawText(xOffset, yOffset, "Saved:", termbox.ColorWhite, termbox.ColorBlue) + view.drawText(xOffset, yOffset, "Saved:", tcell.ColorLightGray, tcell.ColorDarkBlue) if edit.saved { - view.drawText(xOffset+7, yOffset, "yes", termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset+7, yOffset, "yes", tcell.ColorBlack, tcell.ColorLightGray) } else { - view.drawText(xOffset+7, yOffset, "no", termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset+7, yOffset, "no", tcell.ColorBlack, tcell.ColorLightGray) } yOffset += 2 // ascii arrow characters add extra two spaces - view.drawText(xOffset, yOffset, "← - left", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "← - left", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "→ - right", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "→ - right", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↓ - down", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↓ - down", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↑ - up", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↑ - up", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "z - rotate left", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "z - rotate left", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "x - rotate right", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "x - rotate right", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "c - cyan", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "c - cyan", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "b - blue", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "b - blue", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "w - white", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "w - white", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "e - yellow", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "e - yellow", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "g - green", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "g - green", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "a - magenta", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "a - magenta", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "r - red", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "r - red", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "f - free", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "f - free", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl b - change board size", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl b - change board size", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl s - save board", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl s - save board", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl n - save board as new", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl n - save board as new", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl k - delete board", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl k - delete board", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl o - empty board", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl o - empty board", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "ctrl q - quit", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "ctrl q - quit", tcell.ColorLightGray, tcell.ColorBlack) } // drawEditTextsBoardSize draws the edit text for board size mode @@ -219,119 +233,124 @@ func (view *View) drawEditTextsBoardSize() { xOffset := boardXOffset + board.width*2 + 8 yOffset := boardYOffset - view.drawText(xOffset, yOffset, "Name:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, boards[board.boardsIndex].name, termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "Name:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, boards[board.boardsIndex].name, tcell.ColorBlack, tcell.ColorLightGray) yOffset += 2 - view.drawText(xOffset, yOffset, "Size:", termbox.ColorWhite, termbox.ColorBlue) - view.drawText(xOffset+7, yOffset, fmt.Sprintf("%2d X %2d", edit.width, edit.height), termbox.ColorBlack, termbox.ColorWhite) + view.drawText(xOffset, yOffset, "Size:", tcell.ColorLightGray, tcell.ColorDarkBlue) + view.drawText(xOffset+7, yOffset, fmt.Sprintf("%2d X %2d", edit.width, edit.height), tcell.ColorBlack, tcell.ColorLightGray) yOffset += 2 // ascii arrow characters add extra two spaces - view.drawText(xOffset, yOffset, "← - board width decrement", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "← - board width decrement", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "→ - board width increment", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "→ - board width increment", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↓ - board height decrement", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↓ - board height decrement", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "↑ - board height increment", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "↑ - board height increment", tcell.ColorLightGray, tcell.ColorBlack) yOffset++ - view.drawText(xOffset, yOffset, "q - done", termbox.ColorWhite, termbox.ColorBlack) + view.drawText(xOffset, yOffset, "q - done", tcell.ColorLightGray, tcell.ColorBlack) } // DrawPreviewMinoBlock draws the preview mino -func (view *View) DrawPreviewMinoBlock(x int, y int, color termbox.Attribute, rotation int, length int) { - char1 := ' ' - char2 := ' ' +func (view *View) DrawPreviewMinoBlock(x int, y int, color tcell.Color, rotation int, length int) { + char1 := '█' + char2 := '█' switch rotation { case 0: char1 = '▄' char2 = '▄' case 1: - char1 = '█' + char2 = ' ' case 2: char1 = '▀' char2 = '▀' case 3: - char2 = '█' + char1 = ' ' } xOffset := 2*x + 2*board.width + boardXOffset + 11 + (4 - length) - termbox.SetCell(xOffset, y+boardYOffset+2, char1, color, color^termbox.AttrBold) - termbox.SetCell(xOffset+1, y+boardYOffset+2, char2, color, color^termbox.AttrBold) + style := tcell.StyleDefault.Foreground(color).Background(color).Dim(true) + screen.SetContent(xOffset, y+boardYOffset+2, char1, nil, style) + screen.SetContent(xOffset+1, y+boardYOffset+2, char2, nil, style) } // DrawBlock draws a block -func (view *View) DrawBlock(x int, y int, color termbox.Attribute, rotation int) { - char1 := ' ' - char2 := ' ' +func (view *View) DrawBlock(x int, y int, color tcell.Color, rotation int) { + char1 := '█' + char2 := '█' switch rotation { case 0: char1 = '▄' char2 = '▄' case 1: - char1 = '█' + char2 = ' ' case 2: char1 = '▀' char2 = '▀' case 3: - char2 = '█' + char1 = ' ' } - if color == blankColor { - // blankColor means drop Mino - termbox.SetCell(2*x+boardXOffset+2, y+boardYOffset+1, char1, termbox.ColorBlack|termbox.AttrBold, termbox.ColorWhite) - termbox.SetCell(2*x+boardXOffset+3, y+boardYOffset+1, char2, termbox.ColorBlack|termbox.AttrBold, termbox.ColorWhite) + if color == colorBlank { + // colorBlank means drop Mino + style := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorSilver).Bold(true) + screen.SetContent(2*x+boardXOffset+2, y+boardYOffset+1, char1, nil, style) + screen.SetContent(2*x+boardXOffset+3, y+boardYOffset+1, char2, nil, style) } else { - termbox.SetCell(2*x+boardXOffset+2, y+boardYOffset+1, char1, color, color^termbox.AttrBold) - termbox.SetCell(2*x+boardXOffset+3, y+boardYOffset+1, char2, color, color^termbox.AttrBold) + style := tcell.StyleDefault.Foreground(color).Background(color).Dim(true) + screen.SetContent(2*x+boardXOffset+2, y+boardYOffset+1, char1, nil, style) + screen.SetContent(2*x+boardXOffset+3, y+boardYOffset+1, char2, nil, style) } } // drawPaused draws Paused func (view *View) drawPaused() { yOffset := (board.height+1)/2 + boardYOffset - view.drawTextCenter(yOffset, "Paused", termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset, "Paused", tcell.ColorWhite, tcell.ColorBlack) } // drawGameOver draws GAME OVER func (view *View) drawGameOver() { yOffset := boardYOffset + 2 - view.drawTextCenter(yOffset, " GAME OVER", termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset, " GAME OVER", tcell.ColorWhite, tcell.ColorBlack) yOffset += 2 - view.drawTextCenter(yOffset, "sbar for new game", termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset, "sbar for new game", tcell.ColorWhite, tcell.ColorBlack) - if engine.previewBoard { + if engine.mode == engineModePreview { return } yOffset += 2 // ascii arrow characters add extra two spaces - view.drawTextCenter(yOffset, "←previous board", termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset, "←previous board", tcell.ColorWhite, tcell.ColorBlack) yOffset += 2 - view.drawTextCenter(yOffset, "→next board", termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset, "→next board", tcell.ColorWhite, tcell.ColorBlack) } // drawRankingScores draws the ranking scores func (view *View) drawRankingScores() { yOffset := boardYOffset + 10 for index, line := range engine.ranking.scores { - view.drawTextCenter(yOffset+index, fmt.Sprintf("%1d: %6d", index+1, line), termbox.ColorWhite, termbox.ColorBlack) + view.drawTextCenter(yOffset+index, fmt.Sprintf("%1d: %6d", index+1, line), tcell.ColorWhite, tcell.ColorBlack) } } // drawText draws the provided text -func (view *View) drawText(x int, y int, text string, fg termbox.Attribute, bg termbox.Attribute) { +func (view *View) drawText(x int, y int, text string, fg tcell.Color, bg tcell.Color) { + style := tcell.StyleDefault.Foreground(fg).Background(bg) for index, char := range text { - termbox.SetCell(x+index, y, rune(char), fg, bg) + screen.SetContent(x+index, y, rune(char), nil, style) } } // drawTextCenter draws text in the center of the board -func (view *View) drawTextCenter(y int, text string, fg termbox.Attribute, bg termbox.Attribute) { +func (view *View) drawTextCenter(y int, text string, fg tcell.Color, bg tcell.Color) { xOffset := board.width - (len(text)+1)/2 + boardXOffset + 2 + style := tcell.StyleDefault.Foreground(fg).Background(bg) for index, char := range text { - termbox.SetCell(index+xOffset, y, rune(char), fg, bg) + screen.SetContent(index+xOffset, y, rune(char), nil, style) } } @@ -341,9 +360,9 @@ func (view *View) ShowDeleteAnimation(lines []int) { for times := 0; times < 3; times++ { for _, y := range lines { - view.colorizeLine(y, termbox.ColorCyan) + view.colorizeLine(y, tcell.ColorLightGray) } - termbox.Flush() + screen.Show() time.Sleep(140 * time.Millisecond) view.RefreshScreen() @@ -357,20 +376,23 @@ func (view *View) ShowGameOverAnimation() { switch rand.Intn(3) { case 0: + logger.Println("View ShowGameOverAnimation case 0") for y := board.height - 1; y >= 0; y-- { - view.colorizeLine(y, termbox.ColorBlack) - termbox.Flush() + view.colorizeLine(y, tcell.ColorLightGray) + screen.Show() time.Sleep(60 * time.Millisecond) } case 1: + logger.Println("View ShowGameOverAnimation case 1") for y := 0; y < board.height; y++ { - view.colorizeLine(y, termbox.ColorBlack) - termbox.Flush() + view.colorizeLine(y, tcell.ColorLightGray) + screen.Show() time.Sleep(60 * time.Millisecond) } case 2: + logger.Println("View ShowGameOverAnimation case 2") sleepTime := 50 * time.Millisecond topStartX := boardXOffset + 3 topEndX := board.width*2 + boardXOffset + 1 @@ -384,33 +406,34 @@ func (view *View) ShowGameOverAnimation() { leftStartY := rightEndY - 1 leftEndY := rightStartY - 1 leftX := boardXOffset + 2 + style := tcell.StyleDefault.Foreground(tcell.ColorLightGray).Background(tcell.ColorLightGray) for topStartX <= topEndX && rightStartY <= rightEndY { for x := topStartX; x < topEndX; x++ { - termbox.SetCell(x, topY, ' ', termbox.ColorBlack, termbox.ColorBlack) + screen.SetContent(x, topY, ' ', nil, style) } topStartX++ topEndX-- topY++ for y := rightStartY; y < rightEndY; y++ { - termbox.SetCell(rightX, y, ' ', termbox.ColorBlack, termbox.ColorBlack) + screen.SetContent(rightX, y, ' ', nil, style) } rightStartY++ rightEndY-- rightX-- for x := bottomStartX; x > bottomEndX; x-- { - termbox.SetCell(x, bottomY, ' ', termbox.ColorBlack, termbox.ColorBlack) + screen.SetContent(x, bottomY, ' ', nil, style) } bottomStartX-- bottomEndX++ bottomY-- for y := leftStartY; y > leftEndY; y-- { - termbox.SetCell(leftX, y, ' ', termbox.ColorBlack, termbox.ColorBlack) + screen.SetContent(leftX, y, ' ', nil, style) } leftStartY-- leftEndY++ leftX++ - termbox.Flush() + screen.Show() time.Sleep(sleepTime) sleepTime += 4 * time.Millisecond } @@ -420,16 +443,21 @@ func (view *View) ShowGameOverAnimation() { } // colorizeLine changes the color of a line -func (view *View) colorizeLine(y int, color termbox.Attribute) { +func (view *View) colorizeLine(y int, color tcell.Color) { + style := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(color) for x := 0; x < board.width; x++ { - termbox.SetCell(x*2+boardXOffset+2, y+boardYOffset+1, ' ', termbox.ColorDefault, color) - termbox.SetCell(x*2+boardXOffset+3, y+boardYOffset+1, ' ', termbox.ColorDefault, color) + screen.SetContent(x*2+boardXOffset+2, y+boardYOffset+1, ' ', nil, style) + screen.SetContent(x*2+boardXOffset+3, y+boardYOffset+1, ' ', nil, style) } } // DrawCursor draws current cursor location -func (view *View) DrawCursor(x int, y int, color termbox.Attribute) { - termbox.SetCell(x*2+boardXOffset+2, y+boardYOffset+1, '◄', color^termbox.AttrBold, termbox.ColorBlack^termbox.AttrBold) - termbox.SetCell(x*2+boardXOffset+3, y+boardYOffset+1, '►', color^termbox.AttrBold, termbox.ColorBlack^termbox.AttrBold) - termbox.Flush() +func (view *View) DrawCursor(x int, y int, color tcell.Color) { + style := tcell.StyleDefault.Foreground(color).Background(tcell.ColorBlack) + if color == colorBlank { + style = tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorLightGrey) + } + screen.SetContent(x*2+boardXOffset+2, y+boardYOffset+1, '◄', nil, style) + screen.SetContent(x*2+boardXOffset+3, y+boardYOffset+1, '►', nil, style) + screen.Show() }