package game import ( "errors" "fmt" "localrepo/plugins/structs" ) // TicTacToeRules implements GameRules for 3x3 Tic Tac Toe. type TicTacToeRules struct{} // ------------------------------- // GameRules Implementation // ------------------------------- func (t *TicTacToeRules) MaxPlayers() int { return 2 } // Assign player symbols: X and O func (t *TicTacToeRules) AssignPlayerSymbols(players []*Player) { if len(players) < 2 { return } players[0].Metadata["symbol"] = "X" players[1].Metadata["symbol"] = "O" } // ValidateMove checks bounds and empty cell. func (t *TicTacToeRules) ValidateMove(state *structs.MatchState, playerIdx int, payload MovePayload) bool { rowVal, ok1 := payload.Data["row"] colVal, ok2 := payload.Data["col"] if !ok1 || !ok2 { return false } row, ok3 := rowVal.(float64) col, ok4 := colVal.(float64) if !ok3 || !ok4 { return false } r := int(row) c := int(col) // bounds if !state.Board.InBounds(r, c) { return false } // empty? return state.Board.IsEmpty(r, c) } // ApplyMove writes X or O to the board. func (t *TicTacToeRules) ApplyMove(state *structs.MatchState, playerIdx int, payload MovePayload) { symbol := state.Players[playerIdx].Metadata["symbol"] r := int(payload.Data["row"].(float64)) c := int(payload.Data["col"].(float64)) state.Board.Set(r, c, symbol) } // CheckGameOver determines win/draw state. func (t *TicTacToeRules) CheckGameOver(state *structs.MatchState) (bool, int) { winnerSymbol := t.findWinner(state.Board) if winnerSymbol != "" { // find the player with this symbol for _, p := range state.Players { if p.Metadata["symbol"] == winnerSymbol { return true, p.Index } } return true, -1 } if state.Board.Full() { return true, -1 // draw } return false, -1 } // OnForfeit: whoever leaves loses instantly func (t *TicTacToeRules) ForfeitWinner(state *structs.MatchState, leaverIndex int) int { // If player 0 leaves, player 1 wins. if leaverIndex == 0 && len(state.Players) > 1 { return 1 } // If player 1 leaves, player 0 wins. if leaverIndex == 1 && len(state.Players) > 0 { return 0 } // Otherwise draw. return -1 } // ------------------------------- // Helper: winner detection // ------------------------------- func (t *TicTacToeRules) findWinner(b *structs.Board) string { lines := [][][2]int{ // rows {{0, 0}, {0, 1}, {0, 2}}, {{1, 0}, {1, 1}, {1, 2}}, {{2, 0}, {2, 1}, {2, 2}}, // cols {{0, 0}, {1, 0}, {2, 0}}, {{0, 1}, {1, 1}, {2, 1}}, {{0, 2}, {1, 2}, {2, 2}}, // diagonals {{0, 0}, {1, 1}, {2, 2}}, {{0, 2}, {1, 1}, {2, 0}}, } for _, line := range lines { r1, c1 := line[0][0], line[0][1] r2, c2 := line[1][0], line[1][1] r3, c3 := line[2][0], line[2][1] v1 := b.Get(r1, c1) if v1 != "" && v1 == b.Get(r2, c2) && v1 == b.Get(r3, c3) { return v1 } } return "" }