Rename game/ -> connect4/ to avoid c4/game stutter. Drop redundant
Game prefix from exported types (GameStore -> Store, GameInstance ->
Instance, GameStatus -> Status). Rename NATS subjects from game.{id}
to connect4.{id}. URL routes unchanged.
99 lines
2.2 KiB
Go
99 lines
2.2 KiB
Go
// Package connect4 implements Connect 4 game logic, state management, and persistence.
|
|
package connect4
|
|
|
|
// DropPiece attempts to drop a piece in the given column.
|
|
// Returns (row placed, success).
|
|
func (g *Game) DropPiece(col, playerColor int) (int, bool) {
|
|
if col < 0 || col > 6 {
|
|
return -1, false
|
|
}
|
|
if g.CurrentTurn != playerColor {
|
|
return -1, false
|
|
}
|
|
if g.Status != StatusInProgress {
|
|
return -1, false
|
|
}
|
|
|
|
// Find lowest empty row in column
|
|
for row := 5; row >= 0; row-- {
|
|
if g.Board[row][col] == 0 {
|
|
g.Board[row][col] = playerColor
|
|
return row, true
|
|
}
|
|
}
|
|
return -1, false // Column full
|
|
}
|
|
|
|
// CheckWin checks if the last move at (row, col) created a win.
|
|
func (g *Game) CheckWin(row, col int) bool {
|
|
color := g.Board[row][col]
|
|
directions := [][2]int{
|
|
{0, 1}, // Horizontal
|
|
{1, 0}, // Vertical
|
|
{1, 1}, // Diagonal down-right
|
|
{1, -1}, // Diagonal down-left
|
|
}
|
|
|
|
for _, dir := range directions {
|
|
cells := g.countLine(row, col, dir[0], dir[1], color)
|
|
if len(cells) >= 4 {
|
|
g.Status = StatusWon
|
|
g.WinningCells = cells[:4]
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (g *Game) countLine(row, col, dr, dc, color int) [][2]int {
|
|
cells := [][2]int{{row, col}}
|
|
|
|
// Count in positive direction
|
|
for r, c := row+dr, col+dc; r >= 0 && r < 6 && c >= 0 && c < 7; r, c = r+dr, c+dc {
|
|
if g.Board[r][c] != color {
|
|
break
|
|
}
|
|
cells = append(cells, [2]int{r, c})
|
|
}
|
|
|
|
// Count in negative direction
|
|
for r, c := row-dr, col-dc; r >= 0 && r < 6 && c >= 0 && c < 7; r, c = r-dr, c-dc {
|
|
if g.Board[r][c] != color {
|
|
break
|
|
}
|
|
cells = append(cells, [2]int{r, c})
|
|
}
|
|
|
|
return cells
|
|
}
|
|
|
|
// CheckDraw checks if board is full with no winner.
|
|
func (g *Game) CheckDraw() bool {
|
|
for col := 0; col < 7; col++ {
|
|
if g.Board[0][col] == 0 {
|
|
return false
|
|
}
|
|
}
|
|
g.Status = StatusDraw
|
|
return true
|
|
}
|
|
|
|
// SwitchTurn alternates the current turn.
|
|
func (g *Game) SwitchTurn() {
|
|
if g.CurrentTurn == 1 {
|
|
g.CurrentTurn = 2
|
|
} else {
|
|
g.CurrentTurn = 1
|
|
}
|
|
}
|
|
|
|
// IsWinningCell checks if a cell is part of the winning line.
|
|
func (g *Game) IsWinningCell(row, col int) bool {
|
|
for _, cell := range g.WinningCells {
|
|
if cell[0] == row && cell[1] == col {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|