From d3503f1441ed233a145a928b8e4d49479d1cbced Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Sun, 30 Jun 2024 21:06:03 +0200 Subject: [PATCH] Sliding pieces moves are now illegal if after piece of same color --- Sources/Engine/Board.swift | 35 +------ Sources/Engine/Pieces/Bishop.swift | 18 ++++ Sources/Engine/Pieces/CommonMoves.swift | 126 ++++++++++++++++++++---- Sources/Engine/Pieces/Piece.swift | 2 +- Sources/Engine/Pieces/Queen.swift | 21 ++++ Sources/Engine/Pieces/Rook.swift | 18 ++++ Sources/Engine/Square.swift | 35 +++++++ Sources/exe/main.swift | 24 ++++- 8 files changed, 225 insertions(+), 54 deletions(-) diff --git a/Sources/Engine/Board.swift b/Sources/Engine/Board.swift index 323299f..8096f47 100644 --- a/Sources/Engine/Board.swift +++ b/Sources/Engine/Board.swift @@ -145,38 +145,11 @@ public class Board: CustomStringConvertible, EventDelegate { } public func move(src: String, dst: String) throws { - guard src.count < 3 else { - throw MoveFailure.squareANIsTooLong(an: src) + if let from = try Square.Position(with: src), + let to = try Square.Position(with: dst) + { + try move(src: from, dst: to) } - 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 = dst.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 { diff --git a/Sources/Engine/Pieces/Bishop.swift b/Sources/Engine/Pieces/Bishop.swift index af1047a..67787e5 100644 --- a/Sources/Engine/Pieces/Bishop.swift +++ b/Sources/Engine/Pieces/Bishop.swift @@ -8,6 +8,24 @@ final class Bishop: Piece, DiagonalMoves { return getDiagonalMoves(from: position) } + override func getLegalPosition() { + legalPositions = [] + var last: Square.Position? = nil + for position in pseudoLegalPositions { + if !isLegal(on: position) { + last = position + continue + } + + if !DiagonalDirection.isLegal(last: &last, current: position) { + last = nil + } + if last == nil { + legalPositions.append(position) + } + } + } + override func isLegal(on pos: Square.Position) -> Bool { super.isLegal(on: pos) } diff --git a/Sources/Engine/Pieces/CommonMoves.swift b/Sources/Engine/Pieces/CommonMoves.swift index 9896888..45bbc1b 100644 --- a/Sources/Engine/Pieces/CommonMoves.swift +++ b/Sources/Engine/Pieces/CommonMoves.swift @@ -1,30 +1,118 @@ -fileprivate func getDirectionalMoves(from pos: Square.Position, with dir: [(Int8, Int8)]) -> [Square.Position] { - var squares = [Square.Position]() - for i: (Int8, Int8) in dir { - var currentSquare = pos + i - while currentSquare.index != nil { - squares.append(currentSquare) - currentSquare += i - } - } - return squares +private func getDirectionalMoves( + from pos: Square.Position, with dir: [(Int8, Int8)] +) -> [Square.Position] { + var squares = [Square.Position]() + for i: (Int8, Int8) in dir { + var currentSquare = pos + i + while currentSquare.index != nil { + squares.append(currentSquare) + currentSquare += i + } + } + return squares } + protocol DiagonalMoves { - func getDiagonalMoves(from pos: Square.Position) -> [Square.Position] + func getDiagonalMoves(from pos: Square.Position) -> [Square.Position] +} + +protocol Direction: CaseIterable { + var values: (Int8, Int8) { get } + static func isLegal( + last: inout Square.Position?, + current: Square.Position + ) -> Bool +} + +public enum DiagonalDirection: Direction { + case northWest + case northEast + case southEast + case southWest + + public var values: (Int8, Int8) { + return switch self { + case .northWest: (1, -1) + case .northEast: (1, 1) + case .southEast: (-1, 1) + case .southWest: (-1, -1) + } + } + public static func isLegal( + last: inout Square.Position?, + current: + Square.Position + ) -> Bool { + var isPred = false + for dir in DiagonalDirection.allCases { + isPred = last == current - dir.values + if isPred { + last = current + break + } + } + + return isPred + } } extension DiagonalMoves { - func getDiagonalMoves(from pos: Square.Position) -> [Square.Position] { - getDirectionalMoves(from: pos, with: [(1, -1), (1, 1), (-1, 1), (-1, -1)]) - } + func getDiagonalMoves(from pos: Square.Position) -> [Square.Position] { + getDirectionalMoves( + from: pos, + with: [ + DiagonalDirection.northWest.values, + DiagonalDirection.northEast.values, + DiagonalDirection.southEast.values, + DiagonalDirection.southWest.values, + ]) + } } protocol LinearMoves { - func getLinearMoves(from pos: Square.Position) -> [Square.Position] + func getLinearMoves(from pos: Square.Position) -> [Square.Position] +} + +public enum LinearDirection: Direction { + case north + case south + case east + case west + + public var values: (Int8, Int8) { + return switch self { + case .north: (1, 0) + case .south: (-1, 0) + case .east: (0, 1) + case .west: (0, -1) + } + } + + public static func isLegal( + last: inout Square.Position?, current: Square.Position + ) -> Bool { + var isPred = false + for dir in LinearDirection.allCases { + isPred = last == current - dir.values + if isPred { + last = current + break + } + } + + return isPred + } } extension LinearMoves { - func getLinearMoves(from pos: Square.Position) -> [Square.Position] { - getDirectionalMoves(from: pos, with: [(1, 0), (0, 1), (-1, 0), (0, -1)]) - } -} \ No newline at end of file + func getLinearMoves(from pos: Square.Position) -> [Square.Position] { + getDirectionalMoves( + from: pos, + with: [ + LinearDirection.north.values, + LinearDirection.east.values, + LinearDirection.south.values, + LinearDirection.west.values, + ]) + } +} diff --git a/Sources/Engine/Pieces/Piece.swift b/Sources/Engine/Pieces/Piece.swift index 5faebb0..212b4b6 100644 --- a/Sources/Engine/Pieces/Piece.swift +++ b/Sources/Engine/Pieces/Piece.swift @@ -65,7 +65,7 @@ public class Piece: Hashable { internal var pseudoLegalPositions: [Square.Position] { return [] } - internal var legalPositions = [Square.Position]() + package internal(set) var legalPositions = [Square.Position]() internal func getLegalPosition() { legalPositions = pseudoLegalPositions.filter { isLegal(on: $0) } } diff --git a/Sources/Engine/Pieces/Queen.swift b/Sources/Engine/Pieces/Queen.swift index b5e375a..90cd67c 100644 --- a/Sources/Engine/Pieces/Queen.swift +++ b/Sources/Engine/Pieces/Queen.swift @@ -7,6 +7,27 @@ final class Queen: Piece, LinearMoves, DiagonalMoves { return getDiagonalMoves(from: position) + getLinearMoves(from: position) } + override func getLegalPosition() { + legalPositions = [] + var last: Square.Position? = nil + for position in pseudoLegalPositions { + if !isLegal(on: position) { + last = position + continue + } + + if !LinearDirection.isLegal(last: &last, current: position) + && !DiagonalDirection.isLegal(last: &last, current: position) + { + last = nil + } + + if last == nil { + legalPositions.append(position) + } + } + } + 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 0ed9fba..c0929d9 100644 --- a/Sources/Engine/Pieces/Rook.swift +++ b/Sources/Engine/Pieces/Rook.swift @@ -8,6 +8,24 @@ final class Rook: Piece, LinearMoves { return getLinearMoves(from: position) } + override func getLegalPosition() { + legalPositions = [] + var last: Square.Position? = nil + for position in pseudoLegalPositions { + if !isLegal(on: position) { + last = position + continue + } + + if !LinearDirection.isLegal(last: &last, current: position) { + last = nil + } + if last == nil { + legalPositions.append(position) + } + } + } + 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 cf5f3af..2bc574c 100644 --- a/Sources/Engine/Square.swift +++ b/Sources/Engine/Square.swift @@ -16,6 +16,25 @@ public struct Square: Equatable { file = f } + public init?(with rep: String) throws { + guard rep.count < 3 else { + throw Board.MoveFailure.squareANIsTooLong(an: rep) + } + guard rep.count > 1 else { + throw Board.MoveFailure.squareANIsTooShort(an: rep) + } + guard let f1 = rep.first, let r1 = rep.last, r1.isWholeNumber else { + return nil + } + guard + let srcFile = File.init(rawValue: String(f1))?.value + else { + return nil + } + rank = Int8(String(r1))! + file = srcFile + } + public enum File: String, CustomStringConvertible { case a, b, c, d, e, f, g, h @@ -70,6 +89,22 @@ public struct Square: Equatable { public static func += (lhs: inout Position, rhs: (Int8, Int8)) { lhs = lhs + rhs } + + public static func - (lhs: Position, rhs: Position) -> Position { + .init(rank: lhs.rank - rhs.rank, file: lhs.file - rhs.file) + } + + public static func - (lhs: Position, rhs: (Int8, Int8)) -> Position { + .init(rank: lhs.rank - rhs.0, file: lhs.file - rhs.1) + } + + public static func -= (lhs: inout Position, rhs: Position) { + lhs = lhs - rhs + } + + public static func -= (lhs: inout Position, rhs: (Int8, Int8)) { + lhs = lhs - rhs + } } public let position: Position diff --git a/Sources/exe/main.swift b/Sources/exe/main.swift index b479d59..67cdfaf 100644 --- a/Sources/exe/main.swift +++ b/Sources/exe/main.swift @@ -7,6 +7,7 @@ enum Command: Equatable { case move(from: String, to: String) case history(command: String? = nil) case highlightThreatened(color: Color) + case highlightPositions(of: Square.Position) init?(rawValue: String) { let args = rawValue.lowercased().split(separator: " ") @@ -28,16 +29,25 @@ enum Command: Equatable { case "history", "h": self = .history() case "highlight" where args.count == 2, "hi" where args.count == 2: - let color: Color = + let color: Color? = switch args[1] { case "w", "white": .White case "b", "black": .Black default: - .White + nil } - self = .highlightThreatened(color: color) + if let c = color { + self = .highlightThreatened(color: c) + return + } + + if let position = try? Square.Position(with: String(args[1])) { + self = .highlightPositions(of: position) + return + } + return nil default: return nil } @@ -81,6 +91,14 @@ while command != .quit { print(ts.count) print(board.text(with: [color: Array(ts)])) } + case .highlightPositions(let pos): + if let square = board.getSquareInfo(on: pos), + let piece = + square.piece + { + let color = Board.TerminalColors(fg: .def, bg: .green) + print(board.text(with: [color: piece.legalPositions])) + } } } }