498 lines
11 KiB
Go
498 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/gdamore/tcell"
|
|
)
|
|
|
|
// NewBoard creates a new clear board
|
|
func NewBoard() {
|
|
board = &Board{}
|
|
board.Clear()
|
|
}
|
|
|
|
// ChangeBoardSize changes board size
|
|
func ChangeBoardSize(width int, height int) {
|
|
if board.width == width && board.height == height {
|
|
return
|
|
}
|
|
|
|
newBoard := &Board{width: width, height: height, boardsIndex: board.boardsIndex}
|
|
newBoard.colors = make([][]tcell.Color, width)
|
|
for i := 0; i < width; i++ {
|
|
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] = colorBlank
|
|
}
|
|
}
|
|
}
|
|
newBoard.rotation = make([][]int, width)
|
|
for i := 0; i < width; i++ {
|
|
newBoard.rotation[i] = make([]int, height)
|
|
for j := 0; j < height; j++ {
|
|
if i < board.width && j < board.height {
|
|
newBoard.rotation[i][j] = board.rotation[i][j]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
board = newBoard
|
|
board.fullLinesY = make([]bool, board.height)
|
|
board.previewMino = NewMino()
|
|
board.currentMino = NewMino()
|
|
}
|
|
|
|
// Clear clears the board to original state
|
|
func (board *Board) Clear() {
|
|
board.width = len(boards[board.boardsIndex].colors)
|
|
board.height = len(boards[board.boardsIndex].colors[0])
|
|
board.colors = make([][]tcell.Color, board.width)
|
|
for i := 0; i < board.width; i++ {
|
|
board.colors[i] = make([]tcell.Color, board.height)
|
|
copy(board.colors[i], boards[board.boardsIndex].colors[i])
|
|
}
|
|
board.rotation = make([][]int, board.width)
|
|
for i := 0; i < board.width; i++ {
|
|
board.rotation[i] = make([]int, board.height)
|
|
copy(board.rotation[i], boards[board.boardsIndex].rotation[i])
|
|
}
|
|
board.fullLinesY = make([]bool, board.height)
|
|
board.previewMino = NewMino()
|
|
board.currentMino = NewMino()
|
|
}
|
|
|
|
// EmptyBoard removes all blocks/colors from the board
|
|
func (board *Board) EmptyBoard() {
|
|
for i := 0; i < board.width; i++ {
|
|
for j := 0; j < board.height; j++ {
|
|
board.colors[i][j] = colorBlank
|
|
}
|
|
}
|
|
for i := 0; i < board.width; i++ {
|
|
for j := 0; j < board.height; j++ {
|
|
board.rotation[i][j] = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
// PreviousBoard switches to previous board
|
|
func (board *Board) PreviousBoard() {
|
|
board.boardsIndex--
|
|
if board.boardsIndex < 0 {
|
|
board.boardsIndex = len(boards) - 1
|
|
}
|
|
engine.PreviewBoard()
|
|
board.Clear()
|
|
}
|
|
|
|
// NextBoard switches to next board
|
|
func (board *Board) NextBoard() {
|
|
board.boardsIndex++
|
|
if board.boardsIndex == len(boards) {
|
|
board.boardsIndex = 0
|
|
}
|
|
engine.PreviewBoard()
|
|
board.Clear()
|
|
}
|
|
|
|
// MinoMoveLeft moves mino left
|
|
func (board *Board) MinoMoveLeft() {
|
|
board.dropDistance = 0
|
|
mino := board.currentMino.CloneMoveLeft()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
}
|
|
}
|
|
|
|
// MinoMoveRight moves mino right
|
|
func (board *Board) MinoMoveRight() {
|
|
board.dropDistance = 0
|
|
mino := board.currentMino.CloneMoveRight()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
}
|
|
}
|
|
|
|
// MinoRotateRight rotates mino right
|
|
func (board *Board) MinoRotateRight() {
|
|
board.dropDistance = 0
|
|
mino := board.currentMino.CloneRotateRight()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
mino.MoveLeft()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
mino.MoveRight()
|
|
mino.MoveRight()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
}
|
|
|
|
// MinoRotateLeft rotates mino right
|
|
func (board *Board) MinoRotateLeft() {
|
|
board.dropDistance = 0
|
|
mino := board.currentMino.CloneRotateLeft()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
mino.MoveLeft()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
mino.MoveRight()
|
|
mino.MoveRight()
|
|
if mino.ValidLocation(false) {
|
|
board.currentMino = mino
|
|
board.StartLockDelayIfBottom()
|
|
return
|
|
}
|
|
}
|
|
|
|
// MinoMoveDown moves mino down
|
|
func (board *Board) MinoMoveDown() {
|
|
mino := board.currentMino.CloneMoveDown()
|
|
if mino.ValidLocation(false) {
|
|
board.dropDistance = 0
|
|
board.currentMino = mino
|
|
if !board.StartLockDelayIfBottom() {
|
|
engine.ResetTimer(0)
|
|
}
|
|
return
|
|
}
|
|
if !board.currentMino.ValidLocation(true) {
|
|
engine.GameOver()
|
|
return
|
|
}
|
|
board.nextMino()
|
|
}
|
|
|
|
// MinoDrop dropps mino
|
|
func (board *Board) MinoDrop() {
|
|
board.dropDistance = 0
|
|
mino := board.currentMino.CloneMoveDown()
|
|
for mino.ValidLocation(false) {
|
|
board.dropDistance++
|
|
mino.MoveDown()
|
|
}
|
|
for i := 0; i < board.dropDistance; i++ {
|
|
board.currentMino.MoveDown()
|
|
}
|
|
if !board.currentMino.ValidLocation(true) {
|
|
engine.GameOver()
|
|
return
|
|
}
|
|
if board.dropDistance < 1 {
|
|
return
|
|
}
|
|
if !board.StartLockDelayIfBottom() {
|
|
engine.ResetTimer(0)
|
|
}
|
|
}
|
|
|
|
// StartLockDelayIfBottom if at bottom, starts lock delay
|
|
func (board *Board) StartLockDelayIfBottom() bool {
|
|
mino := board.currentMino.CloneMoveDown()
|
|
if mino.ValidLocation(false) {
|
|
return false
|
|
}
|
|
engine.ResetTimer(300 * time.Millisecond)
|
|
return true
|
|
}
|
|
|
|
// nextMino gets next mino
|
|
func (board *Board) nextMino() {
|
|
engine.AddScore(board.dropDistance)
|
|
|
|
board.currentMino.SetOnBoard()
|
|
|
|
board.deleteCheck()
|
|
|
|
if !board.previewMino.ValidLocation(false) {
|
|
board.previewMino.MoveUp()
|
|
if !board.previewMino.ValidLocation(false) {
|
|
engine.GameOver()
|
|
return
|
|
}
|
|
}
|
|
|
|
board.currentMino = board.previewMino
|
|
board.previewMino = NewMino()
|
|
engine.AiGetBestQueue()
|
|
engine.ResetTimer(0)
|
|
}
|
|
|
|
// deleteCheck checks if there are any lines on the board that can be deleted
|
|
func (board *Board) deleteCheck() {
|
|
lines := board.fullLines()
|
|
if len(lines) < 1 {
|
|
return
|
|
}
|
|
|
|
view.ShowDeleteAnimation(lines)
|
|
for _, line := range lines {
|
|
board.deleteLine(line)
|
|
}
|
|
|
|
engine.AddDeleteLines(len(lines))
|
|
}
|
|
|
|
// fullLines returns the line numbers that have full lines
|
|
func (board *Board) fullLines() []int {
|
|
fullLines := make([]int, 0, 1)
|
|
for j := 0; j < board.height; j++ {
|
|
if board.isFullLine(j) {
|
|
fullLines = append(fullLines, j)
|
|
}
|
|
}
|
|
return fullLines
|
|
}
|
|
|
|
// 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] == colorBlank {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// deleteLine deletes the line
|
|
func (board *Board) deleteLine(line int) {
|
|
for i := 0; i < board.width; i++ {
|
|
board.colors[i][line] = colorBlank
|
|
}
|
|
for j := line; j > 0; j-- {
|
|
for i := 0; i < board.width; i++ {
|
|
board.colors[i][j] = board.colors[i][j-1]
|
|
board.rotation[i][j] = board.rotation[i][j-1]
|
|
}
|
|
}
|
|
for i := 0; i < board.width; i++ {
|
|
board.colors[i][0] = colorBlank
|
|
}
|
|
}
|
|
|
|
// SetColor sets the color and rotation of board location
|
|
func (board *Board) SetColor(x int, y int, color tcell.Color, rotation int) {
|
|
board.colors[x][y] = color
|
|
if rotation < 0 {
|
|
return
|
|
}
|
|
board.rotation[x][y] = rotation
|
|
}
|
|
|
|
// RotateLeft rotates cell left
|
|
func (board *Board) RotateLeft(x int, y int) {
|
|
if board.rotation[x][y] == 0 {
|
|
board.rotation[x][y] = 3
|
|
return
|
|
}
|
|
board.rotation[x][y]--
|
|
}
|
|
|
|
// RotateRight rotates cell right
|
|
func (board *Board) RotateRight(x int, y int) {
|
|
if board.rotation[x][y] == 3 {
|
|
board.rotation[x][y] = 0
|
|
return
|
|
}
|
|
board.rotation[x][y]++
|
|
}
|
|
|
|
// ValidBlockLocation checks if block location is vaild
|
|
func (board *Board) ValidBlockLocation(x int, y int, mustBeOnBoard bool) bool {
|
|
if x < 0 || x >= board.width || y >= board.height {
|
|
return false
|
|
}
|
|
if mustBeOnBoard {
|
|
if y < 0 {
|
|
return false
|
|
}
|
|
} else {
|
|
if y < -2 {
|
|
return false
|
|
}
|
|
}
|
|
if y > -1 {
|
|
if board.colors[x][y] != colorBlank {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ValidDisplayLocation checks if vaild display location
|
|
func ValidDisplayLocation(x int, y int) bool {
|
|
return x >= 0 && x < board.width && y >= 0 && y < board.height
|
|
}
|
|
|
|
// DrawBoard draws the board with help from view
|
|
func (board *Board) DrawBoard() {
|
|
for i := 0; i < board.width; i++ {
|
|
for j := 0; j < board.height; j++ {
|
|
if board.colors[i][j] != colorBlank {
|
|
view.DrawBlock(i, j, board.colors[i][j], board.rotation[i][j])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// DrawPreviewMino draws the preview mino
|
|
func (board *Board) DrawPreviewMino() {
|
|
board.previewMino.DrawMino(MinoPreview)
|
|
}
|
|
|
|
// DrawCurrentMino draws the current mino
|
|
func (board *Board) DrawCurrentMino() {
|
|
board.currentMino.DrawMino(MinoCurrent)
|
|
}
|
|
|
|
// DrawDropMino draws the drop mino
|
|
func (board *Board) DrawDropMino() {
|
|
mino := board.currentMino.CloneMoveDown()
|
|
if !mino.ValidLocation(false) {
|
|
return
|
|
}
|
|
for mino.ValidLocation(false) {
|
|
mino.MoveDown()
|
|
}
|
|
mino.MoveUp()
|
|
mino.DrawMino(MinoDrop)
|
|
}
|
|
|
|
// DrawCursor draws the edit cursor
|
|
func (board *Board) DrawCursor(x int, y int) {
|
|
view.DrawCursor(x, y, board.colors[x][y])
|
|
}
|
|
|
|
// printDebugBoard is for printing board in text for debuging
|
|
func (board *Board) printDebugBoard() {
|
|
for j := 0; j < board.height; j++ {
|
|
for i := 0; i < board.width; i++ {
|
|
switch board.colors[i][j] {
|
|
case colorBlank:
|
|
fmt.Print(".")
|
|
case colorBlue:
|
|
fmt.Print("B")
|
|
case colorCyan:
|
|
fmt.Print("C")
|
|
case colorGreen:
|
|
fmt.Print("G")
|
|
case colorMagenta:
|
|
fmt.Print("M")
|
|
case colorRed:
|
|
fmt.Print("R")
|
|
case colorWhite:
|
|
fmt.Print("W")
|
|
case colorYellow:
|
|
fmt.Print("Y")
|
|
default:
|
|
fmt.Print("U")
|
|
}
|
|
}
|
|
fmt.Println("")
|
|
}
|
|
}
|
|
|
|
// getDebugBoard returns board as string for debuging and testing
|
|
func (board *Board) getDebugBoard() []string {
|
|
lines := make([]string, board.height)
|
|
for j := 0; j < board.height; j++ {
|
|
for i := 0; i < board.width; i++ {
|
|
switch board.colors[i][j] {
|
|
case colorBlank:
|
|
lines[j] += "."
|
|
case colorBlue:
|
|
lines[j] += "B"
|
|
case colorCyan:
|
|
lines[j] += "C"
|
|
case colorGreen:
|
|
lines[j] += "G"
|
|
case colorMagenta:
|
|
lines[j] += "M"
|
|
case colorRed:
|
|
lines[j] += "R"
|
|
case colorWhite:
|
|
lines[j] += "W"
|
|
case colorYellow:
|
|
lines[j] += "Y"
|
|
default:
|
|
lines[j] += "U"
|
|
}
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
// getDebugBoardWithMino returns board with mino placed on it
|
|
func (board *Board) getDebugBoardWithMino(mino *Mino) []string {
|
|
lines := make([]string, board.height)
|
|
for j := 0; j < board.height; j++ {
|
|
for i := 0; i < board.width; i++ {
|
|
switch mino.getMinoColorAtLocation(i, j) {
|
|
case colorBlank:
|
|
switch board.colors[i][j] {
|
|
case colorBlank:
|
|
lines[j] += "."
|
|
case colorBlue:
|
|
lines[j] += "B"
|
|
case colorCyan:
|
|
lines[j] += "C"
|
|
case colorGreen:
|
|
lines[j] += "G"
|
|
case colorMagenta:
|
|
lines[j] += "M"
|
|
case colorRed:
|
|
lines[j] += "R"
|
|
case colorWhite:
|
|
lines[j] += "W"
|
|
case colorYellow:
|
|
lines[j] += "Y"
|
|
default:
|
|
lines[j] += "U"
|
|
}
|
|
case colorBlue:
|
|
lines[j] += "b"
|
|
case colorCyan:
|
|
lines[j] += "c"
|
|
case colorGreen:
|
|
lines[j] += "g"
|
|
case colorMagenta:
|
|
lines[j] += "m"
|
|
case colorRed:
|
|
lines[j] += "r"
|
|
case colorWhite:
|
|
lines[j] += "w"
|
|
case colorYellow:
|
|
lines[j] += "y"
|
|
default:
|
|
lines[j] += "u"
|
|
}
|
|
}
|
|
}
|
|
return lines
|
|
}
|