#import "board.typ": at, is-black, is-white, on-board, piece-type #let enemy(turn, target) = if turn == "white" { is-black(target) } else { is-white(target) } #let pawn-dir(turn) = if turn == "white" { -1 } else { 1 } #let pawn-moves(grid, col, row, turn, ep: none) = { // returns list of (dest-col, dest-row, prom?), ep == en passant let moves = () let start-row = if turn == "white" { 7 } else { 2 } let dir = pawn-dir(turn) // forward one let nr = row + dir if on-board(col, nr) and at(grid, col, nr) == "." { if nr == 1 or nr == 8 { for prom in ("Q", "R", "B", "N") { moves.push((col, nr, prom)) } } else { moves.push((col, nr)) } // forward two from start let nr2 = row + 2 * dir if row == start-row and at(grid, col, nr2) == "." { moves.push((col, nr2)) } } // captures for dc in (-1, 1) { let nc = col + dc let nr = row + dir if on-board(nc, nr) { if enemy(turn, at(grid, nc, nr)) { if nr == 1 or nr == 8 { for prom in ("Q", "R", "B", "N") { moves.push((nc, nr, prom)) } } else { moves.push((nc, nr)) } } // en passant capture: pawn ends on ep square, captured pawn is one rank behind if ep != none and nc == ep.at(0) { let captured-row = ep.at(1) - dir if enemy(turn, at(grid, nc, captured-row)) { moves.push((ep.at(0), ep.at(1))) } } } } moves } #let knight-offsets = ((-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)) #let knight-moves(grid, col, row, turn) = { let moves = () for (dc, dr) in knight-offsets { let nc = col + dc let nr = row + dr if on-board(nc, nr) { let target = at(grid, nc, nr) if target == "." or enemy(turn, target) { moves.push((nc, nr)) } } } moves } #let sliding-moves(grid, col, row, turn, dirs) = { let moves = () for (dc, dr) in dirs { let nc = col + dc let nr = row + dr while on-board(nc, nr) { let target = at(grid, nc, nr) if target == "." { moves.push((nc, nr)) } else if enemy(turn, target) { moves.push((nc, nr)) break } else { break } nc = nc + dc nr = nr + dr } } moves } #let bishop-dirs = ((-1, -1), (-1, 1), (1, -1), (1, 1)) #let bishop-moves(grid, col, row, turn) = sliding-moves(grid, col, row, turn, bishop-dirs) #let rook-dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) #let rook-moves(grid, col, row, turn) = sliding-moves(grid, col, row, turn, rook-dirs) #let queen-moves(grid, col, row, turn) = { let moves = () for m in bishop-moves(grid, col, row, turn) { moves.push(m) } for m in rook-moves(grid, col, row, turn) { moves.push(m) } moves } #let king-dirs = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)) #let king-moves(grid, col, row, turn) = { let moves = () for (dc, dr) in king-dirs { let nc = col + dc let nr = row + dr if on-board(nc, nr) { if at(grid, nc, nr) == "." or enemy(turn, at(grid, nc, nr)) { moves.push((nc, nr)) } } } moves } #let generate-moves-for(grid, col, row, turn, ep: none) = { let p = at(grid, col, row) if p == "." { return () } if turn == "white" and not is-white(p) { return () } if turn == "black" and not is-black(p) { return () } let type = piece-type(p) if type == "P" { return pawn-moves(grid, col, row, turn, ep: ep) } if type == "N" { return knight-moves(grid, col, row, turn) } if type == "B" { return bishop-moves(grid, col, row, turn) } if type == "R" { return rook-moves(grid, col, row, turn) } if type == "Q" { return queen-moves(grid, col, row, turn) } if type == "K" { return king-moves(grid, col, row, turn) } () }