package oop.ch01.chess.scala import oop.ch01.chess.scala.Color.{black, white} import oop.ch01.chess.scala.Kind.rook import scala.collection.mutable.ArrayBuffer enum Color: case black, white enum Kind: case queen, knight, rook class Piece(var kind: Kind, var color: Color, var board: Board, var row: Int, var col: Int) class Board(var pieces: List[Piece]) object Chess: def newPiece(kind: Kind, color: Color, board: Board, row: Int, col: Int): Piece = if row < 1 || row > 8 || col < 1 || col > 8 then throw new IllegalArgumentException("Invalid pos " + row + "/" + col) val piece = new Piece(kind, color, board, row, col) add(board, piece) piece def charRep(piece: Piece): Char = piece.kind match case Kind.queen => queenCharRep(piece) case Kind.knight => knightCharRep(piece) case Kind.rook => rookCharRep(piece) def toString(piece: Piece): String = piece.kind match case Kind.queen => queenToString(piece) case Kind.knight => knightToString(piece) case Kind.rook => rookToString(piece) def canCapture(piece: Piece, other: Piece): Boolean = piece.kind match case Kind.queen => queenCanCapture(piece, other) case Kind.knight => knightCanCapture(piece, other) case Kind.rook => rookCanCapture(piece, other) def queenCharRep(piece: Piece): Char = if piece.color == Color.white then 'q' else 'Q' def knightCharRep(piece: Piece): Char = if piece.color == Color.white then 'n' else 'N' def rookCharRep(piece: Piece): Char = if piece.color == Color.white then 'r' else 'R' def queenToString(piece: Piece): String = "" + piece.color + " queen at (" + piece.row + ", " + piece.col + ")" def knightToString(piece: Piece): String = "" + piece.color + " knight at (" + piece.row + ", " + piece.col + ")" def rookToString(piece: Piece): String = "" + piece.color + " rook at (" + piece.row + ", " + piece.col + ")" def queenCanCapture(queen: Piece, other: Piece): Boolean = if queen.board != other.board || queen.color == other.color then return false if other.row != queen.row && other.col != queen.col && (other.row - queen.row).abs != (other.col - queen.col).abs then return false val dr: Int = (other.row - queen.row).sign val dc: Int = (other.col - queen.col).sign var r: Int = queen.row + dr var c: Int = queen.col + dc while r != other.row || c != other.col do if pieceAt(queen.board, r, c) != null then return false r += dr c += dc true def knightCanCapture(knight: Piece, other: Piece): Boolean = if knight.board != other.board || knight.color == other.color then return false val dr: Int = (knight.row - other.row).abs val dc: Int = (knight.col - other.col).abs dr == 2 && dc == 1 || dr == 1 && dc == 2 def rookCanCapture(piece: Piece, other: Piece): Boolean = if piece.board != other.board || piece.color == other.color then return false if other.row != piece.row && other.col != piece.col then return false val dr: Int = (other.row - piece.row).sign val dc: Int = (other.col - piece.col).sign var r: Int = piece.row + dr var c: Int = piece.col + dc while r != other.row || c != other.col do if pieceAt(piece.board, r, c) != null then return false r += dr c += dc true def newBoard(): Board = new Board(List()) def add(board: Board, piece: Piece): Unit = if piece.board != board then throw new IllegalArgumentException("wrong board") val existing = pieceAt(board, piece.row, piece.col) if existing != null then throw new IllegalArgumentException("already occupied by " + toString(existing)) piece.board.pieces = piece :: piece.board.pieces def toString(board: Board): String = //"pieces: " + board.pieces "pieces: " + board.pieces.map(toString) def printBoard(board: Board): Unit = println(" 1 2 3 4 5 6 7 8") println(" +---+---+---+---+---+---+---+---+") for row: Int <- 1 to 8 do print("" + row + " ") for col: Int <- 1 to 8 do val p: Piece = pieceAt(board, row, col) val c: Char = if p == null then ' ' else charRep(p) print("| " + c + " ") println("|") println(" +---+---+---+---+---+---+---+---+") def pieceAt(board: Board, row: Int, col: Int): Piece = for p: Piece <- board.pieces do if p.row == row && p.col == col then return p null def check(board: Board): Unit = for p1: Piece <- board.pieces do println(toString(p1)) for p2: Piece <- board.pieces if p1 != p2 do if canCapture(p1, p2) then println(" can capture " + toString(p2)) else println(" cannot capture " + toString(p2)) def main(args: Array[String]): Unit = val board: Board = newBoard() val p1: Piece = newPiece(Kind.queen, Color.black, board, 1, 4) val p2: Piece = newPiece(Kind.queen, Color.white, board, 8, 4) val p3: Piece = newPiece(Kind.knight, Color.white, board, 3, 3) val p4: Piece = newPiece(Kind.knight, Color.black, board, 6, 4) val p5: Piece = newPiece(Kind.rook, white, board, 2,7) val p6: Piece = newPiece(rook, black, board, 2,1) println(board) println(toString(board)) printBoard(board) check(board)