aboutsummaryrefslogtreecommitdiff
path: root/Sources/SwiftChessNeo/Piece.swift
blob: d97efe118c5c5d475f200fd50425349903774ca9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//
//  Piece.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 chess piece.
public struct Piece: Hashable, CustomStringConvertible {

    /// A piece kind.
    public enum Kind: Int {

        /// Pawn piece kind.
        case pawn

        /// Knight piece kind.
        case knight

        /// Bishop piece kind.
        case bishop

        /// Rook piece kind.
        case rook

        /// Queen piece kind.
        case queen

        /// King piece kind.
        case king

        /// Pawn
        internal static let _pawn = Kind.pawn

        /// Knight
        internal static let _knight = Kind.knight

        /// Bishop
        internal static let _bishop = Kind.bishop

        /// Rook
        internal static let _rook = Kind.rook

        /// Queen
        internal static let _queen = Kind.queen

        /// King
        internal static let _king = Kind.king

        /// An array of all piece kinds.
        public static let all: [Kind] = [.pawn, .knight, .bishop, .rook, .queen, .king]

        /// The piece kind's name.
        public var name: String {
            switch self {
                case .pawn:   return "Pawn"
                case .knight: return "Knight"
                case .bishop: return "Bishop"
                case .rook:   return "Rook"
                case .queen:  return "Queen"
                case .king:   return "King"
            }
        }

        /// The piece kind's relative value. Can be used to determine how valuable a piece or combination of pieces is.
        public var relativeValue: Double {
            switch self {
                case .pawn:   return 1
                case .knight: return 3
                case .bishop: return 3.25
                case .rook:   return 5
                case .queen:  return 9
                case .king:   return .infinity
            }
        }

        /// The piece is `Pawn`.
        public var isPawn: Bool {
            return self == ._pawn
        }

        /// The piece `Knight`.
        public var isKnight: Bool {
            return self == ._knight
        }

        /// The piece is `Bishop`.
        public var isBishop: Bool {
            return self == ._bishop
        }

        /// The piece is `Rook`.
        public var isRook: Bool {
            return self == ._rook
        }

        /// The piece is `Queen`.
        public var isQueen: Bool {
            return self == ._queen
        }

        /// The piece is `King`.
        public var isKing: Bool {
            return self == ._king
        }

        /// Returns `true` if `self` can be a promotion for a pawn.
        public func canPromote() -> Bool {
            return !(isPawn || isKing)
        }

        /// Returns `true` if `self` can be a promotion for `other`.
        public func canPromote(_ other: Kind) -> Bool {
            return canPromote() ? other.isPawn : false
        }

    }

    internal static let _whiteHashes: [Int] = whitePieces.map({ $0.rawValue })

    internal static let _blackHashes: [Int] = blackPieces.map({ $0.rawValue })

    internal static func _hashes(for color: Color) -> [Int] {
        return color.isWhite ? _whiteHashes : _blackHashes
    }

    internal static let _whiteNonQueens: [Piece] = whitePieces.filter({ !$0.kind.isQueen })

    internal static let _blackNonQueens: [Piece] = blackPieces.filter({ !$0.kind.isQueen })

    internal static func _nonQueens(for color: Color) -> [Piece] {
        return color.isWhite ? _whiteNonQueens : _blackNonQueens
    }

    /// An array of all pieces.
    public static let all: [Piece] = {
        return [.white, .black].reduce([]) { pieces, color in
            return pieces + Kind.all.map({ Piece(kind: $0, color: color) })
        }
    }()

    /// An array of all white pieces.
    public static let whitePieces: [Piece] = all.filter({ $0.color.isWhite })

    /// An array of all black pieces.
    public static let blackPieces: [Piece] = all.filter({ $0.color.isBlack })

    /// Returns an array of all pieces for `color`.
    public static func pieces(for color: Color) -> [Piece] {
        return color.isWhite ? whitePieces : blackPieces
    }

    /// The piece's kind.
    public var kind: Kind

    /// The piece's color.
    public var color: Color

    /// The character for the piece. Uppercase if white or lowercase if black.
    public var character: Character {
        switch kind {
            case .pawn:   return color.isWhite ? "P" : "p"
            case .knight: return color.isWhite ? "N" : "n"
            case .bishop: return color.isWhite ? "B" : "b"
            case .rook:   return color.isWhite ? "R" : "r"
            case .queen:  return color.isWhite ? "Q" : "q"
            case .king:   return color.isWhite ? "K" : "k"
        }
    }

    /// A textual representation of `self`.
    public var description: String {
        return "\(kind.name)(\(color))"
    }

    /// The piece's raw value.
    public var rawValue: Int {
        return (kind.rawValue << 1) | color.numericValue
    }

    /// Create a piece from an integer value.
    internal init?(value: Int) {
        guard let kind = Kind(rawValue: value >> 1) else {
            return nil
        }
        self.init(kind: kind, color: value & 1 == 0 ? .white : .black)
    }

    /// Create a piece from `kind` and `color`.
    public init(kind: Kind, color: Color) {
        self.kind = kind
        self.color = color
    }

    /// Create a pawn piece with `color`.
    public init(pawn color: Color) {
        self.init(kind: ._pawn, color: color)
    }

    /// Create a knight piece with `color`.
    public init(knight color: Color) {
        self.init(kind: ._knight, color: color)
    }

    /// Create a bishop piece with `color`.
    public init(bishop color: Color) {
        self.init(kind: ._bishop, color: color)
    }

    /// Create a rook piece with `color`.
    public init(rook color: Color) {
        self.init(kind: ._rook, color: color)
    }

    /// Create a queen piece with `color`.
    public init(queen color: Color) {
        self.init(kind: ._queen, color: color)
    }

    /// Create a king piece with `color`.
    public init(king color: Color) {
        self.init(kind: ._king, color: color)
    }

    /// Create a piece from a character.
    public init?(character: Character) {
        switch character {
            case "P": self.init(pawn: .white)
            case "p": self.init(pawn: .black)
            case "N": self.init(knight: .white)
            case "n": self.init(knight: .black)
            case "B": self.init(bishop: .white)
            case "b": self.init(bishop: .black)
            case "R": self.init(rook: .white)
            case "r": self.init(rook: .black)
            case "Q": self.init(queen: .white)
            case "q": self.init(queen: .black)
            case "K": self.init(king: .white)
            case "k": self.init(king: .black)
            default:
                return nil
        }
    }

    /// Returns `true` if `self` can be a promotion for `other`.
    public func canPromote(_ other: Piece) -> Bool {
        return kind.canPromote(other.kind) && color == other.color
    }

    /// Returns `true` if `self` can be a promotion for `color`.
    public func canPromote(_ color: Color) -> Bool {
        return kind.canPromote() ? self.color == color : false
    }

    /// The special character for the piece.
    public func specialCharacter(background color: Color = .white) -> Character {
        switch kind {
            case .pawn:   return color == self.color ? "♙" : "♟"
            case .knight: return color == self.color ? "♘" : "♞"
            case .bishop: return color == self.color ? "♗" : "♝"
            case .rook:   return color == self.color ? "♖" : "♜"
            case .queen:  return color == self.color ? "♕" : "♛"
            case .king:   return color == self.color ? "♔" : "♚"
        }
    }
    
    public func unicodeCharacter() -> Character {
        switch kind {
        case .pawn:   return "♟"
        case .knight: return "♞"
        case .bishop: return "♝"
        case .rook:   return "♜"
        case .queen:  return "♛"
        case .king:   return "♚"
        }
    }
    
    public func getNaturalName() -> String {
        switch kind {
        case .pawn:   return self.color.isWhite ? "White Pawn" : "Black Pawn"
        case .bishop: return self.color.isWhite ? "White Bishop" : "Black Bishop"
        case .king: return self.color.isWhite ? "White King" : "Black King"
        case .queen: return self.color.isWhite ? "White Queen" : "Black Queen"
        case .rook: return self.color.isWhite ? "White Rook" : "Black Rook"
        case .knight: return self.color.isWhite ? "White Knight" : "Black Knight"
        }
    }

}

/// Returns `true` if both pieces are the same.
public func == (lhs: Piece, rhs: Piece) -> Bool {
    return lhs.kind == rhs.kind
        && lhs.color == rhs.color
}