feat(core): migrate to multi-board architecture and implement per-game InitBoards
### Major changes - Replace single-board MatchState (`Board`) with multi-board map (`Boards`) - Update GenericMatch to initialize empty Boards and populate them via GameRules.InitBoards - Remove legacy `newEmptyBoard` helper from match.go - Update .gitignore to include *BKP* patterns ### GameRules interface - Add InitBoards(players, cfg) to allow games to construct their own board sets - Add detailed documentation explaining method responsibilities and usage - Improve MovePayload comment clarity ### TicTacToe updates - Implement InitBoards to produce a single `"tictactoe"` board - Replace all old references to `state.Board` with `state.Boards["tictactoe"]` - Make CheckGameOver, ValidateMove, and ApplyMove multi-board compatible ### Battleship updates - Implement InitBoards generating per-player ships + shots boards: - p0_ships, p0_shots - p1_ships, p1_shots ### Match flow updates - Boards are now created only when all players have joined - Initial state broadcast now includes `boards` instead of `board` This completes the backend migration for multi-board games and prepares the architecture for Battleship and other complex board-based games.
This commit is contained in:
@@ -15,6 +15,12 @@ func (t *TicTacToeRules) MaxPlayers() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (t *TicTacToeRules) InitBoards(players []*structs.Player, cfg GameConfiguration) map[string]*structs.Board {
|
||||
return map[string]*structs.Board{
|
||||
"tictactoe": structs.NewBoard(cfg.Board.Rows, cfg.Board.Cols),
|
||||
}
|
||||
}
|
||||
|
||||
// Assign player symbols: X and O
|
||||
func (t *TicTacToeRules) AssignPlayerSymbols(players []*structs.Player) {
|
||||
if len(players) < 2 {
|
||||
@@ -45,13 +51,18 @@ func (t *TicTacToeRules) ValidateMove(state *structs.MatchState, playerIdx int,
|
||||
r := int(row)
|
||||
c := int(col)
|
||||
|
||||
b := state.Boards["tictactoe"]
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// bounds
|
||||
if !state.Board.InBounds(r, c) {
|
||||
if !b.InBounds(r, c) {
|
||||
return false
|
||||
}
|
||||
|
||||
// empty?
|
||||
return state.Board.IsEmpty(r, c)
|
||||
return b.IsEmpty(r, c)
|
||||
}
|
||||
|
||||
// ApplyMove writes X or O to the board.
|
||||
@@ -60,12 +71,17 @@ func (t *TicTacToeRules) ApplyMove(
|
||||
playerIdx int,
|
||||
payload MovePayload,
|
||||
) (bool, bool, int) {
|
||||
b := state.Boards["tictactoe"]
|
||||
if b == nil {
|
||||
return false, false, -1
|
||||
}
|
||||
|
||||
symbol := state.Players[playerIdx].Metadata["symbol"]
|
||||
|
||||
r := int(payload.Data["row"].(float64))
|
||||
c := int(payload.Data["col"].(float64))
|
||||
|
||||
state.Board.Set(r, c, symbol)
|
||||
b.Set(r, c, symbol)
|
||||
|
||||
over, winner := t.CheckGameOver(state)
|
||||
|
||||
@@ -75,8 +91,12 @@ func (t *TicTacToeRules) ApplyMove(
|
||||
|
||||
// CheckGameOver determines win/draw state.
|
||||
func (t *TicTacToeRules) CheckGameOver(state *structs.MatchState) (bool, int) {
|
||||
b := state.Boards["tictactoe"]
|
||||
if b == nil {
|
||||
return true, -1 // fallback safety
|
||||
}
|
||||
|
||||
winnerSymbol := t.findWinner(state.Board)
|
||||
winnerSymbol := t.findWinner(b)
|
||||
|
||||
if winnerSymbol != "" {
|
||||
// find the player with this symbol
|
||||
@@ -88,7 +108,7 @@ func (t *TicTacToeRules) CheckGameOver(state *structs.MatchState) (bool, int) {
|
||||
return true, -1
|
||||
}
|
||||
|
||||
if state.Board.Full() {
|
||||
if b.Full() {
|
||||
return true, -1 // draw
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user