aboutsummaryrefslogtreecommitdiff
path: root/Sources/SwiftChessNeo/Square.swift
diff options
context:
space:
mode:
authorNavan Chauhan <navanchauhan@gmail.com>2024-04-17 12:02:38 -0600
committerNavan Chauhan <navanchauhan@gmail.com>2024-04-17 12:02:38 -0600
commite7e30f219c0129db7cb72f04e200098417ce25d0 (patch)
tree3d34da76f3a45e93d4d2fc31aad13a3712208e53 /Sources/SwiftChessNeo/Square.swift
parentebfdb87512e7e32787b71e7a162ff109584f6fcf (diff)
initial commit
Diffstat (limited to 'Sources/SwiftChessNeo/Square.swift')
-rw-r--r--Sources/SwiftChessNeo/Square.swift428
1 files changed, 428 insertions, 0 deletions
diff --git a/Sources/SwiftChessNeo/Square.swift b/Sources/SwiftChessNeo/Square.swift
new file mode 100644
index 0000000..8dbc17c
--- /dev/null
+++ b/Sources/SwiftChessNeo/Square.swift
@@ -0,0 +1,428 @@
+//
+// Square.swift
+// Sage
+//
+// Copyright 2016-2017 Nikolai Vazquez
+// Modified by SuperGeroy
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+/// A pair of a chess board `File` and `Rank`.
+public typealias Location = (file: File, rank: Rank)
+
+/// A chess board square.
+///
+/// A `Square` can be one of sixty-four possible values, ranging from `A1` to `H8`.
+public enum Square: Int, CustomStringConvertible {
+
+ /// A1 square.
+ case a1
+
+ /// B1 square.
+ case b1
+
+ /// C1 square.
+ case c1
+
+ /// D1 square.
+ case d1
+
+ /// E1 square.
+ case e1
+
+ /// F1 square.
+ case f1
+
+ /// G1 square.
+ case g1
+
+ /// H1 square.
+ case h1
+
+ /// A2 square.
+ case a2
+
+ /// B2 square.
+ case b2
+
+ /// C2 square.
+ case c2
+
+ /// D2 square.
+ case d2
+
+ /// E2 square.
+ case e2
+
+ /// F2 square.
+ case f2
+
+ /// G2 square.
+ case g2
+
+ /// H2 square.
+ case h2
+
+ /// A3 square.
+ case a3
+
+ /// B3 square.
+ case b3
+
+ /// C3 square.
+ case c3
+
+ /// D3 square.
+ case d3
+
+ /// E3 square.
+ case e3
+
+ /// F3 square.
+ case f3
+
+ /// G3 square.
+ case g3
+
+ /// H3 square.
+ case h3
+
+ /// A4 square.
+ case a4
+
+ /// B4 square.
+ case b4
+
+ /// C4 square.
+ case c4
+
+ /// D4 square.
+ case d4
+
+ /// E4 square.
+ case e4
+
+ /// F4 square.
+ case f4
+
+ /// G4 square.
+ case g4
+
+ /// H4 square.
+ case h4
+
+ /// A5 square.
+ case a5
+
+ /// B5 square.
+ case b5
+
+ /// C5 square.
+ case c5
+
+ /// D5 square.
+ case d5
+
+ /// E5 square.
+ case e5
+
+ /// F5 square.
+ case f5
+
+ /// G5 square.
+ case g5
+
+ /// H5 square.
+ case h5
+
+ /// A6 square.
+ case a6
+
+ /// B6 square.
+ case b6
+
+ /// C6 square.
+ case c6
+
+ /// D6 square.
+ case d6
+
+ /// E6 square.
+ case e6
+
+ /// F6 square.
+ case f6
+
+ /// G6 square.
+ case g6
+
+ /// H6 square.
+ case h6
+
+ /// A7 square.
+ case a7
+
+ /// B7 square.
+ case b7
+
+ /// C7 square.
+ case c7
+
+ /// D7 square.
+ case d7
+
+ /// E7 square.
+ case e7
+
+ /// F7 square.
+ case f7
+
+ /// G7 square.
+ case g7
+
+ /// H7 square.
+ case h7
+
+ /// A8 square.
+ case a8
+
+ /// B8 square.
+ case b8
+
+ /// C8 square.
+ case c8
+
+ /// D8 square.
+ case d8
+
+ /// E8 square.
+ case e8
+
+ /// F8 square.
+ case f8
+
+ /// G8 square.
+ case g8
+
+ /// H8 square.
+ case h8
+
+}
+
+extension Square {
+
+ /// An array of all squares.
+ public static let all: [Square] = (0 ..< 64).compactMap(Square.init(rawValue:))
+
+ /// The file of `self`.
+ public var file: File {
+ get {
+ return File(index: rawValue & 7)!
+ }
+ set(newFile) {
+ self = Square(file: newFile, rank: rank)
+ }
+ }
+
+ /// The rank of `self`.
+ public var rank: Rank {
+ get {
+ return Rank(index: rawValue >> 3)!
+ }
+ set(newRank) {
+ self = Square(file: file, rank: newRank)
+ }
+ }
+
+ /// The location of `self`.
+ public var location: Location {
+ get {
+ return (file, rank)
+ }
+ set(newLocation) {
+ self = Square(location: newLocation)
+ }
+ }
+
+ /// The square's color.
+ public var color: Color {
+ return (file.index & 1 != rank.index & 1) ? ._white : ._black
+ }
+
+ /// A textual representation of `self`.
+ public var description: String {
+ return "\(file)\(rank)"
+ }
+
+ /// Create a square from `file` and `rank`.
+ public init(file: File, rank: Rank) {
+ self.init(rawValue: file.index + (rank.index << 3))!
+ }
+
+ /// Create a square from `location`.
+ public init(location: Location) {
+ self.init(file: location.file, rank: location.rank)
+ }
+
+ /// Create a square from `file` and `rank`. Returns `nil` if either is `nil`.
+ public init?(file: File?, rank: Rank?) {
+ guard let file = file, let rank = rank else {
+ return nil
+ }
+ self.init(file: file, rank: rank)
+ }
+
+ /// Create a square from `string`.
+ public init?(_ string: String) {
+ guard string.count == 2 else {
+ return nil
+ }
+ guard let file = File(string.first!) else {
+ return nil
+ }
+ guard let rank = Int(String(string.last!)).flatMap(Rank.init(_:)) else {
+ return nil
+ }
+ self.init(file: file, rank: rank)
+ }
+
+ /// Returns the squares between `self` and `other`.
+ public func between(_ other: Square) -> Bitboard {
+ return _betweenTable[_triangleIndex(self, other)]
+ }
+
+ /// Returns the squares along the line with `other`.
+ public func line(with other: Square) -> Bitboard {
+ return _lineTable[_triangleIndex(self, other)]
+ }
+
+ /// Returns `true` if `self` is between `start` and `end`.
+ public func isBetween(start: Square, end: Square) -> Bool {
+ return start.between(end)[self]
+ }
+
+ /// Returns `true` if `self` is aligned with `first` and `second`.
+ public func isAligned(with first: Square, and second: Square) -> Bool {
+ return line(with: first)[second]
+ || line(with: second)[first]
+ || (self == first && self == second)
+ }
+
+ /// Returns `true` if `self` is aligned with `first` and `rest`.
+ public func isAligned(with first: Square, _ rest: Square...) -> Bool {
+ var line = self == first ? Bitboard(square: self) : self.line(with: first)
+ for square in rest where square != self {
+ if line == Bitboard(square: self) {
+ line = self.line(with: square)
+ }
+ guard line[square] else {
+ return false
+ }
+ }
+ return !line.isEmpty
+ }
+
+ /// Returns `true` if `self` is aligned with `squares`.
+ public func isAligned<S: Sequence>(with squares: S) -> Bool where S.Iterator.Element == Square {
+ var line: Bitboard? = nil
+ let bitboard = Bitboard(square: self)
+ for square in squares {
+ if let lineBitboard = line {
+ if lineBitboard == bitboard {
+ line = self.line(with: square)
+ } else {
+ guard lineBitboard[square] else {
+ return false
+ }
+ }
+ } else if square == self {
+ line = bitboard
+ } else {
+ line = self.line(with: square)
+ }
+ }
+ return line?.isEmpty == false
+ }
+
+ /// Returns a bitboard mask of attacks for a king at `self`.
+ public func kingAttacks() -> Bitboard {
+ return _kingAttackTable[rawValue]
+ }
+
+ /// Returns a bitboard mask of attacks for a knight at `self`.
+ public func knightAttacks() -> Bitboard {
+ return _knightAttackTable[rawValue]
+ }
+
+ /// Returns a bitboard mask of attacks for a piece at `self`.
+ ///
+ /// - parameter piece: The piece for the attacks.
+ /// - parameter stoppers: The pieces stopping a sliding move. The returned bitboard includes the stopped space.
+ ///
+ /// - seealso: `attackMoves(for:stoppers:)`
+ public func attacks(for piece: Piece, stoppers: Bitboard = 0) -> Bitboard {
+ switch piece.kind {
+ case .king:
+ return kingAttacks()
+ case .knight:
+ return knightAttacks()
+ case .pawn:
+ return _pawnAttackTable(for: piece.color)[rawValue]
+ default:
+ return Bitboard(square: self)._attacks(for: piece, stoppers: stoppers)
+ }
+ }
+
+ /// Returns an array of attack moves for a piece at `self`.
+ ///
+ /// - seealso: `attacks(for:stoppers:)`
+ public func attackMoves(for piece: Piece, stoppers: Bitboard = 0) -> [Move] {
+ return attacks(for: piece, stoppers: stoppers).moves(from: self)
+ }
+
+ /// Returns moves from the squares in `squares` to `self`.
+ public func moves<S: Sequence>(from squares: S) -> [Move] where S.Iterator.Element == Square {
+ return squares.moves(to: self)
+ }
+
+ /// Returns moves from `self` to the squares in `squares`.
+ public func moves<S: Sequence>(to squares: S) -> [Move] where S.Iterator.Element == Square {
+ return squares.moves(from: self)
+ }
+
+}
+
+extension Square: ExpressibleByStringLiteral { }
+
+extension Square {
+
+ /// Create an instance initialized to `value`.
+ public init(stringLiteral value: String) {
+ guard let square = Square(value) else {
+ fatalError("Invalid string for square: \"\(value)\"")
+ }
+ self = square
+ }
+
+ /// Create an instance initialized to `value`.
+ public init(unicodeScalarLiteral value: String) {
+ self.init(stringLiteral: value)
+ }
+
+ /// Create an instance initialized to `value`.
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self.init(stringLiteral: value)
+ }
+
+}