package bingo import ( "fmt" "iter" ) type WinDirection int const ( Row WinDirection = iota Column Diagonal ) type Game struct { Board []Tile Length int // the number of rows/cols FreeSquare bool Seed GameSeed } func all(group []Tile) bool { allTrue := true for _, tile := range group { allTrue = allTrue && tile.Checked } return allTrue } // create a new game of a given side length // // returns an error if side length is not odd func newGame(length int) (*Game, error) { if length%2 != 1 || length < 3 { return nil, fmt.Errorf("Invalid Side Length: %d", length) } game := new(Game) game.Length = length return game, nil } // Return if a game has been won func (g Game) Win() bool { for row := range g.Rows() { if all(row) { return true } } for col := range g.Cols() { if all(col) { return true } } for diag := range g.Diags() { if all(diag) { return true } } return false } // Iterator for rows of a board func (g Game) Rows() iter.Seq[[]Tile] { return func(yield func([]Tile) bool) { for row := 0; (row+1)*g.Length <= len(g.Board); row++ { if !yield(g.Board[row*g.Length : (row+1)*g.Length]) { return } } } } // Iterator for columns of a board func (g Game) Cols() iter.Seq[[]Tile] { return func(yield func([]Tile) bool) { for col := 0; col*g.Length+1 < len(g.Board); col++ { column := make([]Tile, g.Length) for i := range g.Length { column[i] = g.Board[i*g.Length+col] } if !yield(column) { return } } } } // Iterator for diagonals of square boards // // On non-square boards yields nothing func (g Game) Diags() iter.Seq[[]Tile] { return func(yield func([]Tile) bool) { if g.Length*g.Length != len(g.Board) { return } diagonal := make([]Tile, g.Length) for i := 0; i < g.Length; i++ { diagonal[i] = g.Board[i*g.Length+i] } if !yield(diagonal) { return } for i := 0; i < g.Length; i++ { diagonal[i] = g.Board[i*g.Length+(g.Length-1-i)] } if !yield(diagonal) { return } } }