114 lines
2.7 KiB
Swift
114 lines
2.7 KiB
Swift
public struct Fen: CustomStringConvertible {
|
|
public enum CastlingAvailibility: String {
|
|
case Neither = "-"
|
|
case WhiteKingSide = "K"
|
|
case WhiteQueenSide = "Q"
|
|
case BlackKingSide = "k"
|
|
case BlackQueenSide = "q"
|
|
case WhiteSide = "KQ"
|
|
case BlackSide = "kq"
|
|
case Kings = "Kk"
|
|
case Queens = "Qq"
|
|
case WKingBQueen = "Kq"
|
|
case WQueenBKing = "Qk"
|
|
case All = "KQkq"
|
|
}
|
|
private var _fen: String = ""
|
|
public private(set) var value: String {
|
|
get {
|
|
return _fen
|
|
}
|
|
set {
|
|
_fen = newValue
|
|
let splitted = _fen.split(separator: " ")
|
|
guard splitted.count == 6 else {
|
|
return
|
|
}
|
|
placement = String(splitted[0])
|
|
activeColor =
|
|
switch splitted[1] {
|
|
case "w": .White
|
|
case "b": .Black
|
|
default: .White
|
|
}
|
|
castlingAvailibility =
|
|
CastlingAvailibility(
|
|
rawValue:
|
|
String(splitted[2])) ?? .Neither
|
|
enPassant = String(splitted[3])
|
|
halfMoveClock =
|
|
if let c = splitted[4].first, c.isWholeNumber {
|
|
UInt8(String(c)) ?? 0
|
|
} else {
|
|
0
|
|
}
|
|
fullMoveClock =
|
|
if let c = splitted[5].first, c.isWholeNumber {
|
|
UInt8(String(c)) ?? 1
|
|
} else {
|
|
1
|
|
}
|
|
}
|
|
}
|
|
|
|
public private(set) var placement: String = "" // 70 chars
|
|
public private(set) var activeColor: Color = .White // 1 char
|
|
public private(set) var castlingAvailibility: CastlingAvailibility = .All
|
|
// 1 to 4 chars
|
|
public private(set) var enPassant: String = "-" // 1 or 2 chars
|
|
public private(set) var halfMoveClock: UInt8 = 0
|
|
public package(set) var fullMoveClock: UInt8 = 1
|
|
|
|
public init(fen value: String) {
|
|
self.value = value
|
|
}
|
|
|
|
internal mutating func set(
|
|
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"
|
|
)
|
|
var placement = ""
|
|
var rankNr = 0
|
|
for rank in board {
|
|
var count = 0
|
|
for i in 0..<rank.count {
|
|
let file = rank[i]
|
|
if let piece = file.piece {
|
|
if count > 0 {
|
|
placement += String(count)
|
|
count = 0
|
|
}
|
|
placement += piece.kind.fenRepresentation(with: piece.color)
|
|
} else {
|
|
count += 1
|
|
}
|
|
}
|
|
if count > 0 {
|
|
placement += String(count)
|
|
}
|
|
if rankNr != rank.count - 1 {
|
|
placement += "/"
|
|
}
|
|
rankNr += 1
|
|
}
|
|
|
|
value =
|
|
"\(placement) \(activeColor) \(ca.rawValue) \(enPassant) \(halfMoveClock) \(fullMoveClock)"
|
|
}
|
|
|
|
public enum FenError: Error {
|
|
case InvalidCharacter(c: String, column: Int8)
|
|
case NumberTooBig(n: Int8, column: Int8)
|
|
case NumberTooSmall(n: Int8, column: Int8)
|
|
case NotAppropriateLength(n: Int8, column: Int8)
|
|
}
|
|
|
|
public var description: String {
|
|
return value
|
|
}
|
|
}
|