diff --git a/Sources/Engine/Board.swift b/Sources/Engine/Board.swift index 0203e54..88cd9e8 100644 --- a/Sources/Engine/Board.swift +++ b/Sources/Engine/Board.swift @@ -1,5 +1,5 @@ enum Event { - case kingInCheck(_ by: Piece) + case kingInCheck(_ by: Piece, on: King) case piecePinned(from: Piece, on: Piece) } protocol EventDelegate { @@ -24,12 +24,30 @@ public class Board: CustomStringConvertible, EventDelegate { } + public private(set) var halfMoveClock: UInt8 = 0 + + public var fullMoveClock: UInt8 { + halfMoveClock / 2 + } + + internal var threatenedSquares: [Color: Set] = [ + .White: [], + .Black: [], + ] + + public private(set) var turn: Color = .White + 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) + halfMoveClock += 1 + turn = !turn + fen.set( + from: board, activeColor: turn, castling: .All, + enPassant: fen.enPassant, + halfMoveClock: halfMoveClock, fullMoveClock: fullMoveClock) } public enum MoveFailure: Error, CustomStringConvertible { @@ -105,6 +123,12 @@ public class Board: CustomStringConvertible, EventDelegate { } try piece.move(to: dst) + #warning("May need to clear threatenedSquares before hand.") + for square in squares where square.piece != nil { + let p = square.piece! + p.getLegalPosition() + threatenedSquares[p.color]! += Set(p.legalPositions) + } } public func getSquareInfo(on pos: Square.Position) -> Square? { @@ -183,9 +207,6 @@ public class Board: CustomStringConvertible, EventDelegate { file += 1 index += 1 } - #if DEBUG - print(b) - #endif squares = b } @@ -206,7 +227,7 @@ public class Board: CustomStringConvertible, EventDelegate { rank -= 1 } } - // TODO: Handle better + #warning("Handle better.") do { try setBoard() } catch Fen.FenError.NotAppropriateLength(let n, let column) { diff --git a/Sources/Engine/Extensions/Set+Union.swift b/Sources/Engine/Extensions/Set+Union.swift new file mode 100644 index 0000000..b28e31c --- /dev/null +++ b/Sources/Engine/Extensions/Set+Union.swift @@ -0,0 +1,9 @@ +extension Set { + static func + (lhs: Set, rhs: Set) -> Set { + return lhs.union(rhs) + } + + static func += (lhs: inout Set, rhs: Set) { + lhs.formUnion(rhs) + } +} diff --git a/Sources/Engine/FEN.swift b/Sources/Engine/FEN.swift index a836749..c84a532 100644 --- a/Sources/Engine/FEN.swift +++ b/Sources/Engine/FEN.swift @@ -64,8 +64,9 @@ public struct Fen: CustomStringConvertible { } internal mutating func set( - from board: Board.Grid, castling ca: CastlingAvailibility, - enPassant: String + from board: Board.Grid, activeColor: Color, + castling ca: CastlingAvailibility, + enPassant: String, halfMoveClock hmc: UInt8, fullMoveClock fmc: UInt8 ) { #warning( "Determine active color, halfMoveClock, fullMoveClock based on history SAN later passed in arguments" @@ -95,7 +96,8 @@ public struct Fen: CustomStringConvertible { rankNr += 1 } - value = placement + " w " + ca.rawValue + " " + enPassant + " 0 " + "1" + value = + "\(placement) \(activeColor) \(ca.rawValue) \(enPassant) \(halfMoveClock) \(fullMoveClock)" } public enum FenError: Error { diff --git a/Sources/Engine/Pieces/Bishop.swift b/Sources/Engine/Pieces/Bishop.swift index 74c5840..af1047a 100644 --- a/Sources/Engine/Pieces/Bishop.swift +++ b/Sources/Engine/Pieces/Bishop.swift @@ -8,10 +8,6 @@ final class Bishop: Piece, DiagonalMoves { return getDiagonalMoves(from: position) } - override var legalPositions: [Square.Position] { - return pseudoLegalPositions.filter { isLegal(on: $0) } - } - override func isLegal(on pos: Square.Position) -> Bool { super.isLegal(on: pos) } diff --git a/Sources/Engine/Pieces/King.swift b/Sources/Engine/Pieces/King.swift index ebc4091..b95af31 100644 --- a/Sources/Engine/Pieces/King.swift +++ b/Sources/Engine/Pieces/King.swift @@ -1,5 +1,6 @@ final class King: Piece { - + typealias Threats = (Piece?, Piece?) + var threats: Threats = (nil, nil) override var unicodeRepresentation: String { return color == .Black ? "♛" : "♕" } @@ -17,12 +18,12 @@ final class King: Piece { ].filter { $0.index != nil } } - override var legalPositions: [Square.Position] { - return pseudoLegalPositions.filter { isLegal(on: $0) } - } - override func isLegal(on pos: Square.Position) -> Bool { - super.isLegal(on: pos) + guard super.isLegal(on: pos) else { + return false + } + // Do stuff + return true } init(with color: Color, on position: Square.Position) { diff --git a/Sources/Engine/Pieces/Knight.swift b/Sources/Engine/Pieces/Knight.swift index 2c14116..6682d67 100644 --- a/Sources/Engine/Pieces/Knight.swift +++ b/Sources/Engine/Pieces/Knight.swift @@ -16,10 +16,6 @@ final class Knight: Piece { ].filter { $0.index != nil } } - override var legalPositions: [Square.Position] { - return pseudoLegalPositions.filter { isLegal(on: $0) } - } - override func isLegal(on pos: Square.Position) -> Bool { super.isLegal(on: pos) } diff --git a/Sources/Engine/Pieces/Pawn.swift b/Sources/Engine/Pieces/Pawn.swift index 517331c..fa2d50b 100644 --- a/Sources/Engine/Pieces/Pawn.swift +++ b/Sources/Engine/Pieces/Pawn.swift @@ -9,10 +9,6 @@ final class Pawn: Piece { ].filter { $0.index != nil } } - override var legalPositions: [Square.Position] { - pseudoLegalPositions.filter { isLegal(on: $0) } - } - override var unicodeRepresentation: String { color == .Black ? "♟" : "♙" } diff --git a/Sources/Engine/Pieces/Piece.swift b/Sources/Engine/Pieces/Piece.swift index 4c04b78..5023495 100644 --- a/Sources/Engine/Pieces/Piece.swift +++ b/Sources/Engine/Pieces/Piece.swift @@ -54,9 +54,10 @@ public enum Kind: String, CaseIterable { } public class Piece { - #warning("TODO: TO be removed, handle everything through the delegate") + #warning("TODO: To be removed, handle everything through the delegate") internal weak var board: Board? public internal(set) var color: Color + public internal(set) var halfMoveCount: UInt8 = 0 public var unicodeRepresentation: String { return "" } @@ -65,25 +66,29 @@ public class Piece { internal var pseudoLegalPositions: [Square.Position] { return [] } - internal var legalPositions: [Square.Position] { - return [] + internal var legalPositions = [Square.Position]() + internal func getLegalPosition() { + legalPositions = pseudoLegalPositions.filter { isLegal(on: $0) } } internal var delegate: EventDelegate? + 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) + halfMoveCount += 1 } - internal func isLegal(on pos: Square.Position) -> Bool { + #warning("This method should be better thought out.") + 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)) + if let king = p as? King { + #warning("It could be in check or behind another piece.") + delegate?.notify(.kingInCheck(self, on: king)) return false } } diff --git a/Sources/Engine/Pieces/Queen.swift b/Sources/Engine/Pieces/Queen.swift index 6a1a54d..b5e375a 100644 --- a/Sources/Engine/Pieces/Queen.swift +++ b/Sources/Engine/Pieces/Queen.swift @@ -7,10 +7,6 @@ final class Queen: Piece, LinearMoves, DiagonalMoves { return getDiagonalMoves(from: position) + getLinearMoves(from: position) } - override var legalPositions: [Square.Position] { - return pseudoLegalPositions.filter { isLegal(on: $0) } - } - override func isLegal(on pos: Square.Position) -> Bool { super.isLegal(on: pos) } diff --git a/Sources/Engine/Pieces/Rook.swift b/Sources/Engine/Pieces/Rook.swift index 6c9f2f1..0ed9fba 100644 --- a/Sources/Engine/Pieces/Rook.swift +++ b/Sources/Engine/Pieces/Rook.swift @@ -8,10 +8,6 @@ final class Rook: Piece, LinearMoves { return getLinearMoves(from: position) } - override var legalPositions: [Square.Position] { - return pseudoLegalPositions.filter { isLegal(on: $0) } - } - override func isLegal(on pos: Square.Position) -> Bool { super.isLegal(on: pos) } diff --git a/Sources/Engine/Square.swift b/Sources/Engine/Square.swift index 79aaa91..22165c5 100644 --- a/Sources/Engine/Square.swift +++ b/Sources/Engine/Square.swift @@ -1,6 +1,6 @@ public struct Square: Equatable { - public struct Position: Equatable { + public struct Position: Equatable, Hashable { public let rank: Int8 public let file: Int8 diff --git a/Sources/Engine/utils.swift b/Sources/Engine/utils.swift index d15222a..02ccaad 100644 --- a/Sources/Engine/utils.swift +++ b/Sources/Engine/utils.swift @@ -1,4 +1,15 @@ -public enum Color: UInt8 { +public enum Color: UInt8, CustomStringConvertible { case Black = 0 case White = 1 + + public var description: String { + return switch self { + case .Black: "b" + case .White: "w" + } + } + + public static prefix func ! (rhs: Self) -> Self { + return rhs == .White ? .Black : .White + } } diff --git a/Tests/EngineTests/EngineTests.swift b/Tests/EngineTests/EngineTests.swift index 4018a90..4ab67a6 100644 --- a/Tests/EngineTests/EngineTests.swift +++ b/Tests/EngineTests/EngineTests.swift @@ -109,8 +109,11 @@ final class EngineTests: XCTestCase { let board: Board = .init() let fen = board.fen.value board.fen.set( - from: board.board, castling: board.fen.castlingAvailibility, - enPassant: board.fen.enPassant) + from: board.board, activeColor: board.fen.activeColor, + castling: board.fen.castlingAvailibility, + enPassant: board.fen.enPassant, + halfMoveClock: + board.fen.halfMoveClock, fullMoveClock: board.fen.fullMoveClock) XCTAssertEqual( fen, board.fen.value, "Expected \(fen) got \(board.fen.value)") }