go-tetris/board.go

301 lines
5.9 KiB
Go

package main
import (
"fmt"
"github.com/nsf/termbox-go"
"time"
)
type Board struct {
colors [boardWidth][boardHeight]termbox.Attribute
rotation [boardWidth][boardHeight]int
previewMino *Mino
currentMino *Mino
dropDistance int
}
func NewBoard() *Board {
board := &Board{}
for i := 0; i < boardWidth; i++ {
for j := 0; j < boardHeight; j++ {
board.colors[i][j] = blankColor
}
}
for i := 0; i < boardWidth; i++ {
for j := 0; j < boardHeight; j++ {
board.rotation[i][j] = 0
}
}
board.previewMino = NewMino()
board.currentMino = NewMino()
return board
}
func (board *Board) MinoMoveLeft() {
board.dropDistance = 0
mino := board.currentMino.CloneMoveLeft()
if mino.ValidLocation(false) {
board.currentMino = mino
board.StartLockDelayIfBottom()
}
}
func (board *Board) MinoMoveRight() {
board.dropDistance = 0
mino := board.currentMino.CloneMoveRight()
if mino.ValidLocation(false) {
board.currentMino = mino
board.StartLockDelayIfBottom()
}
}
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
}
}
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
}
}
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()
}
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)
}
}
func (board *Board) StartLockDelayIfBottom() bool {
mino := board.currentMino.CloneMoveDown()
if mino.ValidLocation(false) {
return false
}
engine.ResetTimer(300 * time.Millisecond)
return true
}
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.ResetAiTimer()
engine.ResetTimer(0)
}
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))
}
func (board *Board) fullLines() []int {
fullLines := make([]int, 0, 1)
for j := 0; j < boardHeight; j++ {
if board.isFullLine(j) {
fullLines = append(fullLines, j)
}
}
return fullLines
}
func (board *Board) isFullLine(j int) bool {
for i := 0; i < boardWidth; i++ {
if board.colors[i][j] == blankColor {
return false
}
}
return true
}
func (board *Board) deleteLine(line int) {
for i := 0; i < boardWidth; i++ {
board.colors[i][line] = blankColor
}
for j := line; j > 0; j-- {
for i := 0; i < boardWidth; i++ {
board.colors[i][j] = board.colors[i][j-1]
board.rotation[i][j] = board.rotation[i][j-1]
}
}
for i := 0; i < boardWidth; i++ {
board.colors[i][0] = blankColor
}
}
func (board *Board) SetColor(x int, y int, color termbox.Attribute, rotation int) {
board.colors[x][y] = color
board.rotation[x][y] = rotation
}
func ValidBlockLocation(x int, y int, mustBeOnBoard bool) bool {
if x < 0 || x >= boardWidth || y >= boardHeight {
return false
}
if mustBeOnBoard {
if y < 0 {
return false
}
} else {
if y < -2 {
return false
}
}
if y > -1 {
if board.colors[x][y] != blankColor {
return false
}
}
return true
}
func ValidDisplayLocation(x int, y int) bool {
return x >= 0 && x < boardWidth && y >= 0 && y < boardHeight
}
func (board *Board) DrawBoard() {
for i := 0; i < boardWidth; i++ {
for j := 0; j < boardHeight; j++ {
if board.colors[i][j] != blankColor {
view.DrawBlock(i, j, board.colors[i][j], board.rotation[i][j])
}
}
}
}
func (board *Board) DrawPreviewMino() {
board.previewMino.DrawMino(MinoPreview)
}
func (board *Board) DrawCurrentMino() {
board.currentMino.DrawMino(MinoCurrent)
}
func (board *Board) DrawDropMino() {
mino := board.currentMino.CloneMoveDown()
if !mino.ValidLocation(false) {
return
}
for mino.ValidLocation(false) {
mino.MoveDown()
}
mino.MoveUp()
mino.DrawMino(MinoDrop)
}
// for debuging
func (board *Board) drawDebugBoard() {
for j := 0; j < boardHeight; j++ {
for i := 0; i < boardWidth; i++ {
switch board.colors[i][j] {
case blankColor:
fmt.Print(".")
case termbox.ColorBlue:
fmt.Print("B")
case termbox.ColorCyan:
fmt.Print("C")
case termbox.ColorGreen:
fmt.Print("G")
case termbox.ColorMagenta:
fmt.Print("M")
case termbox.ColorRed:
fmt.Print("R")
case termbox.ColorWhite:
fmt.Print("W")
case termbox.ColorYellow:
fmt.Print("Y")
}
}
fmt.Println("")
}
}