Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29cb7535a4 | |||
| 97b10dfa7b | |||
| b3ab7cfbf0 | |||
| 82f68a6875 | |||
| c0cc240716 | |||
| 8c64655149 | |||
| ee073d5edf | |||
| 8592eb4928 | |||
| 1594973694 | |||
| 8c1ad9b6e5 | |||
| babc1cf062 | |||
| dde1d3f054 | |||
| e93f6758fc | |||
| aaec289f33 | |||
| 8fd84bea84 | |||
| fa2e34d026 | |||
| 8fa6ca0002 | |||
| b200acaffb | |||
| 150e763d87 | |||
| 5503a770b6 |
@@ -13,6 +13,7 @@
|
||||
6719898D1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6719898C1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift */; };
|
||||
671989911DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671989901DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift */; };
|
||||
676EF7C51E15AC1700E275B4 /* BoardRaterKingSurroundingPossession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676EF7C41E15AC1700E275B4 /* BoardRaterKingSurroundingPossession.swift */; };
|
||||
67A3EB161E3A926C00F6F01B /* BoardScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A3EB121E3A826800F6F01B /* BoardScenarios.swift */; };
|
||||
67A9CA341DE64DD600510FB8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
|
||||
67A9CA351DE64DD800510FB8 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* MenuViewController.swift */; };
|
||||
67A9CA361DE64DDA00510FB8 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67AD33A61D7C67BF002730DF /* GameViewController.swift */; };
|
||||
@@ -24,7 +25,6 @@
|
||||
67A9CA3D1DE64E7100510FB8 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607FACEA1AFB9204008FA782 /* Info.plist */; };
|
||||
67B73A9B1E15351900C19176 /* PromotionSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B73A9A1E15351900C19176 /* PromotionSelectionViewController.swift */; };
|
||||
67B73A9F1E154C1E00C19176 /* AIPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B73A9E1E154C1E00C19176 /* AIPlayerTests.swift */; };
|
||||
67D54A5C1DE7682900C12258 /* ASCIIBoard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A501DE7680E00C12258 /* ASCIIBoard.swift */; };
|
||||
67D54A5D1DE7682D00C12258 /* BoardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A511DE7680E00C12258 /* BoardTests.swift */; };
|
||||
67D54A5E1DE7683000C12258 /* GameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A521DE7680E00C12258 /* GameTests.swift */; };
|
||||
67D54A5F1DE7683300C12258 /* PieceMovementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A531DE7680E00C12258 /* PieceMovementTests.swift */; };
|
||||
@@ -37,6 +37,8 @@
|
||||
67F779261E1C32A400885B89 /* BoardRaterCenterFourOccupationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F779251E1C32A400885B89 /* BoardRaterCenterFourOccupationTests.swift */; };
|
||||
67F9DB6E1E1AC8DC00C7EC5A /* BoardRaterCheckMateOpportunityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F9DB6D1E1AC8DC00C7EC5A /* BoardRaterCheckMateOpportunityTests.swift */; };
|
||||
67F9DB751E1AD3BB00C7EC5A /* AIBehaviourTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F9DB741E1AD3BB00C7EC5A /* AIBehaviourTests.swift */; };
|
||||
67FD868D1E41099B0023335C /* BoardLocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FD868C1E41099B0023335C /* BoardLocationTests.swift */; };
|
||||
67FD86911E4128F00023335C /* OpeningsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FD86901E4128F00023335C /* OpeningsTests.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -77,6 +79,7 @@
|
||||
6719898C1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterBoardDominanceTests.swift; sourceTree = "<group>"; };
|
||||
671989901DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCenterDominanceTests.swift; sourceTree = "<group>"; };
|
||||
676EF7C41E15AC1700E275B4 /* BoardRaterKingSurroundingPossession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterKingSurroundingPossession.swift; sourceTree = "<group>"; };
|
||||
67A3EB121E3A826800F6F01B /* BoardScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardScenarios.swift; sourceTree = "<group>"; };
|
||||
67A9CA071DE64D6500510FB8 /* SwiftChess.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftChess.xcodeproj; path = ../SwiftChess/SwiftChess.xcodeproj; sourceTree = "<group>"; };
|
||||
67A9CA141DE64DAA00510FB8 /* SwiftChessExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftChessExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
67A9CA271DE64DAA00510FB8 /* SwiftChessExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftChessExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -84,7 +87,6 @@
|
||||
67AD33A81D7C67DF002730DF /* BoardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardView.swift; sourceTree = "<group>"; };
|
||||
67B73A9A1E15351900C19176 /* PromotionSelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PromotionSelectionViewController.swift; sourceTree = "<group>"; };
|
||||
67B73A9E1E154C1E00C19176 /* AIPlayerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AIPlayerTests.swift; sourceTree = "<group>"; };
|
||||
67D54A501DE7680E00C12258 /* ASCIIBoard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASCIIBoard.swift; sourceTree = "<group>"; };
|
||||
67D54A511DE7680E00C12258 /* BoardTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardTests.swift; sourceTree = "<group>"; };
|
||||
67D54A521DE7680E00C12258 /* GameTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTests.swift; sourceTree = "<group>"; };
|
||||
67D54A531DE7680E00C12258 /* PieceMovementTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieceMovementTests.swift; sourceTree = "<group>"; };
|
||||
@@ -97,6 +99,8 @@
|
||||
67F779251E1C32A400885B89 /* BoardRaterCenterFourOccupationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCenterFourOccupationTests.swift; sourceTree = "<group>"; };
|
||||
67F9DB6D1E1AC8DC00C7EC5A /* BoardRaterCheckMateOpportunityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCheckMateOpportunityTests.swift; sourceTree = "<group>"; };
|
||||
67F9DB741E1AD3BB00C7EC5A /* AIBehaviourTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AIBehaviourTests.swift; sourceTree = "<group>"; };
|
||||
67FD868C1E41099B0023335C /* BoardLocationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardLocationTests.swift; sourceTree = "<group>"; };
|
||||
67FD86901E4128F00023335C /* OpeningsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpeningsTests.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -173,6 +177,7 @@
|
||||
children = (
|
||||
607FACE91AFB9204008FA782 /* Supporting Files */,
|
||||
67D54A651DE986F700C12258 /* PieceTests.swift */,
|
||||
67FD868C1E41099B0023335C /* BoardLocationTests.swift */,
|
||||
67D54A511DE7680E00C12258 /* BoardTests.swift */,
|
||||
67D54A521DE7680E00C12258 /* GameTests.swift */,
|
||||
67D54A531DE7680E00C12258 /* PieceMovementTests.swift */,
|
||||
@@ -180,6 +185,7 @@
|
||||
67B73A9E1E154C1E00C19176 /* AIPlayerTests.swift */,
|
||||
67F7791F1E1B923B00885B89 /* AIConfigurationTests.swift */,
|
||||
67F9DB741E1AD3BB00C7EC5A /* AIBehaviourTests.swift */,
|
||||
67FD86901E4128F00023335C /* OpeningsTests.swift */,
|
||||
67D54A621DE9768200C12258 /* BoardRaters */,
|
||||
67D54A551DE7680E00C12258 /* Tests.swift */,
|
||||
);
|
||||
@@ -189,8 +195,8 @@
|
||||
607FACE91AFB9204008FA782 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67D54A501DE7680E00C12258 /* ASCIIBoard.swift */,
|
||||
607FACEA1AFB9204008FA782 /* Info.plist */,
|
||||
67A3EB121E3A826800F6F01B /* BoardScenarios.swift */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
@@ -372,8 +378,10 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
67D54A601DE7683800C12258 /* PlayerTests.swift in Sources */,
|
||||
67FD86911E4128F00023335C /* OpeningsTests.swift in Sources */,
|
||||
67D54A611DE7683A00C12258 /* Tests.swift in Sources */,
|
||||
676EF7C51E15AC1700E275B4 /* BoardRaterKingSurroundingPossession.swift in Sources */,
|
||||
67FD868D1E41099B0023335C /* BoardLocationTests.swift in Sources */,
|
||||
67D54A5D1DE7682D00C12258 /* BoardTests.swift in Sources */,
|
||||
67F9DB751E1AD3BB00C7EC5A /* AIBehaviourTests.swift in Sources */,
|
||||
671989911DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift in Sources */,
|
||||
@@ -382,9 +390,9 @@
|
||||
67D54A661DE986F700C12258 /* PieceTests.swift in Sources */,
|
||||
671989891DFFE0410053EA3D /* BoardRaterCenterOwnershipTests.swift in Sources */,
|
||||
67B73A9F1E154C1E00C19176 /* AIPlayerTests.swift in Sources */,
|
||||
67D54A5C1DE7682900C12258 /* ASCIIBoard.swift in Sources */,
|
||||
67F779201E1B923B00885B89 /* AIConfigurationTests.swift in Sources */,
|
||||
67D54A641DE976A900C12258 /* BoardRaterCountPiecesTests.swift in Sources */,
|
||||
67A3EB161E3A926C00F6F01B /* BoardScenarios.swift in Sources */,
|
||||
09AE32551E03D71D00A149FE /* BoardRaterPawnProgressionTests.swift in Sources */,
|
||||
09A4C0291E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift in Sources */,
|
||||
67D54A5F1DE7683300C12258 /* PieceMovementTests.swift in Sources */,
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
// Copyright © 2017 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
AI behaviour tests are tests that try to avoid or encourage certain behaviours in the AI.
|
||||
These are complex outcomes, so may require changing several variables to manipulate outcomes.
|
||||
*/
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftChess
|
||||
|
||||
@@ -21,7 +27,148 @@ class AIBehaviourTests: XCTestCase {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
func makeGameWithBoard(board: Board, colorToMove: Color) -> Game {
|
||||
|
||||
let whitePlayer = AIPlayer(color: .white)
|
||||
let blackPlayer = AIPlayer(color: .black)
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
|
||||
|
||||
return game
|
||||
}
|
||||
|
||||
func findMovedPieceLocation(startBoard: Board, endBoard: Board, color: Color) -> BoardLocation {
|
||||
|
||||
for location in BoardLocation.all {
|
||||
|
||||
let startBoardPiece = startBoard.getPiece(at: location)
|
||||
let endBoardPiece = endBoard.getPiece(at: location)
|
||||
|
||||
// If there is no end board piece, this is not the location
|
||||
if endBoardPiece == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the end piece exists, but start doesn't, it's the moved piece
|
||||
if endBoardPiece != nil && startBoardPiece == nil {
|
||||
return location
|
||||
}
|
||||
|
||||
// If both pieces exist, and are not the same, then it's the location
|
||||
if endBoardPiece != nil && startBoardPiece != nil {
|
||||
|
||||
if endBoardPiece!.color != startBoardPiece!.color
|
||||
&& endBoardPiece!.type != startBoardPiece!.type{
|
||||
return location
|
||||
}
|
||||
}
|
||||
|
||||
// Else continue - this isn't the location
|
||||
continue
|
||||
}
|
||||
|
||||
fatalError("Failed to find moved location")
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Scenario Tests
|
||||
|
||||
func test_ScenarioOne_BlackShouldNotGiveAwayBishop() {
|
||||
|
||||
|
||||
// In the following example, the black player can move the bishop at (5,7) to the * location (2,4).
|
||||
// The can look like a good move because the bishop will then threaten the white queen at (3,3).
|
||||
// The white queen will also be threatening the black bishop, but because this is a lower value piece black can think that it is in a more preferrable position.
|
||||
// In reality, because the black bishop is unprotected, the white queen will take it.
|
||||
|
||||
let board = ASCIIBoard(pieces: "r - b - q b - r" +
|
||||
"p p g - - p p p" +
|
||||
"- - - - - k - -" +
|
||||
"- - * - - - - -" +
|
||||
"- - p Q P - - -" +
|
||||
"- - - - - - - -" +
|
||||
"P P G - - P P P" +
|
||||
"R K B - - B K R" )
|
||||
|
||||
|
||||
let location = board .locationOfCharacter("*")
|
||||
|
||||
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
XCTFail("Expected an AI Player")
|
||||
return
|
||||
}
|
||||
|
||||
player.makeMove()
|
||||
|
||||
// If there is not piece at the location, then test has passed
|
||||
guard let piece = game.board.getPiece(at: location) else {
|
||||
return
|
||||
}
|
||||
|
||||
// If the piece is not black, or is not a bishop, test passed
|
||||
if piece.type != .bishop || piece.color != .black {
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, black has moved the bishop to the *, test failed
|
||||
XCTFail("Black moved bishop")
|
||||
}
|
||||
|
||||
func test_ScenarioTwo_BlackShouldTradeKnight() {
|
||||
|
||||
// In this example, the black knight at (2, 1) can either take the white rook at (0,0), or trade itself for the white queen at (0,2)
|
||||
// Both of these are good moves
|
||||
|
||||
let board = ASCIIBoard(pieces: "- r - - q b k r" +
|
||||
"p - - g - - p p" +
|
||||
"- - - - b p - -" +
|
||||
"- - - - p - - -" +
|
||||
"- - - p P - - -" +
|
||||
"Q - - - - - - -" +
|
||||
"P P k P K P P P" +
|
||||
"R K B G - - - R" )
|
||||
|
||||
let queenLocation = BoardLocation(x: 0, y: 2)
|
||||
let rookLocation = BoardLocation(x: 0, y: 0)
|
||||
|
||||
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
XCTFail("Expected an AI Player")
|
||||
return
|
||||
}
|
||||
|
||||
player.makeMove()
|
||||
|
||||
if let queenLocationPiece = game.board.getPiece(at: queenLocation) {
|
||||
|
||||
if queenLocationPiece.color == .black && queenLocationPiece.type == .knight {
|
||||
print("PASSED - Black took queen")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if let rookLocationPiece = game.board.getPiece(at: rookLocation) {
|
||||
|
||||
if rookLocationPiece.color == .black && rookLocationPiece.type == .knight {
|
||||
print("PASSED - black took rook")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let location = findMovedPieceLocation(startBoard: board.board, endBoard: game.board, color: .black)
|
||||
|
||||
guard let piece = game.board.getPiece(at: location) else {
|
||||
XCTFail("Couldn't find moved piece")
|
||||
return
|
||||
}
|
||||
|
||||
XCTFail("FAILED - Black moved \(piece.type) to (\(location.x),\(location.y))")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -32,4 +32,161 @@ class AIPlayerTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Test Cannot move in the check
|
||||
|
||||
func makeTestGame(board: Board, colorToMove: Color) -> Game {
|
||||
|
||||
let whitePlayer = AIPlayer(color: .white)
|
||||
let blackPlayer = AIPlayer(color: .black)
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
|
||||
return game
|
||||
}
|
||||
|
||||
func testKnightCannotPutOwnKingInToCheck() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "r - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - * - - - - -" +
|
||||
"K - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - - - - - -" )
|
||||
|
||||
let knightLocation = board.locationOfCharacter("K")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: knightLocation, toLocation: testLocation))
|
||||
}
|
||||
|
||||
func testKingCannotMoveInToCheck() {
|
||||
|
||||
// This is a complex scenario because it was one that I observed from an actual game
|
||||
|
||||
let board = ASCIIBoard(pieces: "r k - - q b - r" +
|
||||
"p p p g k - - p" +
|
||||
"- - * p b p - -" +
|
||||
"P P - - p - p P" +
|
||||
"- - P - P - - -" +
|
||||
"- B - - Q P - -" +
|
||||
"- B - P K G P -" +
|
||||
"R K - - - - - R" )
|
||||
|
||||
let kingLocation = board.locationOfCharacter("g")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .black)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: kingLocation, toLocation: testLocation))
|
||||
}
|
||||
|
||||
func testPawnCannotPutOwnKingInToCheck() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - * - - - -" +
|
||||
"G - - P - - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let pawnLocation = board.locationOfCharacter("P")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: pawnLocation, toLocation: testLocation))
|
||||
|
||||
}
|
||||
|
||||
func testQueenCannotPutOwnKingInToCheck() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - * - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - Q - - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = board.locationOfCharacter("Q")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: queenLocation, toLocation: testLocation))
|
||||
}
|
||||
|
||||
func testRookCannotPutOwnKingInToCheck() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - * - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - R - - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let rookLocation = board.locationOfCharacter("R")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: rookLocation, toLocation: testLocation))
|
||||
}
|
||||
|
||||
func testBishopCannotPutOwnKingInToCheck() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - *" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - B - - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let bishopLocation = board.locationOfCharacter("B")
|
||||
let testLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = makeTestGame(board: board.board, colorToMove: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertFalse(player.canAIMovePiece(fromLocation: bishopLocation, toLocation: testLocation))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// BoardLocationTests.swift
|
||||
// Example
|
||||
//
|
||||
// Created by Steve Barnegren on 31/01/2017.
|
||||
// Copyright © 2017 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftChess
|
||||
|
||||
class BoardLocationTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testBoardLocationFromGridPositionResultsInCorrectIndex() {
|
||||
|
||||
let testCases = [
|
||||
// row 1
|
||||
(BoardLocation.GridPosition.a1, 0),
|
||||
(BoardLocation.GridPosition.b1, 1),
|
||||
(BoardLocation.GridPosition.c1, 2),
|
||||
(BoardLocation.GridPosition.d1, 3),
|
||||
(BoardLocation.GridPosition.e1, 4),
|
||||
(BoardLocation.GridPosition.f1, 5),
|
||||
(BoardLocation.GridPosition.g1, 6),
|
||||
(BoardLocation.GridPosition.h1, 7),
|
||||
// row 2
|
||||
(BoardLocation.GridPosition.a2, 8),
|
||||
(BoardLocation.GridPosition.b2, 9),
|
||||
(BoardLocation.GridPosition.c2, 10),
|
||||
(BoardLocation.GridPosition.d2, 11),
|
||||
(BoardLocation.GridPosition.e2, 12),
|
||||
(BoardLocation.GridPosition.f2, 13),
|
||||
(BoardLocation.GridPosition.g2, 14),
|
||||
(BoardLocation.GridPosition.h2, 15),
|
||||
// row 3
|
||||
(BoardLocation.GridPosition.a3, 16),
|
||||
(BoardLocation.GridPosition.b3, 17),
|
||||
(BoardLocation.GridPosition.c3, 18),
|
||||
(BoardLocation.GridPosition.d3, 19),
|
||||
(BoardLocation.GridPosition.e3, 20),
|
||||
(BoardLocation.GridPosition.f3, 21),
|
||||
(BoardLocation.GridPosition.g3, 22),
|
||||
(BoardLocation.GridPosition.h3, 23),
|
||||
// row 4
|
||||
(BoardLocation.GridPosition.a4, 24),
|
||||
(BoardLocation.GridPosition.b4, 25),
|
||||
(BoardLocation.GridPosition.c4, 26),
|
||||
(BoardLocation.GridPosition.d4, 27),
|
||||
(BoardLocation.GridPosition.e4, 28),
|
||||
(BoardLocation.GridPosition.f4, 29),
|
||||
(BoardLocation.GridPosition.g4, 30),
|
||||
(BoardLocation.GridPosition.h4, 31),
|
||||
// row 5
|
||||
(BoardLocation.GridPosition.a5, 32),
|
||||
(BoardLocation.GridPosition.b5, 33),
|
||||
(BoardLocation.GridPosition.c5, 34),
|
||||
(BoardLocation.GridPosition.d5, 35),
|
||||
(BoardLocation.GridPosition.e5, 36),
|
||||
(BoardLocation.GridPosition.f5, 37),
|
||||
(BoardLocation.GridPosition.g5, 38),
|
||||
(BoardLocation.GridPosition.h5, 39),
|
||||
// row 6
|
||||
(BoardLocation.GridPosition.a6, 40),
|
||||
(BoardLocation.GridPosition.b6, 41),
|
||||
(BoardLocation.GridPosition.c6, 42),
|
||||
(BoardLocation.GridPosition.d6, 43),
|
||||
(BoardLocation.GridPosition.e6, 44),
|
||||
(BoardLocation.GridPosition.f6, 45),
|
||||
(BoardLocation.GridPosition.g6, 46),
|
||||
(BoardLocation.GridPosition.h6, 47),
|
||||
// row 7
|
||||
(BoardLocation.GridPosition.a7, 48),
|
||||
(BoardLocation.GridPosition.b7, 49),
|
||||
(BoardLocation.GridPosition.c7, 50),
|
||||
(BoardLocation.GridPosition.d7, 51),
|
||||
(BoardLocation.GridPosition.e7, 52),
|
||||
(BoardLocation.GridPosition.f7, 53),
|
||||
(BoardLocation.GridPosition.g7, 54),
|
||||
(BoardLocation.GridPosition.h7, 55),
|
||||
// row 8
|
||||
(BoardLocation.GridPosition.a8, 56),
|
||||
(BoardLocation.GridPosition.b8, 57),
|
||||
(BoardLocation.GridPosition.c8, 58),
|
||||
(BoardLocation.GridPosition.d8, 59),
|
||||
(BoardLocation.GridPosition.e8, 60),
|
||||
(BoardLocation.GridPosition.f8, 61),
|
||||
(BoardLocation.GridPosition.g8, 62),
|
||||
(BoardLocation.GridPosition.h8, 63),
|
||||
]
|
||||
|
||||
|
||||
for (grid, index) in testCases {
|
||||
XCTAssertEqual(BoardLocation(gridPosition: grid).index, index)
|
||||
XCTAssertEqual(BoardLocation(index: index).gridPosition, grid)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testMoveLocationsForColorReturnsCorrectLocations() {
|
||||
|
||||
class FakeOpening : Opening {
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.e2, .e4), // white moves pawn to e4
|
||||
(.e7, .e5), // black moves pawn to e5
|
||||
(.g1, .f3), // white moves knight to f3
|
||||
(.b8, .c6), // black moves knight to c6
|
||||
(.f1, .b5), // white moves bishop to b5
|
||||
]
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
let expectedWhiteLocations: [(fromLocation: BoardLocation, toLocation: BoardLocation)] = [
|
||||
(BoardLocation(gridPosition: .e2), BoardLocation(gridPosition: .e4)),
|
||||
(BoardLocation(gridPosition: .g1), BoardLocation(gridPosition: .f3)),
|
||||
(BoardLocation(gridPosition: .f1), BoardLocation(gridPosition: .b5)),
|
||||
]
|
||||
|
||||
let expectedBlackLocations: [(fromLocation: BoardLocation, toLocation: BoardLocation)] = [
|
||||
(BoardLocation(gridPosition: .e7), BoardLocation(gridPosition: .e5)),
|
||||
(BoardLocation(gridPosition: .b8), BoardLocation(gridPosition: .c6)),
|
||||
]
|
||||
|
||||
let opening = FakeOpening()
|
||||
|
||||
let whiteMoves = opening.moves(forColor: .white)
|
||||
XCTAssertEqual(whiteMoves.count, expectedWhiteLocations.count)
|
||||
|
||||
let blackMoves = opening.moves(forColor: .black)
|
||||
XCTAssertEqual(blackMoves.count, expectedBlackLocations.count)
|
||||
|
||||
for i in 0..<whiteMoves.count {
|
||||
let expected = expectedWhiteLocations[i]
|
||||
let actual = whiteMoves[i]
|
||||
|
||||
XCTAssertEqual(expected.fromLocation, actual.fromLocation)
|
||||
XCTAssertEqual(expected.toLocation, actual.toLocation)
|
||||
}
|
||||
|
||||
for i in 0..<blackMoves.count {
|
||||
let expected = expectedBlackLocations[i]
|
||||
let actual = blackMoves[i]
|
||||
|
||||
XCTAssertEqual(expected.fromLocation, actual.fromLocation)
|
||||
XCTAssertEqual(expected.toLocation, actual.toLocation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// BoardScenarios.swift
|
||||
// Example
|
||||
//
|
||||
// Created by Steve Barnegren on 26/01/2017.
|
||||
// Copyright © 2017 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftChess
|
||||
|
||||
extension Board {
|
||||
|
||||
public static func whiteInStaleMateScenario() -> Board {
|
||||
|
||||
return ASCIIBoard(pieces: "- - r - r - - g" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"q - - - - - - -" +
|
||||
"- - - G - - - -" ).board
|
||||
}
|
||||
|
||||
public static func blackInStaleMateScenario() -> Board {
|
||||
|
||||
return ASCIIBoard(pieces: "- - R - R - - G" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"Q - - - - - - -" +
|
||||
"- - - g - - - -" ).board
|
||||
}
|
||||
|
||||
public static func whiteInCheckMateScenario() -> Board {
|
||||
|
||||
return ASCIIBoard(pieces: "- p g - - - - K" +
|
||||
"- - - - - P - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"r - - - - - - -" +
|
||||
"r - - G - - - -" ).board
|
||||
}
|
||||
|
||||
public static func blackInCheckMateScenario() -> Board {
|
||||
|
||||
return ASCIIBoard(pieces: "- - - g - - - R" +
|
||||
"- - - - - - - R" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- G - - - - - -" ).board
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -29,10 +29,9 @@ class BoardTests: XCTestCase {
|
||||
|
||||
let board = Board(state: .empty)
|
||||
|
||||
for index in 0..<64 {
|
||||
|
||||
let piece = board.getPiece(at: BoardLocation(index: index))
|
||||
XCTAssert(piece == nil, "Expected piece at index \(index) to be nil")
|
||||
(0..<64).forEach {
|
||||
let piece = board.getPiece(at: BoardLocation(index: $0))
|
||||
XCTAssert(piece == nil, "Expected piece at index \($0) to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +95,29 @@ class BoardTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Board Location
|
||||
|
||||
func testBoardLocationStrideToReturnsExpectedStride() {
|
||||
|
||||
let fromLocation = BoardLocation(x: 1, y: 1)
|
||||
let toLocation = BoardLocation(x: 3, y: 4)
|
||||
|
||||
let stride = fromLocation.strideTo(location: toLocation)
|
||||
XCTAssertEqual(stride.x, 2)
|
||||
XCTAssertEqual(stride.y, 3)
|
||||
}
|
||||
|
||||
func testBoardLocationStrideFromReturnsExpectedStride() {
|
||||
|
||||
let fromLocation = BoardLocation(x: 1, y: 1)
|
||||
let toLocation = BoardLocation(x: 3, y: 4)
|
||||
|
||||
let stride = toLocation.strideFrom(location: fromLocation)
|
||||
XCTAssertEqual(stride.x, 2)
|
||||
XCTAssertEqual(stride.y, 3)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Piece Manipulation
|
||||
|
||||
func testSetAndGetPiece() {
|
||||
@@ -145,8 +167,8 @@ class BoardTests: XCTestCase {
|
||||
let blackPieces = board.getPieces(color: .black)
|
||||
let allPieces = whitePieces + blackPieces
|
||||
|
||||
for piece in allPieces {
|
||||
XCTAssertFalse(piece.hasMoved)
|
||||
allPieces.forEach{
|
||||
XCTAssertFalse($0.hasMoved)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,12 +180,12 @@ class BoardTests: XCTestCase {
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"* - - - - - - -" +
|
||||
"W - - - - - - -" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let fromLocation = BoardLocation(index: board.indexOfCharacter("W") )
|
||||
let toLocation = BoardLocation(x: 4, y: 4)
|
||||
let fromLocation = board.locationOfCharacter("W")
|
||||
let toLocation = board.locationOfCharacter("*")
|
||||
|
||||
var gameBoard = board.board
|
||||
|
||||
@@ -250,44 +272,27 @@ class BoardTests: XCTestCase {
|
||||
|
||||
var board = Board(state: .empty)
|
||||
|
||||
for location in whiteLocations {
|
||||
board.setPiece(Piece(type: .pawn, color: .white), at: location)
|
||||
whiteLocations.forEach{
|
||||
board.setPiece(Piece(type: .pawn, color: .white), at: $0)
|
||||
}
|
||||
|
||||
for location in blackLocations {
|
||||
board.setPiece(Piece(type: .pawn, color: .black), at: location)
|
||||
blackLocations.forEach{
|
||||
board.setPiece(Piece(type: .pawn, color: .black), at: $0)
|
||||
}
|
||||
|
||||
let returnedWhitelocations = board.getLocationsOfColor(.white)
|
||||
XCTAssert(returnedWhitelocations.count == whiteLocations.count,
|
||||
"Expected white count to be \(whiteLocations.count), was \(returnedWhitelocations.count)")
|
||||
XCTAssertEqual(whiteLocations.count, returnedWhitelocations.count);
|
||||
|
||||
let returnedBlacklocations = board.getLocationsOfColor(.black)
|
||||
XCTAssert(returnedBlacklocations.count == blackLocations.count,
|
||||
"Expected white count to be \(blackLocations.count), was \(returnedBlacklocations.count)")
|
||||
XCTAssertEqual(blackLocations.count, returnedBlacklocations.count);
|
||||
|
||||
|
||||
for location in whiteLocations {
|
||||
|
||||
XCTAssert(
|
||||
returnedWhitelocations.filter({ (returnedLocation: BoardLocation) -> Bool in
|
||||
return location == returnedLocation
|
||||
}).count == 1,
|
||||
"Expected only one location to exist"
|
||||
)
|
||||
whiteLocations.forEach { (location) in
|
||||
XCTAssertEqual(returnedWhitelocations.filter{ location == $0 }.count, 1)
|
||||
}
|
||||
|
||||
|
||||
for location in blackLocations {
|
||||
|
||||
XCTAssert(
|
||||
returnedBlacklocations.filter({ (returnedLocation: BoardLocation) -> Bool in
|
||||
return location == returnedLocation
|
||||
}).count == 1,
|
||||
"Expected only one location to exist"
|
||||
)
|
||||
blackLocations.forEach { (location) in
|
||||
XCTAssertEqual(returnedBlacklocations.filter{ location == $0 }.count, 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testIsColorInCheckReturnsTrueWhenInCheck() {
|
||||
@@ -980,7 +985,32 @@ class BoardTests: XCTestCase {
|
||||
XCTAssertFalse(board.doesColorOccupyLocation(color: .white, location: location))
|
||||
XCTAssertFalse(board.doesColorOccupyLocation(color: .black, location: location))
|
||||
}
|
||||
|
||||
// MARK: Test Possible Move Locations
|
||||
|
||||
func testPossibleMoveLocationsReturnsExpectedLocations(){
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - * - * - - -" +
|
||||
"- * - - - * - -" +
|
||||
"- - - K - - - -" +
|
||||
"- * - - - * - -" +
|
||||
"- - * - * - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" );
|
||||
|
||||
let board = asciiBoard.board;
|
||||
|
||||
let sourceLocation = asciiBoard.locationOfCharacter("K")
|
||||
let locations = board.possibleMoveLocationsForPiece(atLocation: sourceLocation)
|
||||
XCTAssertEqual(locations.count, 8)
|
||||
|
||||
let expectedLocations = asciiBoard.locationsWithCharacter("*")
|
||||
|
||||
expectedLocations.forEach{ expectedLocation in
|
||||
XCTAssertEqual(locations.filter( { $0 == expectedLocation} ).count, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,20 +7,22 @@
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SwiftChess
|
||||
@testable import SwiftChess
|
||||
|
||||
class GameTests: XCTestCase {
|
||||
|
||||
var whitePlayer: Human!
|
||||
var blackPlayer: Human!
|
||||
var game: Game!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
let firstPlayer = Human(color: .white)
|
||||
let secondPlayer = Human(color: .black)
|
||||
whitePlayer = Human(color: .white)
|
||||
blackPlayer = Human(color: .black)
|
||||
|
||||
game = Game(firstPlayer: firstPlayer, secondPlayer: secondPlayer)
|
||||
game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@@ -28,20 +30,85 @@ class GameTests: XCTestCase {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
/*
|
||||
func testCurrentPlayerChangesAfterMoveTaken() {
|
||||
|
||||
let firstPlayer = game.currentPlayer
|
||||
|
||||
// White player move leftmost pawn
|
||||
try! game.currentPlayer.movePiece(fromLocation: BoardLocation(x: 0, y: 1),
|
||||
toLocation: BoardLocation(x: 0, y: 2))
|
||||
|
||||
XCTAssert(game.currentPlayer !== firstPlayer)
|
||||
|
||||
}
|
||||
*/
|
||||
// MARK: Game State Tests
|
||||
|
||||
func testGameStateEquatableReturnsCorrectEquability() {
|
||||
|
||||
// Match in progress
|
||||
XCTAssertTrue(Game.State.inProgress == Game.State.inProgress)
|
||||
|
||||
// Match stale mate
|
||||
XCTAssertTrue(Game.State.staleMate(color: .white) == Game.State.staleMate(color: .white))
|
||||
XCTAssertTrue(Game.State.staleMate(color: .black) == Game.State.staleMate(color: .black))
|
||||
XCTAssertFalse(Game.State.staleMate(color: .white) == Game.State.staleMate(color: .black))
|
||||
XCTAssertFalse(Game.State.staleMate(color: .black) == Game.State.staleMate(color: .white))
|
||||
|
||||
// Match won
|
||||
XCTAssertTrue(Game.State.won(color: .white) == Game.State.won(color: .white))
|
||||
XCTAssertTrue(Game.State.won(color: .black) == Game.State.won(color: .black))
|
||||
XCTAssertFalse(Game.State.won(color: .white) == Game.State.won(color: .black))
|
||||
XCTAssertFalse(Game.State.won(color: .black) == Game.State.won(color: .white))
|
||||
|
||||
// Mismatches
|
||||
XCTAssertFalse(Game.State.inProgress == Game.State.won(color: .black))
|
||||
XCTAssertFalse(Game.State.inProgress == Game.State.staleMate(color: .white))
|
||||
XCTAssertFalse(Game.State.staleMate(color: .white) == Game.State.won(color: .white))
|
||||
|
||||
}
|
||||
|
||||
func testGameStartsInProgress() {
|
||||
XCTAssertTrue(game.state == Game.State.inProgress)
|
||||
}
|
||||
|
||||
func testAfterMovesGameIsStillInProgressState() {
|
||||
|
||||
// White player moves pawn
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: BoardLocation(x: 0, y: 1),
|
||||
toLocation: BoardLocation(x: 0, y: 2))
|
||||
} catch {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
// Black player moves pawn
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: BoardLocation(x: 7, y: 6),
|
||||
toLocation: BoardLocation(x: 7, y: 5))
|
||||
} catch {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(game.state == Game.State.inProgress)
|
||||
}
|
||||
|
||||
func testStaleMateScenarioResultsInStaleMateState() {
|
||||
|
||||
game = Game(firstPlayer: whitePlayer,
|
||||
secondPlayer: blackPlayer,
|
||||
board: Board.whiteInStaleMateScenario(),
|
||||
colorToMove: .black)
|
||||
|
||||
game.playerDidMakeMove(player: blackPlayer, boardOperations: [])
|
||||
|
||||
XCTAssertTrue(game.state == Game.State.staleMate(color: .white))
|
||||
}
|
||||
|
||||
func testCheckMateScenarioResultsInWonState() {
|
||||
|
||||
game = Game(firstPlayer: whitePlayer,
|
||||
secondPlayer: blackPlayer,
|
||||
board: Board.whiteInCheckMateScenario(),
|
||||
colorToMove: .black)
|
||||
|
||||
game.playerDidMakeMove(player: blackPlayer, boardOperations: [])
|
||||
|
||||
XCTAssertTrue(game.state == Game.State.won(color: .black))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// OpeningsTests.swift
|
||||
// Example
|
||||
//
|
||||
// Created by Steve Barnegren on 31/01/2017.
|
||||
// Copyright © 2017 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftChess
|
||||
|
||||
class OpeningsTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testOpeningsContainValidMoves() {
|
||||
|
||||
let openings = Opening.allOpenings()
|
||||
|
||||
for opening in openings {
|
||||
|
||||
print("Testing \(opening)")
|
||||
|
||||
var board = Board(state: .newGame)
|
||||
for (source, target) in opening.moveLocations(){
|
||||
|
||||
// Assert that the piece exists
|
||||
guard let piece = board.getPiece(at: source) else {
|
||||
XCTFail("No piece at \(source)")
|
||||
break
|
||||
}
|
||||
|
||||
// Assert the piece can move
|
||||
if piece.movement.canPieceMove(fromLocation: source, toLocation: target, board: board) {
|
||||
board.movePiece(fromLocation: source, toLocation: target)
|
||||
}
|
||||
else{
|
||||
XCTFail("Cannot move piece from \(source) to \(target)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -683,7 +683,6 @@ class PieceMovementTests: XCTestCase {
|
||||
let movement = PieceMovementKnight()
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - King Movement
|
||||
|
||||
@@ -811,7 +810,8 @@ class PieceMovementTests: XCTestCase {
|
||||
let movement = PieceMovementKing()
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: - Pawn Movement
|
||||
|
||||
@@ -1147,6 +1147,275 @@ class PieceMovementTests: XCTestCase {
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
// MARK: - Test pawn En Passant
|
||||
|
||||
func makeGame(board: Board, colorToMove: Color) -> Game {
|
||||
|
||||
let whitePlayer = Human(color: .white)
|
||||
let blackPlayer = Human(color: .black)
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
|
||||
return game
|
||||
}
|
||||
|
||||
func testPawnEnPassantFlagIsTrueAfterMoveTwoSpaces() {
|
||||
|
||||
let board = Board(state: .newGame)
|
||||
|
||||
let startLocation = BoardLocation(x: 0, y: 1)
|
||||
let targetLocation = BoardLocation(x: 0, y: 3)
|
||||
|
||||
let game = makeGame(board: board, colorToMove: .white)
|
||||
|
||||
guard let whitePlayer = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: startLocation, toLocation: targetLocation)
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
guard let piece = game.board.getPiece(at: targetLocation) else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertTrue(piece.color == .white)
|
||||
XCTAssertTrue(piece.type == .pawn)
|
||||
XCTAssertTrue(piece.canBeTakenByEnPassant == true)
|
||||
}
|
||||
|
||||
func testPawnEnPassantFlagIsFalseAfterMoveOneSpace() {
|
||||
|
||||
let board = Board(state: .newGame)
|
||||
|
||||
let startLocation = BoardLocation(x: 0, y: 1)
|
||||
let targetLocation = BoardLocation(x: 0, y: 2)
|
||||
|
||||
let game = makeGame(board: board, colorToMove: .white)
|
||||
|
||||
guard let whitePlayer = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: startLocation, toLocation: targetLocation)
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
guard let piece = game.board.getPiece(at: targetLocation) else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertTrue(piece.color == .white)
|
||||
XCTAssertTrue(piece.type == .pawn)
|
||||
XCTAssertTrue(piece.canBeTakenByEnPassant == false)
|
||||
}
|
||||
|
||||
func testPawnEnPassantFlagIsResetAfterSubsequentMove() {
|
||||
|
||||
// White moves pawn
|
||||
let board = Board(state: .newGame)
|
||||
|
||||
let startLocation = BoardLocation(x: 0, y: 1)
|
||||
let targetLocation = BoardLocation(x: 0, y: 2)
|
||||
|
||||
let game = makeGame(board: board, colorToMove: .white)
|
||||
|
||||
guard let whitePlayer = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: startLocation, toLocation: targetLocation)
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Black moves pawn
|
||||
guard let blackPlayer = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
guard blackPlayer.color == .black else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: BoardLocation(x: 0, y: 6), toLocation: BoardLocation(x: 0, y: 5))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Check white pawn flag is false
|
||||
guard let piece = game.board.getPiece(at: targetLocation) else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
XCTAssertTrue(piece.color == .white)
|
||||
XCTAssertTrue(piece.type == .pawn)
|
||||
XCTAssertTrue(piece.canBeTakenByEnPassant == false)
|
||||
}
|
||||
|
||||
func testWhitePawnCanTakeOpponentUsingEnPassant() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"p - - - - - - -" +
|
||||
"+ - - - - - - -" +
|
||||
"* P - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - G" )
|
||||
|
||||
let game = makeGame(board: board.board, colorToMove: .black)
|
||||
let blackPlayer = game.blackPlayer as! Human
|
||||
let whitePlayer = game.whitePlayer as! Human
|
||||
|
||||
// Black move two spaces
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: board.locationOfCharacter("p"), toLocation: board.locationOfCharacter("*"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// White should be able to take the black pawn using the en passant rule
|
||||
let pieceMovement = PieceMovementPawn()
|
||||
XCTAssertTrue(pieceMovement.canPieceMove(fromLocation: board.locationOfCharacter("P"), toLocation: board.locationOfCharacter("+"), board: game.board),
|
||||
"Expected white to be able to make en passant move")
|
||||
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: board.locationOfCharacter("P"), toLocation: board.locationOfCharacter("+"))
|
||||
} catch {
|
||||
XCTFail("Expected white to be able to execute en passant move")
|
||||
}
|
||||
|
||||
XCTAssertTrue(game.board.getPiece(at: board.locationOfCharacter("*")) == nil, "Expected black pawn to be removed from board")
|
||||
|
||||
}
|
||||
|
||||
func testBlackPawnCanTakeOpponentUsingEnPassant() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"* p - - - - - -" +
|
||||
"+ - - - - - - -" +
|
||||
"P - - - - - - -" +
|
||||
"- - - - - - - G" )
|
||||
|
||||
let game = makeGame(board: board.board, colorToMove: .white)
|
||||
let whitePlayer = game.whitePlayer as! Human
|
||||
let blackPlayer = game.blackPlayer as! Human
|
||||
|
||||
// White move two spaces
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: board.locationOfCharacter("P"), toLocation: board.locationOfCharacter("*"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Black should be able to take the white pawn using the en passant rule
|
||||
let pieceMovement = PieceMovementPawn()
|
||||
XCTAssertTrue(pieceMovement.canPieceMove(fromLocation: board.locationOfCharacter("p"), toLocation: board.locationOfCharacter("+"), board: game.board),
|
||||
"Expected black to be able to make en passant move")
|
||||
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: board.locationOfCharacter("p"), toLocation: board.locationOfCharacter("+"))
|
||||
} catch {
|
||||
XCTFail("Expected black to be able to execute en passant move")
|
||||
}
|
||||
|
||||
XCTAssertTrue(game.board.getPiece(at: board.locationOfCharacter("*")) == nil, "Expected white pawn to be removed from board")
|
||||
|
||||
}
|
||||
|
||||
func testWhitePawnCannotTakeOpponentUsingEnPassantIfMoveNotMadeImmediately() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"p - - - - - - %" +
|
||||
"+ - - - - - - -" +
|
||||
"* P - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - &" +
|
||||
"- - - - - - - G" )
|
||||
|
||||
let game = makeGame(board: board.board, colorToMove: .black)
|
||||
let whitePlayer = game.whitePlayer as! Human
|
||||
let blackPlayer = game.blackPlayer as! Human
|
||||
|
||||
// Black move two spaces
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: board.locationOfCharacter("p"), toLocation: board.locationOfCharacter("*"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// White moves king
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: board.locationOfCharacter("G"), toLocation: board.locationOfCharacter("&"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Black moves king
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: board.locationOfCharacter("g"), toLocation: board.locationOfCharacter("%"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// White should not be able to take the black pawn using the en passant rule
|
||||
let pieceMovement = PieceMovementPawn()
|
||||
XCTAssertFalse(pieceMovement.canPieceMove(fromLocation: board.locationOfCharacter("P"), toLocation: board.locationOfCharacter("+"), board: game.board))
|
||||
|
||||
}
|
||||
|
||||
func testBlackPawnCannotTakeOpponentUsingEnPassantIfMoveNotMadeImmediately() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - - - - - %" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"* p - - - - - -" +
|
||||
"+ - - - - - - -" +
|
||||
"P - - - - - - &" +
|
||||
"- - - - - - - G" )
|
||||
|
||||
let game = makeGame(board: board.board, colorToMove: .white)
|
||||
let whitePlayer = game.whitePlayer as! Human
|
||||
let blackPlayer = game.blackPlayer as! Human
|
||||
|
||||
// White moves pawn two spaces
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: board.locationOfCharacter("P"), toLocation: board.locationOfCharacter("*"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Black moves king
|
||||
do {
|
||||
try blackPlayer.movePiece(fromLocation: board.locationOfCharacter("g"), toLocation: board.locationOfCharacter("%"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// White moves king
|
||||
do {
|
||||
try whitePlayer.movePiece(fromLocation: board.locationOfCharacter("G"), toLocation: board.locationOfCharacter("&"))
|
||||
} catch {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Black should not be able to take the white pawn using the en passant rule
|
||||
let pieceMovement = PieceMovementPawn()
|
||||
XCTAssertFalse(pieceMovement.canPieceMove(fromLocation: board.locationOfCharacter("p"), toLocation: board.locationOfCharacter("+"), board: game.board))
|
||||
}
|
||||
|
||||
// MARK: - Queen movement
|
||||
|
||||
func testQueenCannotMoveToInvalidPositionFromCentre(){
|
||||
@@ -1274,6 +1543,8 @@ class PieceMovementTests: XCTestCase {
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Rook Movement
|
||||
|
||||
func testRookCannotMoveToInvalidPositionFromCentre(){
|
||||
@@ -1400,7 +1671,8 @@ class PieceMovementTests: XCTestCase {
|
||||
let movement = PieceMovementRook()
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: - Bishop movement
|
||||
@@ -1529,6 +1801,8 @@ class PieceMovementTests: XCTestCase {
|
||||
let movement = PieceMovementBishop()
|
||||
XCTAssertFalse(movement.canPieceMove(fromLocation: BoardLocation(index: pieceIndex), toLocation: BoardLocation(index: kingIndex), board: board.board))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,160 @@ class PlayerTests: XCTestCase {
|
||||
XCTAssert(game.whitePlayer.canMovePiece(fromLocation: location, toLocation: location) == false)
|
||||
}
|
||||
|
||||
// TODO: Add tests for move error throw values
|
||||
// MARK: - Move Errors
|
||||
|
||||
func gameForTestingCallbacks(board: Board, color: Color) -> Game {
|
||||
|
||||
let whitePlayer = Human(color: .white)
|
||||
let blackPlayer = Human(color: .black)
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: color)
|
||||
|
||||
return game
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingQueen() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - * - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - - Q - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = board.locationOfCharacter("Q")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: queenLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingPawn() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - * - - -" +
|
||||
"G - - - P - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let pieceLocation = board.locationOfCharacter("P")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: pieceLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingKnight() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - * - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - - K - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let pieceLocation = board.locationOfCharacter("K")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: pieceLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingBishop() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - * - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - - B - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let pieceLocation = board.locationOfCharacter("B")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: pieceLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingRook() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - * - - -" +
|
||||
"- - - - - - - -" +
|
||||
"G - - - R - - r" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
let pieceLocation = board.locationOfCharacter("R")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: pieceLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
func testMoveInToCheckErrorIsThrownByMovingKing() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"* - - - - - - r" +
|
||||
"G - - - - - - -" )
|
||||
|
||||
let pieceLocation = board.locationOfCharacter("G")
|
||||
let targetLocation = board.locationOfCharacter("*")
|
||||
|
||||
let game = gameForTestingCallbacks(board: board.board, color: .white)
|
||||
|
||||
guard let player = game.currentPlayer as? Human else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Assert that the correct error is thrown
|
||||
XCTAssertTrue(player.canMovePieceWithError(fromLocation: pieceLocation, toLocation: targetLocation).error == .cannotMoveInToCheck)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
# SwiftChess
|
||||
|
||||
[](https://travis-ci.org/Steve Barnegren/SwiftChess)
|
||||
[](http://cocoapods.org/pods/SwiftChess)
|
||||
[](http://cocoapods.org/pods/SwiftChess)
|
||||
[](http://cocoapods.org/pods/SwiftChess)
|
||||
SwiftChess is a chess engine written in Swift.
|
||||
|
||||
Rather than a library that you can call to assist in making valid chess moves, SwiftChess aims to be a complete chess game, minus the UI.
|
||||
|
||||
SwiftChess also includes a complete AI implementation.
|
||||
|
||||
There's no documentation for now, as the public api is still in flux, but there is a complete example project for iOS.
|
||||
|
||||
SwiftChess also has a reasonably comprehensive set of unit tests.
|
||||
|
||||
## Example
|
||||
|
||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
||||
Run `Example/Example.xcodeproj`
|
||||
|
||||
## Requirements
|
||||
|
||||
## Installation
|
||||
|
||||
SwiftChess is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
|
||||
```ruby
|
||||
pod "SwiftChess"
|
||||
```
|
||||
The example app can run *player vs player*, *player vs AI*, *AI vs AI* matches
|
||||
|
||||
## Author
|
||||
|
||||
Steve Barnegren, steve.barnegren@gmail.com
|
||||
Steve Barnegren
|
||||
|
||||
[Follow me on Twitter](https://twitter.com/stevebarnegren)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+3
-3
@@ -21,16 +21,16 @@ Pod::Spec.new do |s|
|
||||
TODO: Add long description of the pod here.
|
||||
DESC
|
||||
|
||||
s.homepage = 'https://github.com/<GITHUB_USERNAME>/SwiftChess'
|
||||
s.homepage = 'https://github.com/SteveBarnegren/SwiftChess'
|
||||
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'Steve Barnegren' => 'steve.barnegren@gmail.com' }
|
||||
s.source = { :git => 'https://github.com/<GITHUB_USERNAME>/SwiftChess.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'https://github.com/SteveBarnegren/SwiftChess.git', :tag => s.version.to_s }
|
||||
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
|
||||
s.source_files = 'SwiftChess/Classes/**/*'
|
||||
s.source_files = 'SwiftChess/Source/**/*'
|
||||
|
||||
# s.resource_bundles = {
|
||||
# 'SwiftChess' => ['SwiftChess/Assets/*.png']
|
||||
|
||||
@@ -43,16 +43,16 @@ struct AIConfiguration {
|
||||
}
|
||||
|
||||
mutating func setDefualtValues() {
|
||||
boardRaterCountPiecesWeighting = 1
|
||||
boardRaterBoardDominanceWeighting = 1
|
||||
boardRaterCenterOwnershipWeighting = 1
|
||||
boardRaterCenterDominanceWeighting = 1
|
||||
boardRaterThreatenedPiecesWeighting = 1
|
||||
boardRaterThreatenedPiecesOwnPiecesMultiplier = 2
|
||||
boardRaterCountPiecesWeighting = 3 //1
|
||||
boardRaterBoardDominanceWeighting = 0.1
|
||||
boardRaterCenterOwnershipWeighting = 0.3
|
||||
boardRaterCenterDominanceWeighting = 0.3
|
||||
boardRaterCenterFourOccupationWeighting = 0.3
|
||||
boardRaterThreatenedPiecesWeighting = 1 // 1.5
|
||||
boardRaterThreatenedPiecesOwnPiecesMultiplier = 20 // Higher values will be more defensive
|
||||
boardRaterPawnProgressionWeighting = 1
|
||||
boardRaterKingSurroundingPossessionWeighting = 1
|
||||
boardRaterKingSurroundingPossessionWeighting = 0.3
|
||||
boardRaterCheckMateOpportunityWeighting = 2
|
||||
boardRaterCenterFourOccupationWeighting = 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ open class AIPlayer : Player {
|
||||
|
||||
let boardRaters : [BoardRater]!
|
||||
let configuration = AIConfiguration() // <-- We should pass this in eventually
|
||||
var openingMoves = [OpeningMove]()
|
||||
|
||||
public init(color: Color){
|
||||
|
||||
@@ -24,105 +25,44 @@ open class AIPlayer : Player {
|
||||
BoardRaterThreatenedPieces(configuration: configuration),
|
||||
BoardRaterPawnProgression(configuration: configuration),
|
||||
BoardRaterKingSurroundingPossession(configuration: configuration),
|
||||
BoardRaterCheckMateOpportunity(configuration: configuration)
|
||||
BoardRaterCheckMateOpportunity(configuration: configuration),
|
||||
BoardRaterCenterFourOccupation(configuration: configuration)
|
||||
]
|
||||
|
||||
openingMoves = Opening.allOpeningMoves(forColor: color)
|
||||
|
||||
super.init()
|
||||
self.color = color
|
||||
}
|
||||
|
||||
public func makeMove() {
|
||||
|
||||
print("\n\n****** Make Move ******");
|
||||
|
||||
let board = game.board
|
||||
|
||||
// Build list of possible moves with ratings
|
||||
var move: Move!
|
||||
|
||||
var possibleMoves = [Move]()
|
||||
|
||||
for sourceLocation in BoardLocation.all {
|
||||
|
||||
guard let piece = board.getPiece(at: sourceLocation) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if piece.color != color {
|
||||
continue
|
||||
}
|
||||
|
||||
for targetLocation in BoardLocation.all {
|
||||
|
||||
guard canMovePiece(fromLocation: sourceLocation, toLocation: targetLocation) else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Make move
|
||||
var resultBoard = board
|
||||
resultBoard.movePiece(fromLocation: sourceLocation, toLocation: targetLocation)
|
||||
|
||||
// Promote pawns
|
||||
let pawnsToPromoteLocations = resultBoard.getLocationsOfPromotablePawns(color: color)
|
||||
assert(pawnsToPromoteLocations.count < 2, "There should only ever be one pawn to promote at any time")
|
||||
if pawnsToPromoteLocations.count > 0 {
|
||||
resultBoard = promotePawnsOnBoard(resultBoard)
|
||||
}
|
||||
|
||||
// Rate
|
||||
let rating = ratingForBoard(resultBoard)
|
||||
let move = Move(type: .singlePiece(sourceLocation: sourceLocation, targetLocation: targetLocation),
|
||||
rating: rating)
|
||||
possibleMoves.append(move)
|
||||
}
|
||||
// Get an opening move
|
||||
if let openingMove = openingMoveForBoard(board){
|
||||
print("Playing opening move")
|
||||
move = openingMove
|
||||
}
|
||||
|
||||
// Add castling moves
|
||||
let castleSides: [CastleSide] = [.kingSide, .queenSide]
|
||||
for side in castleSides {
|
||||
|
||||
guard game.board.canColorCastle(color: color, side: side) else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Perform the castling move
|
||||
var resultBoard = board
|
||||
resultBoard.performCastle(color: color, side: side)
|
||||
|
||||
// Rate
|
||||
let rating = ratingForBoard(resultBoard)
|
||||
let move = Move(type: .castle(color: color, side: side), rating: rating)
|
||||
possibleMoves.append(move)
|
||||
// Or, get the Highest rated move
|
||||
else{
|
||||
move = highestRatedMoveOnBoard(board)
|
||||
}
|
||||
|
||||
print("Found \(possibleMoves.count) possible moves")
|
||||
|
||||
// If there are no possible moves, we must be in stale mate
|
||||
if possibleMoves.count == 0 {
|
||||
print("There are no possible moves!!!!");
|
||||
}
|
||||
|
||||
// Choose move with highest rating
|
||||
var highestRating = possibleMoves.first!.rating
|
||||
var highestRatedMove = possibleMoves.first!
|
||||
|
||||
for move in possibleMoves {
|
||||
|
||||
if move.rating > highestRating {
|
||||
highestRating = move.rating
|
||||
highestRatedMove = move;
|
||||
}
|
||||
|
||||
print("rating: \(move.rating)")
|
||||
}
|
||||
|
||||
print("HIGHEST MOVE RATING: \(highestRating)")
|
||||
|
||||
// Make move
|
||||
var operations = [BoardOperation]()
|
||||
|
||||
switch highestRatedMove.type {
|
||||
switch move.type {
|
||||
case .singlePiece(let sourceLocation, let targetLocation):
|
||||
operations = game.board.movePiece(fromLocation: sourceLocation, toLocation: targetLocation)
|
||||
print("Chose move (\(sourceLocation.x),\(sourceLocation.y)) -> (\(targetLocation.x),\(targetLocation.y))");
|
||||
case .castle(let color, let side):
|
||||
operations = game.board.performCastle(color: color, side: side)
|
||||
print("Chose Castling move");
|
||||
}
|
||||
|
||||
// Promote pawns
|
||||
@@ -139,12 +79,130 @@ open class AIPlayer : Player {
|
||||
self.game.playerDidMakeMove(player: self, boardOperations: operations)
|
||||
}
|
||||
|
||||
func openingMoveForBoard(_ board: Board) -> Move? {
|
||||
|
||||
let possibleMoves = openingMoves.filter{
|
||||
$0.board == board
|
||||
}
|
||||
|
||||
guard possibleMoves.count > 0 else{
|
||||
return nil
|
||||
}
|
||||
|
||||
let openingMove = possibleMoves[Int(arc4random()) % possibleMoves.count]
|
||||
|
||||
return Move(type: .singlePiece(sourceLocation: openingMove.fromLocation,
|
||||
targetLocation: openingMove.toLocation),
|
||||
rating: 0)
|
||||
}
|
||||
|
||||
func highestRatedMoveOnBoard(_ board: Board) -> Move {
|
||||
|
||||
var possibleMoves = [Move]()
|
||||
|
||||
for sourceLocation in BoardLocation.all {
|
||||
|
||||
guard let piece = board.getPiece(at: sourceLocation) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if piece.color != color {
|
||||
continue
|
||||
}
|
||||
|
||||
for targetLocation in BoardLocation.all {
|
||||
|
||||
guard canAIMovePiece(fromLocation: sourceLocation, toLocation: targetLocation) else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Make move
|
||||
var resultBoard = board
|
||||
resultBoard.movePiece(fromLocation: sourceLocation, toLocation: targetLocation)
|
||||
|
||||
// Promote pawns
|
||||
let pawnsToPromoteLocations = resultBoard.getLocationsOfPromotablePawns(color: color)
|
||||
assert(pawnsToPromoteLocations.count < 2, "There should only ever be one pawn to promote at any time")
|
||||
if pawnsToPromoteLocations.count > 0 {
|
||||
resultBoard = promotePawnsOnBoard(resultBoard)
|
||||
}
|
||||
|
||||
// Rate
|
||||
print("(\(sourceLocation.x),\(sourceLocation.y)) -> (\(targetLocation.x),\(targetLocation.y))")
|
||||
let rating = ratingForBoard(resultBoard)
|
||||
let move = Move(type: .singlePiece(sourceLocation: sourceLocation, targetLocation: targetLocation),
|
||||
rating: rating)
|
||||
possibleMoves.append(move)
|
||||
print("Rating: \(rating)")
|
||||
}
|
||||
}
|
||||
|
||||
// Add castling moves
|
||||
let castleSides: [CastleSide] = [.kingSide, .queenSide]
|
||||
for side in castleSides {
|
||||
|
||||
guard game.board.canColorCastle(color: color, side: side) else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Perform the castling move
|
||||
var resultBoard = board
|
||||
resultBoard.performCastle(color: color, side: side)
|
||||
|
||||
// Rate
|
||||
let rating = ratingForBoard(resultBoard)
|
||||
let move = Move(type: .castle(color: color, side: side), rating: rating)
|
||||
possibleMoves.append(move)
|
||||
}
|
||||
|
||||
print("Found \(possibleMoves.count) possible moves")
|
||||
|
||||
// If there are no possible moves, we must be in stale mate
|
||||
if possibleMoves.count == 0 {
|
||||
print("There are no possible moves!!!!");
|
||||
}
|
||||
|
||||
// Choose move with highest rating
|
||||
var highestRating = possibleMoves.first!.rating
|
||||
var highestRatedMove = possibleMoves.first!
|
||||
|
||||
for move in possibleMoves {
|
||||
|
||||
if move.rating > highestRating {
|
||||
highestRating = move.rating
|
||||
highestRatedMove = move;
|
||||
}
|
||||
|
||||
//print("rating: \(move.rating)")
|
||||
}
|
||||
|
||||
return highestRatedMove;
|
||||
}
|
||||
|
||||
func canAIMovePiece(fromLocation: BoardLocation, toLocation: BoardLocation) -> Bool {
|
||||
|
||||
// This is a stricter version of the canMove function, used by the AI, that returns false for errors
|
||||
|
||||
let canMove = canMovePieceWithError(fromLocation: fromLocation, toLocation: toLocation)
|
||||
if canMove.error != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return canMove.result
|
||||
}
|
||||
|
||||
|
||||
func ratingForBoard(_ board: Board) -> Double {
|
||||
|
||||
var rating: Double = 0;
|
||||
|
||||
for boardRater in boardRaters {
|
||||
rating += boardRater.ratingfor(board: board, color: color)
|
||||
|
||||
let result = boardRater.ratingfor(board: board, color: color)
|
||||
|
||||
let className = "\(boardRater)"
|
||||
print("\t\(className): \(result)")
|
||||
rating += result
|
||||
}
|
||||
|
||||
// If opponent is in check mate, set the maximum rating
|
||||
@@ -200,8 +258,6 @@ open class AIPlayer : Player {
|
||||
|
||||
return promotedBoard
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct Move {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
import SwiftChess
|
||||
|
||||
func transformASCIIBoardInput(_ input: String) -> String{
|
||||
@@ -33,7 +32,104 @@ public struct ASCIIBoard {
|
||||
let artString: String
|
||||
var stringContainsColors: Bool!
|
||||
|
||||
init(pieces artString: String) {
|
||||
public init(
|
||||
// Row 8
|
||||
_ a8: Character,
|
||||
_ b8: Character,
|
||||
_ c8: Character,
|
||||
_ d8: Character,
|
||||
_ e8: Character,
|
||||
_ f8: Character,
|
||||
_ g8: Character,
|
||||
_ h8: Character,
|
||||
// Row 7
|
||||
_ a7: Character,
|
||||
_ b7: Character,
|
||||
_ c7: Character,
|
||||
_ d7: Character,
|
||||
_ e7: Character,
|
||||
_ f7: Character,
|
||||
_ g7: Character,
|
||||
_ h7: Character,
|
||||
// Row 6
|
||||
_ a6: Character,
|
||||
_ b6: Character,
|
||||
_ c6: Character,
|
||||
_ d6: Character,
|
||||
_ e6: Character,
|
||||
_ f6: Character,
|
||||
_ g6: Character,
|
||||
_ h6: Character,
|
||||
|
||||
// Row 5
|
||||
_ a5: Character,
|
||||
_ b5: Character,
|
||||
_ c5: Character,
|
||||
_ d5: Character,
|
||||
_ e5: Character,
|
||||
_ f5: Character,
|
||||
_ g5: Character,
|
||||
_ h5: Character,
|
||||
// Row 4
|
||||
_ a4: Character,
|
||||
_ b4: Character,
|
||||
_ c4: Character,
|
||||
_ d4: Character,
|
||||
_ e4: Character,
|
||||
_ f4: Character,
|
||||
_ g4: Character,
|
||||
_ h4: Character,
|
||||
// Row 3
|
||||
_ a3: Character,
|
||||
_ b3: Character,
|
||||
_ c3: Character,
|
||||
_ d3: Character,
|
||||
_ e3: Character,
|
||||
_ f3: Character,
|
||||
_ g3: Character,
|
||||
_ h3: Character,
|
||||
// Row 2
|
||||
_ a2: Character,
|
||||
_ b2: Character,
|
||||
_ c2: Character,
|
||||
_ d2: Character,
|
||||
_ e2: Character,
|
||||
_ f2: Character,
|
||||
_ g2: Character,
|
||||
_ h2: Character,
|
||||
// Row 1
|
||||
_ a1: Character,
|
||||
_ b1: Character,
|
||||
_ c1: Character,
|
||||
_ d1: Character,
|
||||
_ e1: Character,
|
||||
_ f1: Character,
|
||||
_ g1: Character,
|
||||
_ h1: Character
|
||||
){
|
||||
|
||||
var inputAsString =
|
||||
"\(a8) \(b8) \(c8) \(d8) \(e8) \(f8) \(g8) \(h8)" +
|
||||
"\(a7) \(b7) \(c7) \(d7) \(e7) \(f7) \(g7) \(h7)" +
|
||||
"\(a6) \(b6) \(c6) \(d6) \(e6) \(f6) \(g6) \(h6)" +
|
||||
"\(a5) \(b5) \(c5) \(d5) \(e5) \(f5) \(g5) \(h5)" +
|
||||
"\(a4) \(b4) \(c4) \(d4) \(e4) \(f4) \(g4) \(h4)" +
|
||||
"\(a3) \(b3) \(c3) \(d3) \(e3) \(f3) \(g3) \(h3)" +
|
||||
"\(a2) \(b2) \(c2) \(d2) \(e2) \(f2) \(g2) \(h2)" +
|
||||
"\(a1) \(b1) \(c1) \(d1) \(e1) \(f1) \(g1) \(h1)";
|
||||
|
||||
// Transform
|
||||
inputAsString = transformASCIIBoardInput(inputAsString)
|
||||
|
||||
// Check string format
|
||||
assert(inputAsString.characters.count == 64, "ASCII board art must be 128 characters long")
|
||||
|
||||
self.artString = inputAsString
|
||||
self.stringContainsColors = false
|
||||
|
||||
}
|
||||
|
||||
public init(pieces artString: String) {
|
||||
|
||||
var artString = artString
|
||||
|
||||
@@ -41,20 +137,20 @@ public struct ASCIIBoard {
|
||||
artString = transformASCIIBoardInput(artString)
|
||||
|
||||
// Check string format
|
||||
XCTAssertEqual(artString.characters.count, 64, "ASCII board art must be 128 characters long")
|
||||
assert(artString.characters.count == 64, "ASCII board art must be 128 characters long")
|
||||
|
||||
self.artString = artString
|
||||
self.stringContainsColors = false
|
||||
|
||||
}
|
||||
|
||||
init(colors artString: String) {
|
||||
public init(colors artString: String) {
|
||||
|
||||
self.init(pieces: artString)
|
||||
self.stringContainsColors = true
|
||||
}
|
||||
|
||||
var board: Board {
|
||||
public var board: Board {
|
||||
|
||||
var boardArt = artString
|
||||
|
||||
@@ -67,15 +163,14 @@ public struct ASCIIBoard {
|
||||
var board = Board(state: .empty)
|
||||
|
||||
// Clear all pieces on the board
|
||||
for i in 0..<64 {
|
||||
board.squares[i].piece = nil;
|
||||
(0..<64).forEach{
|
||||
board.squares[$0].piece = nil;
|
||||
}
|
||||
|
||||
// Setup pieces from ascii art
|
||||
for i in 0..<64 {
|
||||
|
||||
let character = boardArt[boardArt.characters.index(boardArt.startIndex, offsetBy: i)]
|
||||
board.squares[i].piece = pieceFromCharacter(character)
|
||||
(0..<64).forEach{
|
||||
let character = boardArt[boardArt.characters.index(boardArt.startIndex, offsetBy: $0)]
|
||||
board.squares[$0].piece = pieceFromCharacter(character)
|
||||
}
|
||||
|
||||
return board
|
||||
@@ -126,7 +221,7 @@ public struct ASCIIBoard {
|
||||
index = artString.characters.distance(from: artString.startIndex, to: idx)
|
||||
}
|
||||
|
||||
XCTAssert(index != nil, "Unable to find index of character: \(character)")
|
||||
assert(index != nil, "Unable to find index of character: \(character)")
|
||||
return index!
|
||||
}
|
||||
|
||||
@@ -140,11 +235,10 @@ public struct ASCIIBoard {
|
||||
|
||||
var indexes = [Int]()
|
||||
|
||||
for i in 0..<64 {
|
||||
|
||||
let aCharacter = artString[artString.characters.index(artString.startIndex, offsetBy: i)]
|
||||
(0..<64).forEach{
|
||||
let aCharacter = artString[artString.characters.index(artString.startIndex, offsetBy: $0)]
|
||||
if character == aCharacter {
|
||||
indexes.append(i)
|
||||
indexes.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,9 +251,8 @@ public struct ASCIIBoard {
|
||||
|
||||
var locations = [BoardLocation]()
|
||||
|
||||
for index in indexes {
|
||||
|
||||
let location = BoardLocation(index: index)
|
||||
indexes.forEach{
|
||||
let location = BoardLocation(index: $0)
|
||||
locations.append(location)
|
||||
}
|
||||
|
||||
+106
-120
@@ -13,117 +13,26 @@ public enum CastleSide {
|
||||
case queenSide
|
||||
}
|
||||
|
||||
// MARK: - ****** BoardStride ******
|
||||
|
||||
public struct BoardStride {
|
||||
|
||||
public var x: Int;
|
||||
public var y: Int;
|
||||
|
||||
public init(x: Int, y: Int){
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - ****** BoardLocation ******
|
||||
|
||||
public struct BoardLocation : Equatable {
|
||||
|
||||
public var index: Int
|
||||
|
||||
public static var all: [BoardLocation] {
|
||||
|
||||
// TODO: using a computed property could be expensive, can we store this so it doesn't need to be computed each time?
|
||||
var locations = [BoardLocation]()
|
||||
|
||||
for i in 0..<64 {
|
||||
locations.append(BoardLocation(index: i))
|
||||
}
|
||||
|
||||
return locations
|
||||
}
|
||||
|
||||
public var x: Int {
|
||||
return index % 8
|
||||
}
|
||||
|
||||
public var y: Int {
|
||||
return index / 8
|
||||
}
|
||||
|
||||
public init(index: Int) {
|
||||
self.index = index
|
||||
}
|
||||
|
||||
public init(x: Int, y: Int) {
|
||||
self.index = x + (y*8)
|
||||
}
|
||||
|
||||
func isInBounds() -> Bool {
|
||||
return (index < 64 && index >= 0)
|
||||
}
|
||||
|
||||
func incremented(by offset: Int) -> BoardLocation {
|
||||
return BoardLocation(index: index + offset)
|
||||
}
|
||||
|
||||
func incrementedBy(x: Int, y: Int) -> BoardLocation {
|
||||
return self + BoardLocation(x: x, y: y)
|
||||
}
|
||||
|
||||
func incrementedBy(stride: BoardStride) -> BoardLocation {
|
||||
|
||||
// TODO: for performance, we should probably only check this if we're in debug mode
|
||||
if !canIncrementBy(stride: stride) {
|
||||
print("WARNING! BoardLocation is being incremented by a stride that will result in wrapping! call canIncrementBy(stride: BoardStride) first")
|
||||
}
|
||||
|
||||
return BoardLocation(x: x + stride.x,
|
||||
y: y + stride.y)
|
||||
}
|
||||
|
||||
func canIncrementBy(stride: BoardStride) -> Bool {
|
||||
|
||||
// Check will not wrap to right
|
||||
if x + stride.x > 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap to left
|
||||
if x + stride.x < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap top
|
||||
if y + stride.y > 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap bottom
|
||||
if y + stride.y < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public func ==(lhs: BoardLocation, rhs: BoardLocation) -> Bool {
|
||||
return lhs.index == rhs.index
|
||||
}
|
||||
|
||||
public func +(left: BoardLocation, right: BoardLocation) -> BoardLocation {
|
||||
return BoardLocation(index: left.index + right.index)
|
||||
}
|
||||
|
||||
// MARK: - ****** Square ******
|
||||
|
||||
public struct Square {
|
||||
public struct Square : Equatable {
|
||||
|
||||
public var piece: Piece?
|
||||
|
||||
}
|
||||
|
||||
public func ==(lhs: Square, rhs: Square) -> Bool {
|
||||
|
||||
switch (lhs.piece, rhs.piece) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case (.some, .none):
|
||||
return true
|
||||
case (.none, .some):
|
||||
return true
|
||||
case (.some(let rp), .some(let lp)):
|
||||
return rp == lp
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ****** Board ******
|
||||
@@ -141,7 +50,7 @@ public struct Board {
|
||||
public init(state: InitialState) {
|
||||
|
||||
// Setup squares
|
||||
for i in 0..<64 {
|
||||
for _ in 0..<64 {
|
||||
squares.append(Square())
|
||||
}
|
||||
|
||||
@@ -204,23 +113,62 @@ public struct Board {
|
||||
|
||||
var operations = [BoardOperation]()
|
||||
|
||||
if let piece = getPiece(at: fromLocation) {
|
||||
let operation = BoardOperation(type: .movePiece, piece: piece, location: toLocation)
|
||||
operations.append(operation)
|
||||
guard var movingPiece = getPiece(at: fromLocation) else {
|
||||
fatalError("There is no piece on at (\(fromLocation.x),\(fromLocation.y))")
|
||||
}
|
||||
|
||||
let operation = BoardOperation(type: .movePiece, piece: movingPiece, location: toLocation)
|
||||
operations.append(operation)
|
||||
|
||||
if let piece = getPiece(at: toLocation) {
|
||||
let operation = BoardOperation(type: .removePiece, piece: piece, location: toLocation)
|
||||
if let targetPiece = getPiece(at: toLocation) {
|
||||
let operation = BoardOperation(type: .removePiece, piece: targetPiece, location: toLocation)
|
||||
operations.append(operation)
|
||||
}
|
||||
|
||||
squares[toLocation.index].piece = self.squares[fromLocation.index].piece
|
||||
squares[toLocation.index].piece?.hasMoved = true
|
||||
squares[fromLocation.index].piece = nil
|
||||
|
||||
|
||||
// If the moving piece is a pawn, check whether it just made an en passent move, and remove the passed piece
|
||||
IF_EN_PASSANT: if movingPiece.type == .pawn {
|
||||
|
||||
let stride = fromLocation.strideTo(location: toLocation)
|
||||
let enPassentStride = BoardStride(x: stride.x, y: 0)
|
||||
let enPassentLocation = fromLocation.incrementedBy(stride: enPassentStride)
|
||||
|
||||
guard let enPassentPiece = getPiece(at: enPassentLocation) else {
|
||||
break IF_EN_PASSANT
|
||||
}
|
||||
|
||||
if enPassentPiece.canBeTakenByEnPassant && enPassentPiece.color == movingPiece.color.opposite() {
|
||||
squares[enPassentLocation.index].piece = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset en passant flags
|
||||
resetEnPassantFlags()
|
||||
|
||||
// If pawn has moved two squares, then need to update the en passant flag
|
||||
if movingPiece.type == .pawn {
|
||||
|
||||
let startingRow = (movingPiece.color == .white ? 1 : 6)
|
||||
let twoAheadRow = (movingPiece.color == .white ? 3 : 4)
|
||||
|
||||
if fromLocation.y == startingRow && toLocation.y == twoAheadRow {
|
||||
squares[toLocation.index].piece?.canBeTakenByEnPassant = true
|
||||
}
|
||||
}
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
mutating func resetEnPassantFlags() {
|
||||
|
||||
for i in 0..<squares.count {
|
||||
squares[i].piece?.canBeTakenByEnPassant = false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Get Specific pieces
|
||||
|
||||
func getKing(color: Color) -> Piece {
|
||||
@@ -322,20 +270,39 @@ public struct Board {
|
||||
|
||||
public func isColorInCheck(color: Color) -> Bool {
|
||||
|
||||
let kingLocation = getKingLocation(color: color)
|
||||
// Get the King location
|
||||
var kingLocation: BoardLocation?
|
||||
for location in BoardLocation.all {
|
||||
|
||||
guard let piece = getPiece(at: location) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if piece.color == color && piece.type == .king {
|
||||
kingLocation = location
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no king, then return false (some tests will be run without a king)
|
||||
if kingLocation == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Work out if we're in check
|
||||
let oppositionLocations = getLocationsOfColor( color.opposite() )
|
||||
|
||||
// Pieces will not move to take the king, so remove it
|
||||
// Pieces will not move to take the king, so change it for a pawn of the same color
|
||||
var noKingBoard = self
|
||||
noKingBoard.squares[kingLocation.index].piece = nil
|
||||
noKingBoard.squares[kingLocation!.index].piece = Piece(type: .pawn, color: color)
|
||||
|
||||
for location in oppositionLocations {
|
||||
|
||||
guard let piece = getPiece(at: location) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if piece.movement.canPieceMove(fromLocation: location, toLocation: kingLocation, board: noKingBoard) {
|
||||
|
||||
if piece.movement.canPieceMove(fromLocation: location, toLocation: kingLocation!, board: noKingBoard) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -444,6 +411,23 @@ public struct Board {
|
||||
return (piece.color == color ? true : false)
|
||||
}
|
||||
|
||||
public func possibleMoveLocationsForPiece(atLocation location: BoardLocation) -> [BoardLocation] {
|
||||
|
||||
guard let piece = squares[location.index].piece else{
|
||||
return []
|
||||
}
|
||||
|
||||
var locations = [BoardLocation]()
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
if piece.movement.canPieceMove(fromLocation: location, toLocation: $0, board: self){
|
||||
locations.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
return locations
|
||||
}
|
||||
|
||||
// MARK: - Castling
|
||||
|
||||
struct CastleMove{
|
||||
@@ -663,6 +647,8 @@ public struct Board {
|
||||
|
||||
print(printString)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public func ==(lhs: Board, rhs: Board) -> Bool {
|
||||
return lhs.squares == rhs.squares
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// BoardLocation.swift
|
||||
// SwiftChess
|
||||
//
|
||||
// Created by Steve Barnegren on 31/01/2017.
|
||||
// Copyright © 2017 Steve Barnegren. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct BoardLocation : Equatable {
|
||||
|
||||
public enum GridPosition : Int{
|
||||
case a1; case b1; case c1; case d1; case e1; case f1; case g1; case h1;
|
||||
case a2; case b2; case c2; case d2; case e2; case f2; case g2; case h2;
|
||||
case a3; case b3; case c3; case d3; case e3; case f3; case g3; case h3;
|
||||
case a4; case b4; case c4; case d4; case e4; case f4; case g4; case h4;
|
||||
case a5; case b5; case c5; case d5; case e5; case f5; case g5; case h5;
|
||||
case a6; case b6; case c6; case d6; case e6; case f6; case g6; case h6;
|
||||
case a7; case b7; case c7; case d7; case e7; case f7; case g7; case h7;
|
||||
case a8; case b8; case c8; case d8; case e8; case f8; case g8; case h8;
|
||||
}
|
||||
|
||||
public var index: Int
|
||||
|
||||
public static var all: [BoardLocation] {
|
||||
|
||||
// TODO: using a computed property could be expensive, can we store this so it doesn't need to be computed each time?
|
||||
var locations = [BoardLocation]()
|
||||
|
||||
(0..<64).forEach{
|
||||
locations.append(BoardLocation(index: $0))
|
||||
}
|
||||
|
||||
return locations
|
||||
}
|
||||
|
||||
public var x: Int {
|
||||
return index % 8
|
||||
}
|
||||
|
||||
public var y: Int {
|
||||
return index / 8
|
||||
}
|
||||
|
||||
public var gridPosition: GridPosition {
|
||||
return GridPosition(rawValue: index)!
|
||||
}
|
||||
|
||||
public init(index: Int) {
|
||||
self.index = index
|
||||
}
|
||||
|
||||
public init(x: Int, y: Int) {
|
||||
self.index = x + (y*8)
|
||||
}
|
||||
|
||||
public init(gridPosition: GridPosition){
|
||||
self.index = gridPosition.rawValue
|
||||
}
|
||||
|
||||
func isInBounds() -> Bool {
|
||||
return (index < 64 && index >= 0)
|
||||
}
|
||||
|
||||
func incremented(by offset: Int) -> BoardLocation {
|
||||
return BoardLocation(index: index + offset)
|
||||
}
|
||||
|
||||
func incrementedBy(x: Int, y: Int) -> BoardLocation {
|
||||
return self + BoardLocation(x: x, y: y)
|
||||
}
|
||||
|
||||
func incrementedBy(stride: BoardStride) -> BoardLocation {
|
||||
|
||||
// TODO: for performance, we should probably only check this if we're in debug mode
|
||||
if !canIncrementBy(stride: stride) {
|
||||
print("WARNING! BoardLocation is being incremented by a stride that will result in wrapping! call canIncrementBy(stride: BoardStride) first")
|
||||
}
|
||||
|
||||
return BoardLocation(x: x + stride.x,
|
||||
y: y + stride.y)
|
||||
}
|
||||
|
||||
func canIncrementBy(stride: BoardStride) -> Bool {
|
||||
|
||||
// Check will not wrap to right
|
||||
if x + stride.x > 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap to left
|
||||
if x + stride.x < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap top
|
||||
if y + stride.y > 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check will not wrap bottom
|
||||
if y + stride.y < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func strideTo(location: BoardLocation) -> BoardStride {
|
||||
|
||||
return BoardStride(x: location.x - x,
|
||||
y: location.y - y)
|
||||
}
|
||||
|
||||
func strideFrom(location: BoardLocation) -> BoardStride {
|
||||
|
||||
return BoardStride(x: x - location.x,
|
||||
y: y - location.y)
|
||||
}
|
||||
}
|
||||
|
||||
public func ==(lhs: BoardLocation, rhs: BoardLocation) -> Bool {
|
||||
return lhs.index == rhs.index
|
||||
}
|
||||
|
||||
public func +(left: BoardLocation, right: BoardLocation) -> BoardLocation {
|
||||
return BoardLocation(index: left.index + right.index)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// BoardStride.swift
|
||||
// SwiftChess
|
||||
//
|
||||
// Created by Steve Barnegren on 31/01/2017.
|
||||
// Copyright © 2017 Steve Barnegren. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct BoardStride {
|
||||
|
||||
public var x: Int;
|
||||
public var y: Int;
|
||||
|
||||
public init(x: Int, y: Int){
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,38 @@ import Foundation
|
||||
|
||||
open class Game {
|
||||
|
||||
// MARK: Types
|
||||
public enum State: Equatable {
|
||||
case inProgress
|
||||
case staleMate(color: Color)
|
||||
case won(color: Color)
|
||||
|
||||
public static func ==(lhs: State, rhs: State) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.inProgress, .inProgress):
|
||||
return true
|
||||
case (let .staleMate(color1), let staleMate(color2)):
|
||||
return color1 == color2
|
||||
case (let .won(color1), let won(color2)):
|
||||
return color1 == color2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
open var board = Board(state: .newGame)
|
||||
|
||||
open var whitePlayer: Player!
|
||||
open var blackPlayer: Player!
|
||||
open var currentPlayer: Player!
|
||||
|
||||
open var state = Game.State.inProgress
|
||||
open weak var delegate: GameDelegate?
|
||||
|
||||
public init(firstPlayer: Player, secondPlayer: Player){
|
||||
|
||||
// MARK: Init
|
||||
public init(firstPlayer: Player, secondPlayer: Player, board: Board = Board(state: .newGame), colorToMove: Color = .white){
|
||||
|
||||
self.board = board
|
||||
|
||||
// Assign to correct colors
|
||||
if firstPlayer.color == secondPlayer.color {
|
||||
@@ -33,10 +56,13 @@ open class Game {
|
||||
self.blackPlayer.delegate = self
|
||||
self.whitePlayer.game = self
|
||||
self.blackPlayer.game = self
|
||||
self.currentPlayer = self.whitePlayer
|
||||
self.currentPlayer = (colorToMove == .white ? self.whitePlayer : self.blackPlayer)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Game : PlayerDelegate
|
||||
extension Game : PlayerDelegate {
|
||||
|
||||
func playerDidMakeMove(player: Player, boardOperations: [BoardOperation]) {
|
||||
@@ -51,12 +77,14 @@ extension Game : PlayerDelegate {
|
||||
|
||||
// Check for game ended
|
||||
if board.isColorInCheckMate(color: currentPlayer.color.opposite()) {
|
||||
state = .won(color: currentPlayer.color)
|
||||
delegate?.gameWonByPlayer(game: self, player: currentPlayer)
|
||||
return
|
||||
}
|
||||
|
||||
// Check for stalemate
|
||||
if board.isColorInStalemate(color: currentPlayer.color.opposite()) {
|
||||
state = .staleMate(color: currentPlayer.color.opposite())
|
||||
delegate?.gameEndedInStaleMate(game: self)
|
||||
return
|
||||
}
|
||||
@@ -91,6 +119,7 @@ extension Game : PlayerDelegate {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - GameDelegate
|
||||
public protocol GameDelegate: class {
|
||||
|
||||
// State changes
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Opening.swift
|
||||
// SwiftChess
|
||||
//
|
||||
// Created by Steve Barnegren on 31/01/2017.
|
||||
// Copyright © 2017 Steve Barnegren. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct OpeningMove {
|
||||
let board: Board
|
||||
let fromLocation: BoardLocation
|
||||
let toLocation: BoardLocation
|
||||
}
|
||||
|
||||
class Opening {
|
||||
|
||||
static func allOpenings() -> [Opening] {
|
||||
return [
|
||||
RuyLopez(),
|
||||
ItalianGame(),
|
||||
SicilianDefense(),
|
||||
QueensGambit(),
|
||||
KingsGambit(),
|
||||
]
|
||||
}
|
||||
|
||||
static func allOpeningMoves(forColor color: Color) -> [OpeningMove] {
|
||||
|
||||
var openingMoves = [OpeningMove]()
|
||||
|
||||
allOpenings().forEach{
|
||||
openingMoves += $0.moves(forColor: color)
|
||||
}
|
||||
|
||||
return openingMoves
|
||||
}
|
||||
|
||||
public func moves(forColor color: Color) -> [OpeningMove] {
|
||||
|
||||
var moves = [OpeningMove]()
|
||||
|
||||
var board = Board(state: .newGame)
|
||||
for locations in moveLocations(){
|
||||
|
||||
let move = OpeningMove(board: board,
|
||||
fromLocation: locations.fromLocation,
|
||||
toLocation: locations.toLocation)
|
||||
moves.append(move)
|
||||
|
||||
board.movePiece(fromLocation: locations.fromLocation,
|
||||
toLocation: locations.toLocation)
|
||||
}
|
||||
|
||||
// Filter for color
|
||||
return moves.enumerated().flatMap{ (index, value) in
|
||||
index % 2 == (color == .white ? 0 : 1) ? value : nil
|
||||
}
|
||||
}
|
||||
/*
|
||||
public func moveLocations(forColor color: Color) -> [(fromLocation: BoardLocation, toLocation: BoardLocation)] {
|
||||
|
||||
return moveLocations().enumerated().flatMap{ (index, value) in
|
||||
index % 2 == (color == .white ? 0 : 1) ? value : nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
func moveLocations() -> [(fromLocation: BoardLocation, toLocation: BoardLocation)] {
|
||||
|
||||
return moveGridPositions().map{
|
||||
(BoardLocation(gridPosition: $0), BoardLocation(gridPosition: $1))
|
||||
}
|
||||
}
|
||||
|
||||
func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
fatalError("Must override")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Ruy Lopez
|
||||
|
||||
class RuyLopez: Opening {
|
||||
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.e2, .e4), // white moves pawn to e4
|
||||
(.e7, .e5), // black moves pawn to e5
|
||||
(.g1, .f3), // white moves knight to f3
|
||||
(.b8, .c6), // black moves knight to c6
|
||||
(.f1, .b5), // white moves bishop to b5
|
||||
]
|
||||
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Italian Game
|
||||
|
||||
class ItalianGame: Opening {
|
||||
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.e2, .e4), // white moves pawn to e4
|
||||
(.e7, .e5), // black moves pawn to e5
|
||||
(.g1, .f3), // white moves knight to f3
|
||||
(.b8, .c6), // black moves knight to c6
|
||||
(.f1, .c4), // white moves bishop to c4
|
||||
]
|
||||
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sicilian Defense
|
||||
|
||||
class SicilianDefense: Opening {
|
||||
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.e2, .e4), // white moves pawn to e4
|
||||
(.c7, .c5), // black moves pawn to c5
|
||||
]
|
||||
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Queens Gambit
|
||||
|
||||
class QueensGambit: Opening {
|
||||
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.d2, .d4), // white moves pawn to d4
|
||||
(.d7, .d5), // black moves pawn to d5
|
||||
(.c2, .c4), // white moves pawn to c4
|
||||
]
|
||||
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - King's Gambit
|
||||
|
||||
class KingsGambit: Opening {
|
||||
|
||||
override func moveGridPositions() -> [(fromPosition: BoardLocation.GridPosition, toPosition: BoardLocation.GridPosition)] {
|
||||
|
||||
let moves: [(BoardLocation.GridPosition, BoardLocation.GridPosition)] = [
|
||||
(.e2, .e4), // white moves pawn to e4
|
||||
(.e7, .e5), // black moves pawn to e5
|
||||
(.f2, .f4), // white moves pawn to f4
|
||||
]
|
||||
|
||||
return moves
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public enum Color {
|
||||
|
||||
}
|
||||
|
||||
public struct Piece {
|
||||
public struct Piece : Equatable {
|
||||
|
||||
static private var lastAssignedTag = 0
|
||||
|
||||
@@ -43,6 +43,7 @@ public struct Piece {
|
||||
public let color: Color
|
||||
public var tag: Int!
|
||||
public var hasMoved = false
|
||||
public var canBeTakenByEnPassant = false
|
||||
|
||||
var movement : PieceMovement {
|
||||
return PieceMovement.pieceMovementForPieceType(pieceType: self.type)
|
||||
|
||||
@@ -355,11 +355,30 @@ open class PieceMovementPawn: PieceMovement {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the target square has an opponent piece
|
||||
if let piece = board.getPiece(at: location) {
|
||||
if piece.color == color.opposite() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// If can make en passent move
|
||||
let enPassentStride = BoardStride(x: stride.x, y: 0)
|
||||
|
||||
guard fromLocation.canIncrementBy(stride: enPassentStride) else {
|
||||
break
|
||||
}
|
||||
|
||||
let enPassentLocation = fromLocation.incrementedBy(stride: enPassentStride)
|
||||
|
||||
guard let passingPiece = board.getPiece(at: enPassentLocation) else {
|
||||
break
|
||||
}
|
||||
|
||||
if passingPiece.canBeTakenByEnPassant && passingPiece.color == color.opposite() {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
67F779241E1C326D00885B89 /* BoardRaterCenterFourOccupation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F779231E1C326D00885B89 /* BoardRaterCenterFourOccupation.swift */; };
|
||||
67F9DB6C1E1AC59600C7EC5A /* BoardRaterCheckMateOpportunity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F9DB6B1E1AC59600C7EC5A /* BoardRaterCheckMateOpportunity.swift */; };
|
||||
67F9DB771E1AD46000C7EC5A /* AIConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F9DB761E1AD46000C7EC5A /* AIConfiguration.swift */; };
|
||||
67F9DB7A1E2438EC00C7EC5A /* ASCIIBoard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F9DB791E2438EC00C7EC5A /* ASCIIBoard.swift */; };
|
||||
67FD86871E41021E0023335C /* Opening.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FD86861E41021E0023335C /* Opening.swift */; };
|
||||
67FD86891E4105B80023335C /* BoardStride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FD86881E4105B80023335C /* BoardStride.swift */; };
|
||||
67FD868B1E4105D40023335C /* BoardLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FD868A1E4105D40023335C /* BoardLocation.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -63,6 +67,10 @@
|
||||
67F779231E1C326D00885B89 /* BoardRaterCenterFourOccupation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCenterFourOccupation.swift; sourceTree = "<group>"; };
|
||||
67F9DB6B1E1AC59600C7EC5A /* BoardRaterCheckMateOpportunity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCheckMateOpportunity.swift; sourceTree = "<group>"; };
|
||||
67F9DB761E1AD46000C7EC5A /* AIConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AIConfiguration.swift; sourceTree = "<group>"; };
|
||||
67F9DB791E2438EC00C7EC5A /* ASCIIBoard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASCIIBoard.swift; sourceTree = "<group>"; };
|
||||
67FD86861E41021E0023335C /* Opening.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Opening.swift; sourceTree = "<group>"; };
|
||||
67FD86881E4105B80023335C /* BoardStride.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardStride.swift; sourceTree = "<group>"; };
|
||||
67FD868A1E4105D40023335C /* BoardLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardLocation.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -123,6 +131,9 @@
|
||||
67A9C9F81DE64D2500510FB8 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67F9DB791E2438EC00C7EC5A /* ASCIIBoard.swift */,
|
||||
67FD86881E4105B80023335C /* BoardStride.swift */,
|
||||
67FD868A1E4105D40023335C /* BoardLocation.swift */,
|
||||
67A9C9FB1DE64D2500510FB8 /* Board.swift */,
|
||||
67A9C9FD1DE64D2500510FB8 /* Piece.swift */,
|
||||
67A9C9FE1DE64D2500510FB8 /* PieceMovement.swift */,
|
||||
@@ -148,6 +159,7 @@
|
||||
children = (
|
||||
67A9C9FA1DE64D2500510FB8 /* AIPlayer.swift */,
|
||||
67F9DB761E1AD46000C7EC5A /* AIConfiguration.swift */,
|
||||
67FD86851E4101DD0023335C /* Openings */,
|
||||
67D54A4B1DE6D8D200C12258 /* BoardRaters */,
|
||||
);
|
||||
name = AIPlayer;
|
||||
@@ -169,6 +181,14 @@
|
||||
name = BoardRaters;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67FD86851E4101DD0023335C /* Openings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67FD86861E41021E0023335C /* Opening.swift */,
|
||||
);
|
||||
name = Openings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -286,11 +306,15 @@
|
||||
67A9CA051DE64D2500510FB8 /* PieceMovement.swift in Sources */,
|
||||
6719898B1DFFE0D80053EA3D /* BoardRaterBoardDominance.swift in Sources */,
|
||||
67D54A481DE6D34100C12258 /* Player.swift in Sources */,
|
||||
67F9DB7A1E2438EC00C7EC5A /* ASCIIBoard.swift in Sources */,
|
||||
67F9DB6C1E1AC59600C7EC5A /* BoardRaterCheckMateOpportunity.swift in Sources */,
|
||||
67FD86891E4105B80023335C /* BoardStride.swift in Sources */,
|
||||
671989831DEB11900053EA3D /* BoardRaterCenterOwnership.swift in Sources */,
|
||||
67A9CA061DE64D2500510FB8 /* Human.swift in Sources */,
|
||||
676EF7C31E15A8A500E275B4 /* BoardRaterKingSurroundingPossession.swift in Sources */,
|
||||
67FD86871E41021E0023335C /* Opening.swift in Sources */,
|
||||
67F9DB771E1AD46000C7EC5A /* AIConfiguration.swift in Sources */,
|
||||
67FD868B1E4105D40023335C /* BoardLocation.swift in Sources */,
|
||||
67D54A4D1DE6DE1400C12258 /* BoardRaterCountPieces.swift in Sources */,
|
||||
09A4C0271E013950000CFBF4 /* BoardRaterThreatenedPieces.swift in Sources */,
|
||||
67D54A6B1DEAF16200C12258 /* BoardOperation.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user