package sidecheck import ( "strings" ) // Constants for the different board piece types. const ( BOARD int = iota PIECE SELECTED ) type coords struct { x, y int } func coordsAround(x, y int) []coords { return []coords{ {x - 1, y - 1}, {x - 1, y}, {x - 1, y + 1}, {x, y - 1}, {x, y + 1}, {x + 1, y - 1}, {x + 1, y}, {x + 1, y + 1}, } } // Board defines a square or rectangular grid to represent a chess-style board. type Board struct { rows, cols int board [][]int } // NewBoard creates an empty Board with the given size. func NewBoard(rows, cols int) *Board { board := Board{ rows, cols, make([][]int, rows), } for i := 0; i < rows; i++ { board.board[i] = make([]int, cols) } return &board } // StringToBoard creates a Board from a string. func StringToBoard(b string) *Board { rows := strings.Split(b, "\n") rowMax := len(rows) if rowMax == 0 { panic("Board needs to have at least 1 row") } colMax := len(rows[0]) if colMax == 0 { panic("Board needs to have at least 1 column") } board := *NewBoard(rowMax, colMax) for i := 0; i < rowMax; i++ { if len(rows[i]) != colMax { panic("Column mismatch") } for j := 0; j < colMax; j++ { switch rows[i][j] { case '.': board.board[i][j] = BOARD case 'o': board.board[i][j] = PIECE default: panic("Unknown board char encountered") } } } return &board } // Piecep returns true if there's a piece at x, y. func (b Board) Piecep(x, y int) bool { if x < 0 || y < 0 || x >= b.rows || y >= b.cols { return false } return b.board[x][y] == PIECE } // String converts Board to a string. func (b Board) String() string { var result string spotChar := []string{".", "o", "+"} for _, row := range b.board { for _, spot := range row { result += spotChar[spot] } result += "\n" } return result } // NScheck checks if any pieces allow for a path across. func (b Board) NScheck() bool { for j := 0; j < b.cols; j++ { if b.Piecep(0, j) { if found := b.nscheck(newPiece(0, j)); found { return true } } } return false } func (b Board) nscheck(p *piece) bool { if p.x == (b.rows - 1) { return true } cs := coordsAround(p.x, p.y) for _, c := range cs { if c.y > 0 && b.Piecep(c.x, c.y) && !p.seen(c.x, c.y) { next := newPiece(c.x, c.y) next.parent = p if found := b.nscheck(next); found { return true } } } return false } // WEcheck checks if any pieces allow for a path across. func (b Board) WEcheck() bool { for i := 0; i < b.rows; i++ { if b.Piecep(i, 0) { if found := b.wecheck(newPiece(i, 0)); found { return true } } } return false } func (b Board) wecheck(p *piece) bool { if p.y == (b.cols - 1) { return true } cs := coordsAround(p.x, p.y) for _, c := range cs { if c.x > 0 && b.Piecep(c.x, c.y) && !p.seen(c.x, c.y) { next := newPiece(c.x, c.y) next.parent = p if found := b.wecheck(next); found { return true } } } return false } // piece tracks a connected line of pieces through a board. type piece struct { x, y int parent *piece } // newPiece creates a new node. func newPiece(x, y int) *piece { p := &piece{ x: x, y: y, parent: nil, } return p } // seen returns is a coord has been seen. func (p piece) seen(x, y int) bool { pp := p.parent for pp != nil { if pp.x == x && pp.y == y { return true } pp = pp.parent } return false }