diff --git a/Sources/Engine/Board.swift b/Sources/Engine/Board.swift index 57a99aa..0203e54 100644 --- a/Sources/Engine/Board.swift +++ b/Sources/Engine/Board.swift @@ -4,7 +4,7 @@ enum Event { } protocol EventDelegate { func notify(_ event: Event) - func movePiece(_ piece: Piece, to dst: Square.Position) + func movePiece(_ piece: Piece, to dst: Square.Position) throws } public class Board: CustomStringConvertible, EventDelegate { public typealias Grid = [[Square]] @@ -24,8 +24,87 @@ public class Board: CustomStringConvertible, EventDelegate { } - func movePiece(_ piece: Piece, to dst: Square.Position) { + func movePiece(_ piece: Piece, to dst: Square.Position) throws { + let from = piece.position + piece.position = dst + squares[dst.index!].piece = piece + squares[from.index!].piece = nil + fen.set(from: board, castling: .All, enPassant: fen.enPassant) + } + public enum MoveFailure: Error, CustomStringConvertible { + case sourceSquareUnreachable(pos: Square.Position) + case destinationSquareUnreachable(pos: Square.Position) + case squareHasNoPiece(pos: Square.Position) + case destinationIsIllegal(pos: Square.Position) + case squareANIsTooLong(an: String) + case squareANIsTooShort(an: String) + + public var description: String { + return switch self { + case .sourceSquareUnreachable(let pos): + "The source square given \(pos.rank) \(pos.file) is unreachable." + case .destinationSquareUnreachable(let pos): + "The destination square given \(pos.rank) \(pos.file) is unreachable." + case .squareHasNoPiece(let pos): + "The square \(pos.rank) \(pos.file) doesn't have any piece to be moved." + case .destinationIsIllegal(let pos): + "The destination square given \(pos.rank) \(pos.file) is illegal to be moved to." + case .squareANIsTooLong(let an): + "The algebraic notation \(an) is too long. (Needs to be 2 characters)" + case .squareANIsTooShort(let an): + "The algebraic notation \(an) is too short. (Needs to be 2 characters)" + } + } + } + + public func move(src: String, dst: String) throws { + guard src.count < 3 else { + throw MoveFailure.squareANIsTooLong(an: src) + } + guard src.count > 1 else { + throw MoveFailure.squareANIsTooShort(an: src) + } + guard dst.count < 3 else { + throw MoveFailure.squareANIsTooLong(an: dst) + } + guard dst.count > 1 else { + throw MoveFailure.squareANIsTooShort(an: dst) + } + guard let f1 = src.first, let r1 = src.last, r1.isWholeNumber else { + return + } + guard let f2 = dst.first, let r2 = src.last, r2.isWholeNumber else { + return + } + guard + let srcFile = Square.Position.File.init(rawValue: String(f1))?.value + else { + return + } + guard + let dstFile = Square.Position.File.init(rawValue: String(f2))?.value + else { + return + } + + try move( + src: Square.Position(rank: Int8(String(r1))!, file: srcFile), + dst: Square.Position(rank: Int8(String(r2))!, file: dstFile)) + } + + public func move(src: Square.Position, dst: Square.Position) throws { + guard let srcSquare = self[src] else { + throw MoveFailure.sourceSquareUnreachable(pos: src) + } + guard self[dst] != nil else { + throw MoveFailure.destinationSquareUnreachable(pos: src) + } + guard let piece = srcSquare.piece else { + throw MoveFailure.squareHasNoPiece(pos: src) + } + + try piece.move(to: dst) } public func getSquareInfo(on pos: Square.Position) -> Square? { diff --git a/Sources/Engine/FEN.swift b/Sources/Engine/FEN.swift index c7cc30c..a836749 100644 --- a/Sources/Engine/FEN.swift +++ b/Sources/Engine/FEN.swift @@ -64,7 +64,7 @@ public struct Fen: CustomStringConvertible { } internal mutating func set( - from board: Board.Grid, castiling ca: CastlingAvailibility, + from board: Board.Grid, castling ca: CastlingAvailibility, enPassant: String ) { #warning( diff --git a/Sources/Engine/Pieces/Bishop.swift b/Sources/Engine/Pieces/Bishop.swift index 515c0da..74c5840 100644 --- a/Sources/Engine/Pieces/Bishop.swift +++ b/Sources/Engine/Pieces/Bishop.swift @@ -12,20 +12,8 @@ final class Bishop: Piece, DiagonalMoves { return pseudoLegalPositions.filter { isLegal(on: $0) } } - override func move(to dst: Square.Position) { - } - override func isLegal(on pos: Square.Position) -> Bool { - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - delegate?.notify(.kingInCheck(self)) - return false - } - } - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/King.swift b/Sources/Engine/Pieces/King.swift index 95abee5..ebc4091 100644 --- a/Sources/Engine/Pieces/King.swift +++ b/Sources/Engine/Pieces/King.swift @@ -21,21 +21,8 @@ final class King: Piece { return pseudoLegalPositions.filter { isLegal(on: $0) } } - override func move(to dst: Square.Position) { - } - override func isLegal(on pos: Square.Position) -> Bool { - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - // TODO: Notify board of check - return false - } - } - - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/Knight.swift b/Sources/Engine/Pieces/Knight.swift index 1a1e636..2c14116 100644 --- a/Sources/Engine/Pieces/Knight.swift +++ b/Sources/Engine/Pieces/Knight.swift @@ -20,22 +20,8 @@ final class Knight: Piece { return pseudoLegalPositions.filter { isLegal(on: $0) } } - override func move(to dst: Square.Position) { - } - override func isLegal(on pos: Square.Position) -> Bool { - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - // TODO: Notify board of check - delegate?.notify(.kingInCheck(self)) - return false - } - } - - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/Pawn.swift b/Sources/Engine/Pieces/Pawn.swift index 1c92645..517331c 100644 --- a/Sources/Engine/Pieces/Pawn.swift +++ b/Sources/Engine/Pieces/Pawn.swift @@ -17,37 +17,8 @@ final class Pawn: Piece { color == .Black ? "♟" : "♙" } - override func move(to dst: Square.Position) { - if !(legalPositions.contains { $0 == dst }) { - return - } - - delegate?.movePiece(self, to: dst) - - // if let board = board, var s = board[position], var d = board[dst] { - // s.piece = self - // d.piece = nil - // } - - // position = dst - - // return true - } - override func isLegal(on pos: Square.Position) -> Bool { - // TODO: Handle "En-Passant" - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - // TODO: Notify board of check - delegate?.notify(.kingInCheck(self)) - return false - } - } - - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/Piece.swift b/Sources/Engine/Pieces/Piece.swift index 6a37740..4c04b78 100644 --- a/Sources/Engine/Pieces/Piece.swift +++ b/Sources/Engine/Pieces/Piece.swift @@ -69,8 +69,28 @@ public class Piece { return [] } internal var delegate: EventDelegate? - internal func move(to dst: Square.Position) {} - internal func isLegal(on pos: Square.Position) -> Bool { false } + internal func move(to dst: Square.Position) throws { + if !(legalPositions.contains { $0 == dst }) { + throw Board.MoveFailure.destinationIsIllegal(pos: dst) + } + + try delegate?.movePiece(self, to: dst) + } + internal func isLegal(on pos: Square.Position) -> Bool { + + if let board = board, let s = board[pos] { + if let p = s.piece { + if p.color == color { return false } + if p.kind == .King { + // TODO: Notify board of check + delegate?.notify(.kingInCheck(self)) + return false + } + } + + } + return true + } internal init(kind: Kind, on pos: Square.Position, with col: Color) { self.kind = kind diff --git a/Sources/Engine/Pieces/Queen.swift b/Sources/Engine/Pieces/Queen.swift index 585a649..6a1a54d 100644 --- a/Sources/Engine/Pieces/Queen.swift +++ b/Sources/Engine/Pieces/Queen.swift @@ -11,23 +11,8 @@ final class Queen: Piece, LinearMoves, DiagonalMoves { return pseudoLegalPositions.filter { isLegal(on: $0) } } - override func move(to dst: Square.Position) { - - } - override func isLegal(on pos: Square.Position) -> Bool { - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - // TODO: Notify board of check - delegate?.notify(.kingInCheck(self)) - return false - } - } - - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/Rook.swift b/Sources/Engine/Pieces/Rook.swift index 740fd9d..6c9f2f1 100644 --- a/Sources/Engine/Pieces/Rook.swift +++ b/Sources/Engine/Pieces/Rook.swift @@ -12,22 +12,8 @@ final class Rook: Piece, LinearMoves { return pseudoLegalPositions.filter { isLegal(on: $0) } } - override func move(to dst: Square.Position) { - } - override func isLegal(on pos: Square.Position) -> Bool { - if let board = board, let s = board[pos] { - if let p = s.piece { - if p.color == color { return false } - if p.kind == .King { - // TODO: Notify board of check - delegate?.notify(.kingInCheck(self)) - return false - } - } - - } - return true + super.isLegal(on: pos) } init(with color: Color, on position: Square.Position) { diff --git a/Sources/exe/main.swift b/Sources/exe/main.swift index 16bd551..06d4206 100644 --- a/Sources/exe/main.swift +++ b/Sources/exe/main.swift @@ -11,4 +11,18 @@ print(board) // } +do { + try board.move(src: "e4", dst: "f6") +} catch { + print(error) +} +do { + try board.move(src: .init(rank: 2, file: 5), dst: .init(rank: 4, file: 5)) +} catch { + print(error) +} + +print(board) +print(board.fen) + // let square = board.getSquareInfo(on: .init(rank: 2, file: 7)) diff --git a/Tests/EngineTests/EngineTests.swift b/Tests/EngineTests/EngineTests.swift index a8e1b71..4018a90 100644 --- a/Tests/EngineTests/EngineTests.swift +++ b/Tests/EngineTests/EngineTests.swift @@ -109,7 +109,7 @@ final class EngineTests: XCTestCase { let board: Board = .init() let fen = board.fen.value board.fen.set( - from: board.board, castiling: board.fen.castlingAvailibility, + from: board.board, castling: board.fen.castlingAvailibility, enPassant: board.fen.enPassant) XCTAssertEqual( fen, board.fen.value, "Expected \(fen) got \(board.fen.value)")