Converted to tcell for terminal

This commit is contained in:
MichaelS11 2019-12-01 13:03:27 -08:00
parent 39e38c05d9
commit ab91e02f03
15 changed files with 667 additions and 532 deletions

18
ai.go
View File

@ -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++
}
}

View File

@ -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"

View File

@ -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"
}
}

65
colorTest/main.go Normal file
View File

@ -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++
}
}

View File

@ -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]...)
}

View File

@ -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
}

161
engineKeyInput.go Normal file
View File

@ -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()
}
}
}
}

View File

@ -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

View File

@ -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()
}
}

18
mino.go
View File

@ -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]

View File

@ -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} {

View File

@ -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++ {

View File

@ -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()
}

42
tetris_test.go Normal file
View File

@ -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()
}

324
view.go
View File

@ -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()
}