package games import ( "fmt" "localrepo/plugins/structs" ) // // BATTLESHIP RULES IMPLEMENTATION // // NOTES: // - 2 players // - Each player has 2 boards: // 1. Their own ship board (state.Board is not reused here) // 2. Their "shots" board (hits/misses on opponent) // - We store boards in Player.Metadata as JSON strings // (simplest method without changing your structs). // // ShipBoard and ShotBoard are encoded inside Metadata: // // Metadata["ship_board"] = JSON string of [][]string // Metadata["shot_board"] = JSON string of [][]string // // ------------------------------ // Helpers: encode/decode // ------------------------------ func encodeBoard(b [][]string) string { out := "[" for i, row := range b { out += "[" for j, col := range row { out += fmt.Sprintf("%q", col) if j < len(row)-1 { out += "," } } out += "]" if i < len(b)-1 { out += "," } } out += "]" return out } func decodeBoard(s string) [][]string { var out [][]string // should never fail; safe fallback _ = json.Unmarshal([]byte(s), &out) return out } // ------------------------------ // BattleshipRules // ------------------------------ type BattleshipRules struct{} func (b *BattleshipRules) MaxPlayers() int { return 2 } // ------------------------------ // Assign player boards // ------------------------------ func (b *BattleshipRules) AssignPlayerSymbols(players []*Player) { // Battleship has no symbols like X/O, // but we use this hook to initialize per-player boards. for _, p := range players { // 10x10 boards empty := make([][]string, 10) for r := range empty { empty[r] = make([]string, 10) } // ship board → players place ships manually via a "setup" phase p.Metadata["ship_board"] = encodeBoard(empty) // shot board → empty grid that tracks hits/misses p.Metadata["shot_board"] = encodeBoard(empty) } } // ------------------------------ // ValidateMove // payload.data = { "row": int, "col": int } // ------------------------------ func (b *BattleshipRules) ValidateMove(state *structs.MatchState, playerIdx int, payload MovePayload) bool { rF, ok1 := payload.Data["row"].(float64) cF, ok2 := payload.Data["col"].(float64) if !ok1 || !ok2 { return false } r := int(rF) c := int(cF) if r < 0 || r > 9 || c < 0 || c > 9 { return false } // Check if this spot was already shot before shotBoard := decodeBoard(state.Players[playerIdx].Metadata["shot_board"]) return shotBoard[r][c] == "" } // ------------------------------ // ApplyMove // ------------------------------ func (b *BattleshipRules) ApplyMove(state *structs.MatchState, playerIdx int, payload MovePayload) { attacker := state.Players[playerIdx] defenderIdx := 1 - playerIdx defender := state.Players[defenderIdx] r := int(payload.Data["row"].(float64)) c := int(payload.Data["col"].(float64)) shotBoard := decodeBoard(attacker.Metadata["shot_board"]) shipBoard := decodeBoard(defender.Metadata["ship_board"]) if shipBoard[r][c] == "S" { // hit shotBoard[r][c] = "H" shipBoard[r][c] = "X" // ship cell destroyed } else { // miss shotBoard[r][c] = "M" } // Save back attacker.Metadata["shot_board"] = encodeBoard(shotBoard) defender.Metadata["ship_board"] = encodeBoard(shipBoard) } // ------------------------------ // CheckGameOver // ------------------------------ func (b *BattleshipRules) CheckGameOver(state *structs.MatchState) (bool, int) { for i, p := range state.Players { ships := decodeBoard(p.Metadata["ship_board"]) alive := false for r := range ships { for c := range ships[r] { if ships[r][c] == "S" { alive = true } } } if !alive { // this player has no ships left → opponent wins return true, 1 - i } } return false, -1 } // ------------------------------ // Forfeit Winner // ------------------------------ func (b *BattleshipRules) ForfeitWinner(state *structs.MatchState, leaverIndex int) int { // If player leaves, opponent automatically wins. if leaverIndex == 0 { return 1 } if leaverIndex == 1 { return 0 } return -1 }