### Highlights
- Introduced generic match engine (`generic_match.go`) implementing dynamic GameRules-based runtime.
- Added modular structure under `/plugins`:
- /plugins/game → GameRules interface + TicTacToe + Battleship rule sets
- /plugins/structs → Board, Player, MatchState generic structs
- /plugins/modules → matchmaking + RPC handlers + match engine
- Migrated TicTacToe logic into reusable rule implementation.
- Added Battleship game support using same engine.
- Updated matchmaking to accept { game, mode } for multi-game routing.
- Updated UI contract: clients must send `game` (and optional `mode`) when joining matchmaking.
- Removed hardcoded TicTacToe match registration.
- Registered a single “generic” authoritative match with ruleset registry.
- Normalized imports under local dev module path.
- Ensured MatchState and Board are now generic and reusable across games.
- Added strict requirement for `game` metadata in match flow (error if missing).
- Cleaned initial state creation into MatchInit with flexible board dimensions.
- Improved MatchLeave for proper forfeit handling through GameRules.
### Result
The server now supports an unlimited number of turn-based board games
via swappable rulesets while keeping a single authoritative Nakama match loop.
52 lines
1008 B
Go
52 lines
1008 B
Go
package game
|
|
|
|
// Board is a generic 2D grid for turn-based games.
|
|
// Cell data is stored as strings, but can represent anything (piece, move, state).
|
|
type Board struct {
|
|
Rows int `json:"rows"`
|
|
Cols int `json:"cols"`
|
|
Grid [][]string `json:"grid"`
|
|
}
|
|
|
|
// NewBoard creates a grid of empty strings.
|
|
func NewBoard(rows, cols int) *Board {
|
|
b := &Board{
|
|
Rows: rows,
|
|
Cols: cols,
|
|
Grid: make([][]string, rows),
|
|
}
|
|
|
|
for r := 0; r < rows; r++ {
|
|
b.Grid[r] = make([]string, cols)
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *Board) InBounds(row, col int) bool {
|
|
return row >= 0 && row < b.Rows && col >= 0 && col < b.Cols
|
|
}
|
|
|
|
func (b *Board) Get(row, col int) string {
|
|
return b.Grid[row][col]
|
|
}
|
|
|
|
func (b *Board) Set(row, col int, value string) {
|
|
b.Grid[row][col] = value
|
|
}
|
|
|
|
func (b *Board) IsEmpty(row, col int) bool {
|
|
return b.Grid[row][col] == ""
|
|
}
|
|
|
|
func (b *Board) Full() bool {
|
|
for r := 0; r < b.Rows; r++ {
|
|
for c := 0; c < b.Cols; c++ {
|
|
if b.Grid[r][c] == "" {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|