Test + bug fixes
This commit is contained in:
@@ -1,79 +1,3 @@
|
|||||||
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 = ""
|
|
||||||
private 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
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum FenError: Error {
|
|
||||||
case InvalidCharacter(c: String, column: UInt8)
|
|
||||||
case NumberTooBig(n: UInt8, column: UInt8)
|
|
||||||
case NumberTooSmall(n: UInt8, column: UInt8)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Board: CustomStringConvertible {
|
public class Board: CustomStringConvertible {
|
||||||
public typealias Grid = [[Square]]
|
public typealias Grid = [[Square]]
|
||||||
|
|
||||||
@@ -112,6 +36,9 @@ public class Board: CustomStringConvertible {
|
|||||||
for c in fen.placement {
|
for c in fen.placement {
|
||||||
let r = (8 - Int(rank)) % 8
|
let r = (8 - Int(rank)) % 8
|
||||||
if c == "/" {
|
if c == "/" {
|
||||||
|
if file != 9 {
|
||||||
|
throw Fen.FenError.NotAppropriateLength(n: file, column: index)
|
||||||
|
}
|
||||||
rank -= 1
|
rank -= 1
|
||||||
file = 0
|
file = 0
|
||||||
} else if c.isWholeNumber, let n = UInt8(String(c)) {
|
} else if c.isWholeNumber, let n = UInt8(String(c)) {
|
||||||
@@ -175,9 +102,13 @@ public class Board: CustomStringConvertible {
|
|||||||
rank -= 1
|
rank -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: Handle better
|
||||||
do {
|
do {
|
||||||
try setBoard()
|
try setBoard()
|
||||||
|
} catch Fen.FenError.NotAppropriateLength(let n, let column) {
|
||||||
|
fatalError("Not appropriate length: \(n) on \(column)")
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,27 +140,3 @@ public class Board: CustomStringConvertible {
|
|||||||
return squares[i]
|
return squares[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now useless, learn how to setup properly
|
|
||||||
// extension Board.Grid {
|
|
||||||
// public subscript(rank: UInt8, file: UInt8) -> Square? {
|
|
||||||
// get {
|
|
||||||
// guard 1 > rank && rank < 9 && 1 > file && file < 9 else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return self[(8 - Int(rank)) % 8][Int(file) - 1]
|
|
||||||
// }
|
|
||||||
// set {
|
|
||||||
// guard 1 > rank && rank < 9 && 1 > file && file < 9 else {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// guard let n = newValue else {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// self[(8 - Int(rank)) % 8][Int(file) - 1] = n
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
76
Sources/Engine/FEN.swift
Normal file
76
Sources/Engine/FEN.swift
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
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 = ""
|
||||||
|
private 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FenError: Error {
|
||||||
|
case InvalidCharacter(c: String, column: UInt8)
|
||||||
|
case NumberTooBig(n: UInt8, column: UInt8)
|
||||||
|
case NumberTooSmall(n: UInt8, column: UInt8)
|
||||||
|
case NotAppropriateLength(n: UInt8, column: UInt8)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,12 @@ public struct Square: Equatable {
|
|||||||
public let rank: UInt8
|
public let rank: UInt8
|
||||||
|
|
||||||
public var index: Int {
|
public var index: Int {
|
||||||
return Int(file * rank) - 1
|
let r = (8 - rank) % 8
|
||||||
|
return Int(8*r+file-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: Position, rhs: Position) -> Bool {
|
public static func == (lhs: Position, rhs: Position) -> Bool {
|
||||||
return lhs.rank == rhs.rank && lhs.file == rhs.file
|
return lhs.index == rhs.index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Engine
|
import Engine
|
||||||
|
|
||||||
let board = Board()
|
let board = Board(fen: .init(fen: "rnbqkb1r/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"))
|
||||||
|
// let board: Board = .init()
|
||||||
print(board)
|
print(board)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ final class EngineTests: XCTestCase {
|
|||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
}
|
}
|
||||||
|
func testPositionIndex() throws {
|
||||||
|
var rank = 9
|
||||||
|
for i in 0..<64 {
|
||||||
|
let file = (i % 8) + 1
|
||||||
|
if file - 1 == 0 {
|
||||||
|
rank -= 1
|
||||||
|
}
|
||||||
|
let pos: Square.Position = .init(file: UInt8(file), rank: UInt8(rank))
|
||||||
|
XCTAssertTrue(pos.index == i, "Expected \(i) got \(pos.index)")
|
||||||
|
}
|
||||||
|
}
|
||||||
// func testBoard() throws {
|
// func testBoard() throws {
|
||||||
// let board = Board()
|
// let board = Board()
|
||||||
// }
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user