Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88dda592b9 | |||
| 69458e8a28 | |||
| ad5f9e9122 | |||
| 7311b9add1 | |||
| 8f5ae059b1 | |||
| cca6079e56 | |||
| 6d8bcf1211 | |||
| cc4b32dad6 | |||
| c50d4de8d7 | |||
| f75b7cbe9d | |||
| e7b92a6ceb | |||
| 216bbfe0dd | |||
| 455b6e8a8b | |||
| 7c4e85e855 | |||
| 73d3dbbd38 | |||
| 77925776b8 | |||
| 717de7b90c | |||
| 782269b3d5 | |||
| d04cffff36 | |||
| 2f2b6e2d39 | |||
| 17221a7f78 | |||
| 1f64711de9 | |||
| 8b92378030 | |||
| 50514237ff | |||
| 59eebff8ed | |||
| 2ab3ec8f98 | |||
| 3bf665e7d4 | |||
| 7a5c82b638 | |||
| 702ac1ce2c | |||
| 2c5c1e1494 | |||
| 30e274b05f | |||
| 55795bf00a | |||
| 2ee384172c |
@@ -0,0 +1 @@
|
||||
3.0
|
||||
@@ -7,11 +7,14 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
09A4C0291E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A4C0281E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift */; };
|
||||
09AE32551E03D71D00A149FE /* BoardRaterPawnProgressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AE32541E03D71D00A149FE /* BoardRaterPawnProgressionTests.swift */; };
|
||||
671989891DFFE0410053EA3D /* BoardRaterCenterOwnershipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671989881DFFE0410053EA3D /* BoardRaterCenterOwnershipTests.swift */; };
|
||||
6719898D1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6719898C1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift */; };
|
||||
671989911DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671989901DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift */; };
|
||||
673B95191E4CE5880086CA97 /* BoardRaterThreatenedPiecesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A4C0281E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift */; };
|
||||
676902581E432380007C76D7 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676902571E432380007C76D7 /* PerformanceTests.swift */; };
|
||||
676C911B1E478A3A00985A4F /* SwiftChess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67A9CA0D1DE64D6500510FB8 /* SwiftChess.framework */; };
|
||||
676C911C1E478A3A00985A4F /* SwiftChess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 67A9CA0D1DE64D6500510FB8 /* SwiftChess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
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 */; };
|
||||
@@ -33,7 +36,6 @@
|
||||
67D54A641DE976A900C12258 /* BoardRaterCountPiecesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A631DE976A900C12258 /* BoardRaterCountPiecesTests.swift */; };
|
||||
67D54A661DE986F700C12258 /* PieceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A651DE986F700C12258 /* PieceTests.swift */; };
|
||||
67D54A681DE9A2DD00C12258 /* PieceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D54A671DE9A2DD00C12258 /* PieceView.swift */; };
|
||||
67F779201E1B923B00885B89 /* AIConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F7791F1E1B923B00885B89 /* AIConfigurationTests.swift */; };
|
||||
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 */; };
|
||||
@@ -42,6 +44,13 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
676C911D1E478A3A00985A4F /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 67A9CA071DE64D6500510FB8 /* SwiftChess.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 67A9C9DD1DE64CD200510FB8;
|
||||
remoteInfo = SwiftChess;
|
||||
};
|
||||
67A9CA0C1DE64D6500510FB8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 67A9CA071DE64D6500510FB8 /* SwiftChess.xcodeproj */;
|
||||
@@ -65,6 +74,20 @@
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
676C911F1E478A3A00985A4F /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
676C911C1E478A3A00985A4F /* SwiftChess.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
09A4C0281E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterThreatenedPiecesTests.swift; sourceTree = "<group>"; };
|
||||
09AE32541E03D71D00A149FE /* BoardRaterPawnProgressionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterPawnProgressionTests.swift; sourceTree = "<group>"; };
|
||||
@@ -78,6 +101,7 @@
|
||||
671989881DFFE0410053EA3D /* BoardRaterCenterOwnershipTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoardRaterCenterOwnershipTests.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
676902571E432380007C76D7 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.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>"; };
|
||||
@@ -109,6 +133,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
67A9CA3C1DE64E2B00510FB8 /* SwiftChess.framework in Frameworks */,
|
||||
676C911B1E478A3A00985A4F /* SwiftChess.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -185,6 +210,7 @@
|
||||
67B73A9E1E154C1E00C19176 /* AIPlayerTests.swift */,
|
||||
67F7791F1E1B923B00885B89 /* AIConfigurationTests.swift */,
|
||||
67F9DB741E1AD3BB00C7EC5A /* AIBehaviourTests.swift */,
|
||||
676902571E432380007C76D7 /* PerformanceTests.swift */,
|
||||
67FD86901E4128F00023335C /* OpeningsTests.swift */,
|
||||
67D54A621DE9768200C12258 /* BoardRaters */,
|
||||
67D54A551DE7680E00C12258 /* Tests.swift */,
|
||||
@@ -247,10 +273,12 @@
|
||||
67A9CA101DE64DAA00510FB8 /* Sources */,
|
||||
67A9CA111DE64DAA00510FB8 /* Frameworks */,
|
||||
67A9CA121DE64DAA00510FB8 /* Resources */,
|
||||
676C911F1E478A3A00985A4F /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
676C911E1E478A3A00985A4F /* PBXTargetDependency */,
|
||||
);
|
||||
name = SwiftChessExample;
|
||||
productName = SwiftChessExample;
|
||||
@@ -287,6 +315,7 @@
|
||||
TargetAttributes = {
|
||||
67A9CA131DE64DAA00510FB8 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
DevelopmentTeam = VAA3W4LPY2;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
67A9CA261DE64DAA00510FB8 = {
|
||||
@@ -387,14 +416,14 @@
|
||||
671989911DFFE8650053EA3D /* BoardRaterCenterDominanceTests.swift in Sources */,
|
||||
6719898D1DFFE0F40053EA3D /* BoardRaterBoardDominanceTests.swift in Sources */,
|
||||
67D54A5E1DE7683000C12258 /* GameTests.swift in Sources */,
|
||||
673B95191E4CE5880086CA97 /* BoardRaterThreatenedPiecesTests.swift in Sources */,
|
||||
67D54A661DE986F700C12258 /* PieceTests.swift in Sources */,
|
||||
671989891DFFE0410053EA3D /* BoardRaterCenterOwnershipTests.swift in Sources */,
|
||||
67B73A9F1E154C1E00C19176 /* AIPlayerTests.swift in Sources */,
|
||||
67F779201E1B923B00885B89 /* AIConfigurationTests.swift in Sources */,
|
||||
676902581E432380007C76D7 /* PerformanceTests.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 */,
|
||||
67F779261E1C32A400885B89 /* BoardRaterCenterFourOccupationTests.swift in Sources */,
|
||||
67F9DB6E1E1AC8DC00C7EC5A /* BoardRaterCheckMateOpportunityTests.swift in Sources */,
|
||||
@@ -404,6 +433,11 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
676C911E1E478A3A00985A4F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = SwiftChess;
|
||||
targetProxy = 676C911D1E478A3A00985A4F /* PBXContainerItemProxy */;
|
||||
};
|
||||
67A9CA291DE64DAA00510FB8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 67A9CA131DE64DAA00510FB8 /* SwiftChessExample */;
|
||||
@@ -521,11 +555,13 @@
|
||||
67A9CA2F1DE64DAA00510FB8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = VAA3W4LPY2;
|
||||
INFOPLIST_FILE = SwiftChess/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -540,10 +576,12 @@
|
||||
67A9CA301DE64DAA00510FB8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVES = YES;
|
||||
DEVELOPMENT_TEAM = VAA3W4LPY2;
|
||||
INFOPLIST_FILE = SwiftChess/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>classNames</key>
|
||||
<dict>
|
||||
<key>PerformanceTests</key>
|
||||
<dict>
|
||||
<key>testBishopMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testCanAnyPieceMovePerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0.033</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testKingMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0.003</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testKnightMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0.001</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testPawnMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0.001</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testQueenMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0.003</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>testRookMoveValidationPerformance()</key>
|
||||
<dict>
|
||||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
|
||||
<dict>
|
||||
<key>baselineAverage</key>
|
||||
<real>0</real>
|
||||
<key>baselineIntegrationDisplayName</key>
|
||||
<string>Local Baseline</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+31
@@ -4,6 +4,37 @@
|
||||
<dict>
|
||||
<key>runDestinationsByUUID</key>
|
||||
<dict>
|
||||
<key>3E643A92-069B-413B-86C0-1FF211DBD9A9</key>
|
||||
<dict>
|
||||
<key>localComputer</key>
|
||||
<dict>
|
||||
<key>busSpeedInMHz</key>
|
||||
<integer>100</integer>
|
||||
<key>cpuCount</key>
|
||||
<integer>1</integer>
|
||||
<key>cpuKind</key>
|
||||
<string>Intel Core m7</string>
|
||||
<key>cpuSpeedInMHz</key>
|
||||
<integer>1300</integer>
|
||||
<key>logicalCPUCoresPerPackage</key>
|
||||
<integer>4</integer>
|
||||
<key>modelCode</key>
|
||||
<string>MacBook9,1</string>
|
||||
<key>physicalCPUCoresPerPackage</key>
|
||||
<integer>2</integer>
|
||||
<key>platformIdentifier</key>
|
||||
<string>com.apple.platform.macosx</string>
|
||||
</dict>
|
||||
<key>targetArchitecture</key>
|
||||
<string>i386</string>
|
||||
<key>targetDevice</key>
|
||||
<dict>
|
||||
<key>modelCode</key>
|
||||
<string>iPhone5,1</string>
|
||||
<key>platformIdentifier</key>
|
||||
<string>com.apple.platform.iphonesimulator</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>D188478B-4F41-448E-8BB9-06B1C7A6FB8B</key>
|
||||
<dict>
|
||||
<key>localComputer</key>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1212" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="o9i-0S-2Up">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="o9i-0S-2Up">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -39,20 +42,24 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KRf-s7-H9a">
|
||||
<rect key="frame" x="133.5" y="264.5" width="108" height="138"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fGu-y2-Qxm">
|
||||
<rect key="frame" x="0.0" y="92" width="108" height="30"/>
|
||||
<state key="normal" title="AI vs AI"/>
|
||||
<connections>
|
||||
<action selector="AIvsAIButtonPressed:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="mrX-sU-WvD"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kEX-Ea-5Xq">
|
||||
<rect key="frame" x="0.0" y="46" width="108" height="30"/>
|
||||
<state key="normal" title="Player vs Player"/>
|
||||
<connections>
|
||||
<action selector="playerVsPlayerButtonPressed:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="xIZ-0U-CNW"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gcM-Gf-hHc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="108" height="30"/>
|
||||
<state key="normal" title="Player vs AI"/>
|
||||
<connections>
|
||||
<action selector="playerVsAIButtonPressed:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="QvL-y5-tdf"/>
|
||||
@@ -99,41 +106,50 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NnZ-Ew-a2B" customClass="BoardView" customModule="SwiftChessExample" customModuleProvider="target">
|
||||
<rect key="frame" x="8" y="154" width="359" height="359"/>
|
||||
<color key="backgroundColor" red="0.46274509800000002" green="0.71372549019999998" blue="0.97254901959999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="NnZ-Ew-a2B" secondAttribute="height" multiplier="1:1" id="GNF-YV-5Vh"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZZ0-mR-VXd">
|
||||
<rect key="frame" x="8" y="116" width="44" height="30"/>
|
||||
<state key="normal" title="Castle"/>
|
||||
<connections>
|
||||
<action selector="blackKingSideCastleButtonPressedWithSender:" destination="M5V-oM-g1K" eventType="touchUpInside" id="rLk-f9-Sot"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eZD-P8-Hr3">
|
||||
<rect key="frame" x="323" y="116" width="44" height="30"/>
|
||||
<state key="normal" title="Castle"/>
|
||||
<connections>
|
||||
<action selector="blackQueenSideCastleButtonPressedWithSender:" destination="M5V-oM-g1K" eventType="touchUpInside" id="04c-Bn-Bkt"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yxk-YT-5FA">
|
||||
<rect key="frame" x="8" y="521" width="44" height="30"/>
|
||||
<state key="normal" title="Castle"/>
|
||||
<connections>
|
||||
<action selector="whiteQueenSideCastleButtonPressedWithSender:" destination="M5V-oM-g1K" eventType="touchUpInside" id="3el-xi-NUE"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FwI-ZC-g1Z">
|
||||
<rect key="frame" x="323" y="521" width="44" height="30"/>
|
||||
<state key="normal" title="Castle"/>
|
||||
<connections>
|
||||
<action selector="whiteKingSideCastleButtonPressedWithSender:" destination="M5V-oM-g1K" eventType="touchUpInside" id="geO-9J-0qc"/>
|
||||
</connections>
|
||||
</button>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="JEV-Ul-qRP">
|
||||
<rect key="frame" x="177" y="121" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NnZ-Ew-a2B" firstAttribute="top" secondItem="eZD-P8-Hr3" secondAttribute="bottom" constant="8" id="8gI-BZ-sfM"/>
|
||||
<constraint firstItem="Yxk-YT-5FA" firstAttribute="leading" secondItem="NnZ-Ew-a2B" secondAttribute="leading" id="BxL-mm-tSn"/>
|
||||
<constraint firstItem="NnZ-Ew-a2B" firstAttribute="centerY" secondItem="0i2-vi-BZ9" secondAttribute="centerY" id="DzP-fd-RxD"/>
|
||||
<constraint firstItem="JEV-Ul-qRP" firstAttribute="centerY" secondItem="ZZ0-mR-VXd" secondAttribute="centerY" id="LLa-vl-Dcs"/>
|
||||
<constraint firstItem="Yxk-YT-5FA" firstAttribute="top" secondItem="NnZ-Ew-a2B" secondAttribute="bottom" constant="8" id="N2S-Kg-2L0"/>
|
||||
<constraint firstItem="ZZ0-mR-VXd" firstAttribute="leading" secondItem="NnZ-Ew-a2B" secondAttribute="leading" id="NAg-kl-feP"/>
|
||||
<constraint firstItem="eZD-P8-Hr3" firstAttribute="trailing" secondItem="NnZ-Ew-a2B" secondAttribute="trailing" id="NYh-P9-I1j"/>
|
||||
@@ -141,10 +157,12 @@
|
||||
<constraint firstItem="NnZ-Ew-a2B" firstAttribute="top" secondItem="ZZ0-mR-VXd" secondAttribute="bottom" constant="8" id="TgY-3v-zy1"/>
|
||||
<constraint firstItem="NnZ-Ew-a2B" firstAttribute="leading" secondItem="0i2-vi-BZ9" secondAttribute="leading" constant="8" id="Ve9-Wz-Mmx"/>
|
||||
<constraint firstItem="FwI-ZC-g1Z" firstAttribute="top" secondItem="NnZ-Ew-a2B" secondAttribute="bottom" constant="8" id="YnW-Zf-aL2"/>
|
||||
<constraint firstItem="JEV-Ul-qRP" firstAttribute="centerX" secondItem="0i2-vi-BZ9" secondAttribute="centerX" id="dOc-ve-oSq"/>
|
||||
<constraint firstItem="FwI-ZC-g1Z" firstAttribute="trailing" secondItem="NnZ-Ew-a2B" secondAttribute="trailing" id="rx5-mS-bp0"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="activityIndicator" destination="JEV-Ul-qRP" id="93U-eY-Z18"/>
|
||||
<outlet property="blackKingSideCastleButton" destination="ZZ0-mR-VXd" id="jhh-iW-CTM"/>
|
||||
<outlet property="blackQueenSideCastleButton" destination="eZD-P8-Hr3" id="yY5-WP-Yvk"/>
|
||||
<outlet property="boardView" destination="NnZ-Ew-a2B" id="qeo-BV-9YO"/>
|
||||
|
||||
@@ -16,6 +16,7 @@ class GameViewController: UIViewController {
|
||||
@IBOutlet weak var whiteQueenSideCastleButton: UIButton!
|
||||
@IBOutlet weak var blackKingSideCastleButton: UIButton!
|
||||
@IBOutlet weak var blackQueenSideCastleButton: UIButton!
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
|
||||
var pieceViews = [PieceView]()
|
||||
var game: Game!
|
||||
@@ -60,6 +61,9 @@ class GameViewController: UIViewController {
|
||||
addPieceView(at: location.x, y: location.y, piece: piece)
|
||||
}
|
||||
|
||||
// Activity Indicator
|
||||
activityIndicator.hidesWhenStopped = true
|
||||
|
||||
// Update castle buttons visibility
|
||||
updateCastleButtonsVisibility()
|
||||
|
||||
@@ -71,7 +75,7 @@ class GameViewController: UIViewController {
|
||||
// Take go if the first player is an AI player
|
||||
if !self.hasMadeInitialAppearance {
|
||||
if let player = game.currentPlayer as? AIPlayer {
|
||||
player.makeMove()
|
||||
player.makeMoveAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,7 +364,7 @@ extension GameViewController: GameDelegate {
|
||||
}
|
||||
|
||||
func gameDidEndUpdates(game: Game) {
|
||||
// do nothing
|
||||
activityIndicator.stopAnimating()
|
||||
}
|
||||
|
||||
func gameWonByPlayer(game: Game, player: Player) {
|
||||
@@ -394,7 +398,8 @@ extension GameViewController: GameDelegate {
|
||||
func tellAIToTakeGo() {
|
||||
|
||||
if let player = game.currentPlayer as? AIPlayer {
|
||||
player.makeMove()
|
||||
activityIndicator.startAnimating();
|
||||
player.makeMoveAsync()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class MenuViewController: UIViewController {
|
||||
print("Player vs AI button pressed")
|
||||
|
||||
let whitePlayer = Human(color: .white)
|
||||
let blackPlayer = AIPlayer(color: .black)
|
||||
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer)
|
||||
startGame(game: game)
|
||||
@@ -46,8 +46,8 @@ class MenuViewController: UIViewController {
|
||||
@IBAction func AIvsAIButtonPressed(_ sender: UIButton){
|
||||
print("AI vs AI button pressed")
|
||||
|
||||
let whitePlayer = AIPlayer(color: .white)
|
||||
let blackPlayer = AIPlayer(color: .black)
|
||||
let whitePlayer = AIPlayer(color: .white, configuration: AIConfiguration(difficulty: .hard))
|
||||
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer)
|
||||
startGame(game: game)
|
||||
|
||||
@@ -31,8 +31,8 @@ class AIBehaviourTests: XCTestCase {
|
||||
|
||||
func makeGameWithBoard(board: Board, colorToMove: Color) -> Game {
|
||||
|
||||
let whitePlayer = AIPlayer(color: .white)
|
||||
let blackPlayer = AIPlayer(color: .black)
|
||||
let whitePlayer = AIPlayer(color: .white, configuration: AIConfiguration(difficulty: .hard))
|
||||
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
|
||||
|
||||
@@ -102,7 +102,7 @@ class AIBehaviourTests: XCTestCase {
|
||||
return
|
||||
}
|
||||
|
||||
player.makeMove()
|
||||
player.makeMoveSync()
|
||||
|
||||
// If there is not piece at the location, then test has passed
|
||||
guard let piece = game.board.getPiece(at: location) else {
|
||||
@@ -118,63 +118,53 @@ class AIBehaviourTests: XCTestCase {
|
||||
XCTFail("Black moved bishop")
|
||||
}
|
||||
|
||||
func test_ScenarioTwo_BlackShouldTradeKnight() {
|
||||
func testBlackShouldTradePawnForQueen() {
|
||||
|
||||
// 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 - -" +
|
||||
let board = ASCIIBoard(pieces: "g p - - - - - -" +
|
||||
"p p - - - - - -" +
|
||||
"- - - - p - - -" +
|
||||
"- - - p P - - -" +
|
||||
"Q - - - - - - -" +
|
||||
"P P k P K P P P" +
|
||||
"R K B G - - - R" )
|
||||
"- - - - - Q - -" +
|
||||
"- - - - B - - -" +
|
||||
"- - - - - - - -" +
|
||||
"P P - - - - - -" +
|
||||
"G P - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(x: 0, y: 2)
|
||||
let rookLocation = BoardLocation(x: 0, y: 0)
|
||||
|
||||
let queenLocation = board.locationOfCharacter("Q")
|
||||
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
|
||||
|
||||
guard let player = game.currentPlayer as? AIPlayer else {
|
||||
XCTFail("Expected an AI Player")
|
||||
|
||||
guard let player = game.blackPlayer as? AIPlayer else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
player.makeMove()
|
||||
player.makeMoveSync()
|
||||
|
||||
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))")
|
||||
XCTAssertEqual(game.board.getPiece(at: queenLocation)?.type, .pawn)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func testBlackShouldTakeWhiteQueenWithPawn() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - p g" +
|
||||
"- - - b - - p p" +
|
||||
"- - - - q - - -" +
|
||||
"- B p k P p - -" +
|
||||
"- - - P Q - - -" +
|
||||
"- - - - - - - -" +
|
||||
"P P - - - - - -" +
|
||||
"G P - - - - - -" )
|
||||
|
||||
let queenLocation = board.locationOfCharacter("Q")
|
||||
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
|
||||
|
||||
guard let player = game.blackPlayer as? AIPlayer else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
player.makeMoveSync()
|
||||
|
||||
XCTAssertEqual(game.board.getPiece(at: queenLocation)?.type, .pawn)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,19 +25,19 @@ class AIPlayerTests: XCTestCase {
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
// func testPerformanceExample() {
|
||||
// // This is an example of a performance test case.
|
||||
// self.measure {
|
||||
// // Put the code you want to measure the time of here.
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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 whitePlayer = AIPlayer(color: .white, configuration: AIConfiguration(difficulty: .hard))
|
||||
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
|
||||
return game
|
||||
|
||||
@@ -156,6 +156,33 @@ class BoardLocationTests: XCTestCase {
|
||||
XCTAssertEqual(expected.toLocation, actual.toLocation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testIsDarkSquareReturnsExpectedValue() {
|
||||
|
||||
let board = ASCIIBoard(colors: "! * ! * ! * ! *" +
|
||||
"* ! * ! * ! * !" +
|
||||
"! * ! * ! * ! *" +
|
||||
"* ! * ! * ! * !" +
|
||||
"! * ! * ! * ! *" +
|
||||
"* ! * ! * ! * !" +
|
||||
"! * ! * ! * ! *" +
|
||||
"* ! * ! * ! * !" )
|
||||
|
||||
let darkLocatons = board.locationsWithCharacter("*")
|
||||
let lightLocations = board.locationsWithCharacter("!")
|
||||
|
||||
darkLocatons.forEach{
|
||||
XCTAssertTrue($0.isDarkSquare, "Expected \($0) to be dark")
|
||||
}
|
||||
|
||||
lightLocations.forEach{
|
||||
XCTAssertFalse($0.isDarkSquare, "Expected \($0) to be light")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class BoardRaterBoardDominanceTests: XCTestCase {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
self.boardRater = BoardRaterBoardDominance(configuration: AIConfiguration());
|
||||
self.boardRater = BoardRaterBoardDominance(configuration: AIConfiguration(difficulty: .hard));
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@@ -16,7 +16,7 @@ class BoardRaterCenterDominanceTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
boardRater = BoardRaterCenterDominance(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterCenterDominance(configuration: AIConfiguration(difficulty: .hard))
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@@ -24,7 +24,7 @@ class BoardRaterCenterFourOccupationTests: XCTestCase {
|
||||
|
||||
func defaultBoardRater() -> BoardRaterCenterFourOccupation {
|
||||
|
||||
let configuration = AIConfiguration()
|
||||
let configuration = AIConfiguration(difficulty: .hard)
|
||||
let boardRater = BoardRaterCenterFourOccupation(configuration: configuration)
|
||||
return boardRater
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class BoardRaterCenterOwnershipTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
boardRater = BoardRaterCenterOwnership(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterCenterOwnership(configuration: AIConfiguration(difficulty: .hard))
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@@ -16,7 +16,7 @@ class BoardRaterCheckMateOpportunityTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
boardRater = BoardRaterCheckMateOpportunity(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterCheckMateOpportunity(configuration: AIConfiguration(difficulty: .hard))
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@@ -17,7 +17,7 @@ class BoardRaterCountPiecesTests: XCTestCase {
|
||||
super.setUp()
|
||||
|
||||
// Initiailise a new board rater for each test
|
||||
boardRater = BoardRaterCountPieces(configuration: AIConfiguration());
|
||||
boardRater = BoardRaterCountPieces(configuration: AIConfiguration(difficulty: .hard));
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
@@ -16,7 +16,7 @@ class BoardRaterKingSurroundingPossessionTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
boardRater = BoardRaterKingSurroundingPossession(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterKingSurroundingPossession(configuration: AIConfiguration(difficulty: .hard))
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@@ -357,34 +357,57 @@ class BoardRaterKingSurroundingPossessionTests: XCTestCase {
|
||||
XCTAssertGreaterThan(rating, 0)
|
||||
}
|
||||
|
||||
func testThatPiecesSurroundingOpponentKingResultsInNegativeRatingForWhite() {
|
||||
func testThatPiecesSurroundingOpponentKingResultsInMoreNegativeRatingForWhite() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - p g p - -" +
|
||||
"- - - p p p - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - G - - -" )
|
||||
let openKingBoard = ASCIIBoard(pieces: "- - - - g - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - G - - -" )
|
||||
|
||||
let rating = boardRater.ratingfor(board: board.board, color: .white)
|
||||
XCTAssertLessThan(rating, 0)
|
||||
let surroundedKingBoard = ASCIIBoard(pieces: "- - - p g p - -" +
|
||||
"- - - p p p - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - G - - -" )
|
||||
|
||||
|
||||
let openKingRating = boardRater.ratingfor(board: openKingBoard.board, color: .white)
|
||||
let surroundedKingRating = boardRater.ratingfor(board: surroundedKingBoard.board, color: .white)
|
||||
|
||||
XCTAssertLessThan(surroundedKingRating, openKingRating)
|
||||
}
|
||||
|
||||
func testThatPiecesSurroundingOpponentKingResultsInNegativeRatingForBlack() {
|
||||
func testThatPiecesSurroundingOpponentKingResultsInMoreNegativeRatingForBlack() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - g - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - P P P - -" +
|
||||
"- - - P G P - -" )
|
||||
let openKingBoard = ASCIIBoard(pieces: "- - - - g - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - G - - -" )
|
||||
|
||||
let rating = boardRater.ratingfor(board: board.board, color: .black)
|
||||
XCTAssertLessThan(rating, 0)
|
||||
let surroundedKingBoard = ASCIIBoard(pieces: "- - - - g - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - P P P - -" +
|
||||
"- - - P G P - -" )
|
||||
|
||||
|
||||
let openKingRating = boardRater.ratingfor(board: openKingBoard.board, color: .black)
|
||||
let surroundedKingRating = boardRater.ratingfor(board: surroundedKingBoard.board, color: .black)
|
||||
XCTAssertLessThan(surroundedKingRating, openKingRating)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class BoardRaterPawnProgressionTests: XCTestCase {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
boardRater = BoardRaterPawnProgression(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterPawnProgression(configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
boardRater = BoardRaterThreatenedPieces(configuration: AIConfiguration())
|
||||
boardRater = BoardRaterThreatenedPieces(configuration: AIConfiguration(difficulty: .hard))
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@@ -54,9 +54,9 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
|
||||
let rating = boardRater.ratingfor(board: board.board, color: .white)
|
||||
|
||||
XCTAssert(rating < 0);
|
||||
XCTAssertLessThan(rating, 0)
|
||||
}
|
||||
|
||||
|
||||
func testBoardRaterThreatenedPiecesReturnsPositiveValueIfThreateningOpponant() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
@@ -70,11 +70,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
|
||||
let rating = boardRater.ratingfor(board: board.board, color: .white)
|
||||
|
||||
XCTAssert(rating > 0);
|
||||
XCTAssertGreaterThan(rating, 0)
|
||||
}
|
||||
|
||||
func testBoardRaterThreatenedPiecesReturnsHigherThreatValueForHigherValuePieces() {
|
||||
|
||||
/*
|
||||
|
||||
let queenBoard = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
@@ -109,32 +111,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
|
||||
|
||||
XCTAssert(queenRating > knightRating);
|
||||
}
|
||||
/*
|
||||
func testOwnPiecesMultiplerShouldIncreaceValueOfIncomingThreats() {
|
||||
|
||||
let board = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"q - - - - - Q -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" )
|
||||
|
||||
boardRater.ownPiecesMultipler = 1
|
||||
let expectedLowRating = boardRater.ratingfor(board: board.board, color: .white)
|
||||
|
||||
boardRater.ownPiecesMultipler = 2
|
||||
let expectedHighRating = boardRater.ratingfor(board: board.board, color: .white)
|
||||
|
||||
// Higher threat levels result in negative ratings!
|
||||
XCTAssert(expectedHighRating < expectedLowRating)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func testBoardRaterThreatenedPiecesReturnsMoreNegativeThreatValueForFavourableTrade() {
|
||||
|
||||
/*
|
||||
|
||||
// Good trade (White rook can be taken by the black queen, but the white pawn will then take the queen)
|
||||
let goodTradeBoard = ASCIIBoard(pieces: "- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
@@ -162,6 +145,8 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
let badTradeRating = boardRater.threatRatingForPiece(at: rookLocation, board: badTradeBoard.board, color: .white)
|
||||
|
||||
XCTAssert(goodTradeRating < badTradeRating);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
// MARK - Get protected pieces tests
|
||||
@@ -186,9 +171,9 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
|
||||
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let gameBoard = board.board
|
||||
let protectingLocations = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: board.board).map{ $0.location }
|
||||
|
||||
// Check all of the expected locations appeared in the protecting locations array
|
||||
for expectedIndex in expectedIndexes {
|
||||
@@ -229,12 +214,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- B - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
// None of the pieces are protecting the queen, so expect count to be zero
|
||||
XCTAssertTrue(protectingLocations.count == 0)
|
||||
XCTAssertTrue(protectingPieces.count == 0)
|
||||
}
|
||||
|
||||
func testGetProtectingPiecesDoesntReturnThreateningPiecesOfOppositeColor() {
|
||||
@@ -249,12 +235,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - b - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
// The black pieces cannot protect the white queen, so expect count to be zero
|
||||
XCTAssertTrue(protectingLocations.count == 0)
|
||||
XCTAssertTrue(protectingPieces.count == 0)
|
||||
}
|
||||
|
||||
func testGetProtectingPiecesDoesntReturnPawnsMovingStraightAhead() {
|
||||
@@ -269,11 +256,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
XCTAssertTrue(protectingLocations.count == 0)
|
||||
XCTAssertTrue(protectingPieces.count == 0)
|
||||
|
||||
}
|
||||
|
||||
func testGetProtectingPiecesReturnsPawnsMovingDiagonally() {
|
||||
@@ -288,11 +277,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
XCTAssertTrue(protectingLocations.count == 1)
|
||||
XCTAssertTrue(protectingPieces.count == 1)
|
||||
}
|
||||
|
||||
// MARK - Get threatening pieces tests
|
||||
@@ -317,9 +307,10 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
|
||||
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let gameBoard = board.board
|
||||
|
||||
let threateningLocations = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard).map{ $0.location }
|
||||
|
||||
// Check all of the expected locations appeared in the protecting locations array
|
||||
for expectedIndex in expectedIndexes {
|
||||
@@ -360,12 +351,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- b - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
// None of the pieces are threatening the queen, so expect count to be zero
|
||||
XCTAssertTrue(threateningLocations.count == 0)
|
||||
XCTAssertTrue(threateningPieces.count == 0)
|
||||
}
|
||||
|
||||
func testGetThreateningPiecesDoesntReturnProtectingPiecesOfSameColor() {
|
||||
@@ -380,12 +372,13 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - B - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
// The white pieces cannot threaten the white queen, so expect count to be zero
|
||||
XCTAssertTrue(threateningLocations.count == 0)
|
||||
XCTAssertTrue(threateningPieces.count == 0)
|
||||
}
|
||||
|
||||
func testGetThreateningPiecesDoesntReturnPawnsMovingStraightAhead() {
|
||||
@@ -400,11 +393,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
XCTAssertTrue(threateningLocations.count == 0)
|
||||
XCTAssertTrue(threateningPieces.count == 0)
|
||||
}
|
||||
|
||||
func testGetThreateningPiecesReturnsPawnsMovingDiagonally() {
|
||||
@@ -419,11 +413,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
|
||||
"- - - - - - - -" )
|
||||
|
||||
let queenLocation = BoardLocation(index: board.indexOfCharacter("q"))
|
||||
let gameBoard = board.board
|
||||
|
||||
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
|
||||
on: board.board)
|
||||
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
|
||||
onBoard: gameBoard)
|
||||
|
||||
XCTAssertTrue(threateningLocations.count == 1)
|
||||
XCTAssertTrue(threateningPieces.count == 1)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -138,6 +138,17 @@ class BoardTests: XCTestCase {
|
||||
|
||||
}
|
||||
|
||||
func testRemovePieceRemovesPiece() {
|
||||
|
||||
var board = Board(state: .newGame)
|
||||
let location = BoardLocation(index: 10)
|
||||
board.setPiece(Piece(type: .pawn, color: .white),
|
||||
at: location)
|
||||
board.removePiece(atLocation: location)
|
||||
|
||||
XCTAssertNil(board.getPiece(at: location))
|
||||
}
|
||||
|
||||
func testMovePieceResultsInPieceMoved() {
|
||||
|
||||
var board = Board(state: .empty);
|
||||
@@ -1011,6 +1022,81 @@ class BoardTests: XCTestCase {
|
||||
XCTAssertEqual(locations.filter( { $0 == expectedLocation} ).count, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Test Board Equality
|
||||
|
||||
func testEqualBoardsAreEqual() {
|
||||
|
||||
var board1 = Board(state: .newGame)
|
||||
var board2 = Board(state: .newGame)
|
||||
|
||||
let sourceLocation = BoardLocation(x: 0, y: 1)
|
||||
let targetLocation = BoardLocation(x: 0, y: 3)
|
||||
|
||||
board1.movePiece(fromLocation: sourceLocation, toLocation: targetLocation)
|
||||
board2.movePiece(fromLocation: sourceLocation, toLocation: targetLocation)
|
||||
|
||||
XCTAssertEqual(board1, board2)
|
||||
}
|
||||
|
||||
func testNonEqualBoardsAreNotEqual() {
|
||||
|
||||
var board1 = Board(state: .newGame)
|
||||
board1.movePiece(fromLocation: BoardLocation(x: 0, y: 1),
|
||||
toLocation: BoardLocation(x: 0, y: 3))
|
||||
|
||||
let board2 = Board(state: .newGame)
|
||||
|
||||
XCTAssertNotEqual(board1, board2)
|
||||
}
|
||||
|
||||
// MARK: - Piece Location tests
|
||||
|
||||
func testPieceLocationsAreCorrectForNewBoard() {
|
||||
|
||||
let board = Board(state: .newGame)
|
||||
|
||||
for location in BoardLocation.all {
|
||||
|
||||
guard let piece = board.getPiece(at: location) else {
|
||||
continue
|
||||
}
|
||||
|
||||
XCTAssertEqual(piece.location, location)
|
||||
}
|
||||
}
|
||||
|
||||
func testPieceLocationsAreCorrectAfterMoves() {
|
||||
|
||||
var board = Board(state: .newGame)
|
||||
board.movePiece(fromLocation: BoardLocation(gridPosition: .e2),
|
||||
toLocation: BoardLocation(gridPosition: .e4))
|
||||
|
||||
board.movePiece(fromLocation: BoardLocation(gridPosition: .d7),
|
||||
toLocation: BoardLocation(gridPosition: .d5))
|
||||
|
||||
for location in BoardLocation.all {
|
||||
|
||||
guard let piece = board.getPiece(at: location) else {
|
||||
continue
|
||||
}
|
||||
|
||||
XCTAssertEqual(piece.location, location)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -107,8 +107,4 @@ class GameTests: XCTestCase {
|
||||
XCTAssertTrue(game.state == Game.State.won(color: .black))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// PerformanceTests.swift
|
||||
// Example
|
||||
//
|
||||
// Created by Steve Barnegren on 02/02/2017.
|
||||
// Copyright © 2017 CocoaPods. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftChess
|
||||
|
||||
class PerformanceTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testPawnMoveValidationPerformance() {
|
||||
|
||||
let board = Board(state: .newGame)
|
||||
let pawnLocation = BoardLocation(x: 0, y: 1)
|
||||
|
||||
guard let pawn = board.getPiece(at: pawnLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(pawn.type == .pawn)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
pawn.movement.canPieceMove(fromLocation: pawnLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueenMoveValidationPerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - R - - p -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - Q - - - r" +
|
||||
"- - - - - - - -" +
|
||||
"P P - - - - - -" +
|
||||
"G - - - - - - -" )
|
||||
|
||||
let queenLocation = asciiBoard.locationOfCharacter("Q")
|
||||
let board = asciiBoard.board
|
||||
|
||||
guard let queen = board.getPiece(at: queenLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(queen.type == .queen)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
queen.movement.canPieceMove(fromLocation: queenLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testKingMoveValidationPerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - R - - p -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - Q - - - r" +
|
||||
"- - - - - - - -" +
|
||||
"- - - P - - - -" +
|
||||
"- - - G - - - -" )
|
||||
|
||||
let kingLocation = asciiBoard.locationOfCharacter("G")
|
||||
let board = asciiBoard.board
|
||||
|
||||
guard let king = board.getPiece(at: kingLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(king.type == .king)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
king.movement.canPieceMove(fromLocation: kingLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testKnightMoveValidationPerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - R - - p -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - Q - K - r" +
|
||||
"- - - - - - - -" +
|
||||
"- - - P - - - -" +
|
||||
"- - - G - - - -" )
|
||||
|
||||
let knightLocation = asciiBoard.locationOfCharacter("K")
|
||||
let board = asciiBoard.board
|
||||
|
||||
guard let knight = board.getPiece(at: knightLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(knight.type == .knight)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
knight.movement.canPieceMove(fromLocation: knightLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBishopMoveValidationPerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - R - - p -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - - - - - -" +
|
||||
"- - - Q - K - r" +
|
||||
"- - - - - B - -" +
|
||||
"- - - P - - - -" +
|
||||
"- - - G - - - -" )
|
||||
|
||||
let bishopLocation = asciiBoard.locationOfCharacter("B")
|
||||
let board = asciiBoard.board
|
||||
|
||||
guard let bishop = board.getPiece(at: bishopLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(bishop.type == .bishop)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
bishop.movement.canPieceMove(fromLocation: bishopLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRookMoveValidationPerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "- - - - - - - g" +
|
||||
"- - - - - - p -" +
|
||||
"- - - - - - - -" +
|
||||
"- R - - - - - -" +
|
||||
"- - - Q - K - r" +
|
||||
"- - - - - B - -" +
|
||||
"- - - P - - - -" +
|
||||
"- - - G - - - -" )
|
||||
|
||||
let rookLocation = asciiBoard.locationOfCharacter("R")
|
||||
let board = asciiBoard.board
|
||||
|
||||
guard let rook = board.getPiece(at: rookLocation) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssert(rook.type == .rook)
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
rook.movement.canPieceMove(fromLocation: rookLocation,
|
||||
toLocation: $0,
|
||||
board: board)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCanAnyPieceMovePerformance() {
|
||||
|
||||
let asciiBoard = ASCIIBoard(pieces: "r - b q g b - r" +
|
||||
"p p p - - p - -" +
|
||||
"k - - - p - - k" +
|
||||
"- - - p - - - -" +
|
||||
"- - - P - - - -" +
|
||||
"K - - - P - - -" +
|
||||
"P P P - - P P -" +
|
||||
"R - B Q G B K R" )
|
||||
|
||||
let board = asciiBoard.board;
|
||||
|
||||
self.measure {
|
||||
|
||||
BoardLocation.all.forEach{
|
||||
board.canColorMoveAnyPieceToLocation(color: .white, location: $0)
|
||||
board.canColorMoveAnyPieceToLocation(color: .black, location: $0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SwiftChess
|
||||
@testable import SwiftChess
|
||||
|
||||
class PieceMovementTests: XCTestCase {
|
||||
|
||||
|
||||
@@ -1,26 +1,126 @@
|
||||
# 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.
|
||||
## Features
|
||||
|
||||
There's no documentation for now, as the public api is still in flux, but there is a complete example project for iOS.
|
||||
- Move validation
|
||||
- AI with three difficulty levels
|
||||
- Callbacks for check, checkmate and stalemate
|
||||
- Supports castling
|
||||
- Supports En Passent
|
||||
- Supports pawn promotion
|
||||
- Asyncronous AI move calculation
|
||||
|
||||
SwiftChess also has a reasonably comprehensive set of unit tests.
|
||||
|
||||
SwiftChess doesn't provide any UI, just all of the logic required to create a chess game. The example project contains a complete UIKit UI with touch handling that you start from if you like.
|
||||
|
||||
## Example
|
||||
|
||||
The example application contains a complete implementation of swift chess.
|
||||
|
||||
Run `Example/Example.xcodeproj`
|
||||
|
||||
The example app can run *player vs player*, *player vs AI*, *AI vs AI* matches
|
||||
## Basic Use
|
||||
|
||||
##### Start a game
|
||||
|
||||
```
|
||||
// Make a human player
|
||||
let whitePlayer = Human(color: .white)
|
||||
|
||||
// ... or an AI Player
|
||||
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
|
||||
|
||||
// Create a game
|
||||
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer)
|
||||
```
|
||||
|
||||
##### Make a move
|
||||
|
||||
```
|
||||
if let player = game.currentPlayer as? Human {
|
||||
|
||||
let currentLocation = BoardLocation(x: 4, y: 1)
|
||||
let newLocation = BoardLocation(x: 4, y: 2)
|
||||
|
||||
try! player.movePiece(fromLocation: currentLocation,
|
||||
toLocation: newLocation)
|
||||
}
|
||||
```
|
||||
|
||||
##### Tell the AI to make a move
|
||||
|
||||
```
|
||||
if let player = game.currentPlayer as? AIPlayer {
|
||||
player.makeMoveAsync()
|
||||
}
|
||||
```
|
||||
##### Then just wait for the callbacks!
|
||||
|
||||
```
|
||||
extension GameViewController: GameDelegate {
|
||||
|
||||
func gameDidMovePiece(game: Game, piece: Piece, toLocation: BoardLocation) {
|
||||
// Move piece on board
|
||||
}
|
||||
|
||||
func gameDidRemovePiece(game: Game, piece: Piece, location: BoardLocation) {
|
||||
// Remove piece from board
|
||||
}
|
||||
|
||||
func gameDidTransformPiece(game: Game, piece: Piece, location: BoardLocation) {
|
||||
// A pawn was promoted!
|
||||
}
|
||||
|
||||
func gameWonByPlayer(game: Game, player: Player) {
|
||||
ShowAlert("Checkmate!")
|
||||
}
|
||||
|
||||
func gameEndedInStaleMate(game: Game) {
|
||||
ShowAlert("Stalemate!")
|
||||
}
|
||||
|
||||
func gameDidChangeCurrentPlayer(game: Game) {
|
||||
// Make another move
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Other stuff
|
||||
|
||||
##### Make a castling move
|
||||
|
||||
```
|
||||
if game.board.canColorCastle(color: .white, side: .kingSide) {
|
||||
player.performCastleMove(side: .kingSide)
|
||||
}
|
||||
```
|
||||
|
||||
##### Support pawn promotion
|
||||
|
||||
```
|
||||
func promotedTypeForPawn(location: BoardLocation,
|
||||
player: Human,
|
||||
possiblePromotions: [Piece.PieceType],
|
||||
callback: @escaping (Piece.PieceType) -> Void) {
|
||||
|
||||
// Show UI for the user to select one of the possible promotion types
|
||||
// then call the handler
|
||||
|
||||
// ...or some games just promote to a queen
|
||||
callback(.queen)
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
Steve Barnegren
|
||||
|
||||
[Follow me on Twitter](https://twitter.com/stevebarnegren)
|
||||
Follow me on twitter [@SteveBarnegren](https://twitter.com/stevebarnegren)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+3
-11
@@ -9,7 +9,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'SwiftChess'
|
||||
s.version = '0.1.0'
|
||||
s.summary = 'A short description of SwiftChess.'
|
||||
s.summary = 'Chess engine written in Swift'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
# * Think: What does it do? Why did you write it? What is the focus?
|
||||
@@ -18,25 +18,17 @@ Pod::Spec.new do |s|
|
||||
# * Finally, don't worry about the indent, CocoaPods strips it!
|
||||
|
||||
s.description = <<-DESC
|
||||
TODO: Add long description of the pod here.
|
||||
Chess engine written in Swift
|
||||
DESC
|
||||
|
||||
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/SteveBarnegren/SwiftChess.git', :tag => s.version.to_s }
|
||||
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
||||
s.social_media_url = 'https://twitter.com/stevebarnegren'
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
|
||||
s.source_files = 'SwiftChess/Source/**/*'
|
||||
|
||||
# s.resource_bundles = {
|
||||
# 'SwiftChess' => ['SwiftChess/Assets/*.png']
|
||||
# }
|
||||
|
||||
# s.public_header_files = 'Pod/Classes/**/*.h'
|
||||
# s.frameworks = 'UIKit', 'MapKit'
|
||||
# s.dependency 'AFNetworking', '~> 2.3'
|
||||
end
|
||||
|
||||
@@ -8,51 +8,62 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct AIConfiguration {
|
||||
|
||||
// BoardRater - Count Pieces
|
||||
var boardRaterCountPiecesWeighting: Double!
|
||||
public struct AIConfiguration {
|
||||
|
||||
// BoardRater - Board Dominance
|
||||
var boardRaterBoardDominanceWeighting: Double!
|
||||
|
||||
// BoardRater - Center Ownership
|
||||
var boardRaterCenterOwnershipWeighting: Double!
|
||||
|
||||
// BoardRater - Center Dominance
|
||||
var boardRaterCenterDominanceWeighting: Double!
|
||||
|
||||
// BoardRater - Threatened Pieces
|
||||
var boardRaterThreatenedPiecesWeighting: Double!
|
||||
var boardRaterThreatenedPiecesOwnPiecesMultiplier: Double!
|
||||
|
||||
// BoardRater - Pawn Progression
|
||||
var boardRaterPawnProgressionWeighting: Double!
|
||||
|
||||
// BoardRater - King Surrounding Possession
|
||||
var boardRaterKingSurroundingPossessionWeighting: Double!
|
||||
|
||||
// BoardRater - Check Mate Opportunity
|
||||
var boardRaterCheckMateOpportunityWeighting: Double!
|
||||
|
||||
// BoardRater - Center Four Occupation
|
||||
var boardRaterCenterFourOccupationWeighting: Double!
|
||||
|
||||
init() {
|
||||
setDefualtValues()
|
||||
public enum Difficulty: Int {
|
||||
case easy
|
||||
case medium
|
||||
case hard
|
||||
}
|
||||
|
||||
mutating func setDefualtValues() {
|
||||
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 = 0.3
|
||||
boardRaterCheckMateOpportunityWeighting = 2
|
||||
struct ConfigurationValue {
|
||||
let easyValue: Double
|
||||
let difficultValue: Double
|
||||
let multiplier: Double
|
||||
|
||||
var value: Double {
|
||||
return easyValue + ((difficultValue - easyValue) * multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
public let difficulty: Difficulty!
|
||||
var suicideMultipler: ConfigurationValue!
|
||||
var boardRaterCountPiecesWeighting: ConfigurationValue!
|
||||
var boardRaterBoardDominanceWeighting: ConfigurationValue!
|
||||
var boardRaterCenterOwnershipWeighting: ConfigurationValue!
|
||||
var boardRaterCenterDominanceWeighting: ConfigurationValue!
|
||||
var boardRaterThreatenedPiecesWeighting: ConfigurationValue!
|
||||
var boardRaterPawnProgressionWeighting: ConfigurationValue!
|
||||
var boardRaterKingSurroundingPossessionWeighting: ConfigurationValue!
|
||||
var boardRaterCheckMateOpportunityWeighting: ConfigurationValue!
|
||||
var boardRaterCenterFourOccupationWeighting: ConfigurationValue!
|
||||
|
||||
public init(difficulty: Difficulty) {
|
||||
|
||||
self.difficulty = difficulty
|
||||
|
||||
let multiplier: Double
|
||||
|
||||
switch difficulty {
|
||||
case .easy: multiplier = 0
|
||||
case .medium: multiplier = 0.5
|
||||
case .hard: multiplier = 1
|
||||
}
|
||||
|
||||
func makeValue(_ easyValue: Double, _ hardValue: Double) -> ConfigurationValue {
|
||||
return ConfigurationValue(easyValue: easyValue, difficultValue: hardValue, multiplier: multiplier)
|
||||
}
|
||||
|
||||
suicideMultipler = makeValue(0, 0)
|
||||
boardRaterCountPiecesWeighting = makeValue(3, 3)
|
||||
boardRaterBoardDominanceWeighting = makeValue(0, 0.1)
|
||||
boardRaterCenterOwnershipWeighting = makeValue(0.1, 0.3)
|
||||
boardRaterCenterDominanceWeighting = makeValue(0, 0.3)
|
||||
boardRaterThreatenedPiecesWeighting = makeValue(0, 1.5)
|
||||
boardRaterPawnProgressionWeighting = makeValue(0.1, 1)
|
||||
boardRaterKingSurroundingPossessionWeighting = makeValue(0, 0.3)
|
||||
boardRaterCheckMateOpportunityWeighting = makeValue(0, 2)
|
||||
boardRaterCenterFourOccupationWeighting = makeValue(0.1, 0.3)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ import Foundation
|
||||
|
||||
open class AIPlayer : Player {
|
||||
|
||||
let boardRaters : [BoardRater]!
|
||||
let configuration = AIConfiguration() // <-- We should pass this in eventually
|
||||
let boardRaters: [BoardRater]!
|
||||
public var configuration: AIConfiguration!
|
||||
var openingMoves = [OpeningMove]()
|
||||
|
||||
public init(color: Color){
|
||||
public init(color: Color, configuration: AIConfiguration){
|
||||
|
||||
self.configuration = configuration
|
||||
|
||||
self.boardRaters = [
|
||||
BoardRaterCountPieces(configuration: configuration),
|
||||
@@ -35,17 +37,29 @@ open class AIPlayer : Player {
|
||||
self.color = color
|
||||
}
|
||||
|
||||
public func makeMove() {
|
||||
public func makeMoveAsync() {
|
||||
|
||||
print("\n\n****** Make Move ******");
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
self.makeMoveSync()
|
||||
}
|
||||
}
|
||||
|
||||
public func makeMoveSync() {
|
||||
|
||||
//print("\n\n****** Make Move ******");
|
||||
|
||||
// Check that the game is in progress
|
||||
guard game.state == .inProgress else {
|
||||
return
|
||||
}
|
||||
|
||||
let board = game.board
|
||||
|
||||
|
||||
var move: Move!
|
||||
|
||||
// Get an opening move
|
||||
if let openingMove = openingMoveForBoard(board){
|
||||
print("Playing opening move")
|
||||
//print("Playing opening move")
|
||||
move = openingMove
|
||||
}
|
||||
// Or, get the Highest rated move
|
||||
@@ -59,10 +73,10 @@ open class AIPlayer : Player {
|
||||
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))");
|
||||
//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");
|
||||
//print("Chose Castling move");
|
||||
}
|
||||
|
||||
// Promote pawns
|
||||
@@ -76,7 +90,11 @@ open class AIPlayer : Player {
|
||||
operations.append(transformOperation)
|
||||
}
|
||||
|
||||
self.game.playerDidMakeMove(player: self, boardOperations: operations)
|
||||
|
||||
let strongGame = self.game!
|
||||
DispatchQueue.main.async {
|
||||
strongGame.playerDidMakeMove(player: self, boardOperations: operations)
|
||||
}
|
||||
}
|
||||
|
||||
func openingMoveForBoard(_ board: Board) -> Move? {
|
||||
@@ -85,11 +103,14 @@ open class AIPlayer : Player {
|
||||
$0.board == board
|
||||
}
|
||||
|
||||
//print("Num opening moves`; \(possibleMoves.count)")
|
||||
|
||||
guard possibleMoves.count > 0 else{
|
||||
return nil
|
||||
}
|
||||
|
||||
let openingMove = possibleMoves[Int(arc4random()) % possibleMoves.count]
|
||||
let index = Int(arc4random_uniform(UInt32(possibleMoves.count)))
|
||||
let openingMove = possibleMoves[index]
|
||||
|
||||
return Move(type: .singlePiece(sourceLocation: openingMove.fromLocation,
|
||||
targetLocation: openingMove.toLocation),
|
||||
@@ -128,12 +149,17 @@ open class AIPlayer : Player {
|
||||
}
|
||||
|
||||
// Rate
|
||||
print("(\(sourceLocation.x),\(sourceLocation.y)) -> (\(targetLocation.x),\(targetLocation.y))")
|
||||
let rating = ratingForBoard(resultBoard)
|
||||
var rating = ratingForBoard(resultBoard)
|
||||
|
||||
// reduce rating if suicide
|
||||
if resultBoard.canColorMoveAnyPieceToLocation(color: color.opposite(), location: targetLocation) {
|
||||
rating -= (abs(rating) * configuration.suicideMultipler.value);
|
||||
}
|
||||
|
||||
let move = Move(type: .singlePiece(sourceLocation: sourceLocation, targetLocation: targetLocation),
|
||||
rating: rating)
|
||||
possibleMoves.append(move)
|
||||
print("Rating: \(rating)")
|
||||
// print("Rating: \(rating)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +181,7 @@ open class AIPlayer : Player {
|
||||
possibleMoves.append(move)
|
||||
}
|
||||
|
||||
print("Found \(possibleMoves.count) possible moves")
|
||||
//print("Found \(possibleMoves.count) possible moves")
|
||||
|
||||
// If there are no possible moves, we must be in stale mate
|
||||
if possibleMoves.count == 0 {
|
||||
@@ -200,8 +226,8 @@ open class AIPlayer : Player {
|
||||
|
||||
let result = boardRater.ratingfor(board: board, color: color)
|
||||
|
||||
let className = "\(boardRater)"
|
||||
print("\t\(className): \(result)")
|
||||
//let className = "\(boardRater)"
|
||||
//print("\t\(className): \(result)")
|
||||
rating += result
|
||||
}
|
||||
|
||||
@@ -240,12 +266,13 @@ open class AIPlayer : Player {
|
||||
var newBoard = board
|
||||
|
||||
guard let piece = newBoard.getPiece(at: location) else {
|
||||
|
||||
return board
|
||||
}
|
||||
|
||||
let newPiece = newBoard.getPiece(at: location)?.byChangingType(newType: pieceType)
|
||||
newBoard.squares[location.index].piece = newPiece
|
||||
|
||||
newBoard.setPiece(newPiece!, at: location)
|
||||
|
||||
let rating = ratingForBoard(newBoard)
|
||||
|
||||
if rating > bestRating {
|
||||
|
||||
@@ -66,14 +66,17 @@ public struct ASCIIBoard {
|
||||
var board = Board(state: .empty)
|
||||
|
||||
// Clear all pieces on the board
|
||||
(0..<64).forEach{
|
||||
board.squares[$0].piece = nil;
|
||||
BoardLocation.all.forEach{
|
||||
board.removePiece(atLocation: $0)
|
||||
}
|
||||
|
||||
// Setup pieces from ascii art
|
||||
(0..<64).forEach{
|
||||
let character = boardArt[boardArt.characters.index(boardArt.startIndex, offsetBy: $0)]
|
||||
board.squares[$0].piece = pieceFromCharacter(character)
|
||||
|
||||
if let piece = pieceFromCharacter(character) {
|
||||
board.setPiece(piece, at: BoardLocation(index: $0))
|
||||
}
|
||||
}
|
||||
|
||||
return board
|
||||
|
||||
@@ -27,9 +27,9 @@ public func ==(lhs: Square, rhs: Square) -> Bool {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case (.some, .none):
|
||||
return true
|
||||
return false
|
||||
case (.none, .some):
|
||||
return true
|
||||
return false
|
||||
case (.some(let rp), .some(let lp)):
|
||||
return rp == lp
|
||||
}
|
||||
@@ -37,14 +37,14 @@ public func ==(lhs: Square, rhs: Square) -> Bool {
|
||||
|
||||
// MARK: - ****** Board ******
|
||||
|
||||
public struct Board {
|
||||
public struct Board : Equatable {
|
||||
|
||||
public enum InitialState {
|
||||
case empty
|
||||
case newGame
|
||||
}
|
||||
|
||||
public var squares = [Square]()
|
||||
public private(set) var squares = [Square]()
|
||||
|
||||
// MARK: - Init
|
||||
public init(state: InitialState) {
|
||||
@@ -63,48 +63,56 @@ public struct Board {
|
||||
|
||||
mutating func setupForNewGame() {
|
||||
|
||||
// Setup white bottom row
|
||||
squares[0].piece = Piece(type: .rook, color: .white)
|
||||
squares[1].piece = Piece(type: .knight, color: .white)
|
||||
squares[2].piece = Piece(type: .bishop, color: .white)
|
||||
squares[3].piece = Piece(type: .queen, color: .white)
|
||||
squares[4].piece = Piece(type: .king, color: .white)
|
||||
squares[5].piece = Piece(type: .bishop, color: .white)
|
||||
squares[6].piece = Piece(type: .knight, color: .white)
|
||||
squares[7].piece = Piece(type: .rook, color: .white)
|
||||
func setPieceAtIndex(_ piece: Piece, _ index: Int){
|
||||
setPiece(piece, at: BoardLocation(index: index))
|
||||
}
|
||||
|
||||
// Setup white bottom row
|
||||
setPieceAtIndex(Piece(type: .rook, color: .white), 0)
|
||||
setPieceAtIndex(Piece(type: .knight, color: .white), 1)
|
||||
setPieceAtIndex(Piece(type: .bishop, color: .white), 2)
|
||||
setPieceAtIndex(Piece(type: .queen, color: .white), 3)
|
||||
setPieceAtIndex(Piece(type: .king, color: .white), 4)
|
||||
setPieceAtIndex(Piece(type: .bishop, color: .white), 5)
|
||||
setPieceAtIndex(Piece(type: .knight, color: .white), 6)
|
||||
setPieceAtIndex(Piece(type: .rook, color: .white), 7)
|
||||
|
||||
// Setup white pawn row
|
||||
for i in 8...15 {
|
||||
squares[i].piece = Piece(type: .pawn, color: .white)
|
||||
setPieceAtIndex(Piece(type: .pawn, color: .white), i)
|
||||
}
|
||||
|
||||
// Setup black bottom row
|
||||
squares[63].piece = Piece(type: .rook, color: .black)
|
||||
squares[62].piece = Piece(type: .knight, color: .black)
|
||||
squares[61].piece = Piece(type: .bishop, color: .black)
|
||||
squares[60].piece = Piece(type: .queen, color: .black)
|
||||
squares[59].piece = Piece(type: .king, color: .black)
|
||||
squares[58].piece = Piece(type: .bishop, color: .black)
|
||||
squares[57].piece = Piece(type: .knight, color: .black)
|
||||
squares[56].piece = Piece(type: .rook, color: .black)
|
||||
setPieceAtIndex(Piece(type: .rook, color: .black), 63)
|
||||
setPieceAtIndex(Piece(type: .knight, color: .black), 62)
|
||||
setPieceAtIndex(Piece(type: .bishop, color: .black), 61)
|
||||
setPieceAtIndex(Piece(type: .king, color: .black), 60)
|
||||
setPieceAtIndex(Piece(type: .queen, color: .black), 59)
|
||||
setPieceAtIndex(Piece(type: .bishop, color: .black), 58)
|
||||
setPieceAtIndex(Piece(type: .knight, color: .black), 57)
|
||||
setPieceAtIndex(Piece(type: .rook, color: .black), 56)
|
||||
|
||||
// Setup black pawn row
|
||||
for i in 48...55 {
|
||||
squares[i].piece = Piece(type: .pawn, color: .black)
|
||||
setPieceAtIndex(Piece(type: .pawn, color: .black), i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Manipulate Pieces
|
||||
|
||||
public mutating func setPiece(_ piece: Piece, at location: BoardLocation) {
|
||||
squares[location.index].piece = piece
|
||||
squares[location.index].piece?.location = location
|
||||
}
|
||||
|
||||
public func getPiece(at location: BoardLocation) -> Piece? {
|
||||
return squares[location.index].piece
|
||||
}
|
||||
|
||||
public mutating func removePiece(atLocation location: BoardLocation) {
|
||||
squares[location.index].piece = nil
|
||||
}
|
||||
|
||||
@discardableResult internal mutating func movePiece(fromLocation: BoardLocation, toLocation: BoardLocation) -> [BoardOperation] {
|
||||
|
||||
if toLocation == fromLocation {
|
||||
@@ -113,7 +121,7 @@ public struct Board {
|
||||
|
||||
var operations = [BoardOperation]()
|
||||
|
||||
guard var movingPiece = getPiece(at: fromLocation) else {
|
||||
guard let movingPiece = getPiece(at: fromLocation) else {
|
||||
fatalError("There is no piece on at (\(fromLocation.x),\(fromLocation.y))")
|
||||
}
|
||||
|
||||
@@ -126,6 +134,7 @@ public struct Board {
|
||||
}
|
||||
|
||||
squares[toLocation.index].piece = self.squares[fromLocation.index].piece
|
||||
squares[toLocation.index].piece?.location = toLocation;
|
||||
squares[toLocation.index].piece?.hasMoved = true
|
||||
squares[fromLocation.index].piece = nil
|
||||
|
||||
@@ -470,16 +479,16 @@ public struct Board {
|
||||
rookEndXPos = 3
|
||||
case (.black, .kingSide):
|
||||
yPos = 7
|
||||
kingStartXPos = 3
|
||||
rookStartXPos = 0
|
||||
kingEndXPos = 1
|
||||
rookEndXPos = 2
|
||||
kingStartXPos = 4
|
||||
rookStartXPos = 7
|
||||
kingEndXPos = 6
|
||||
rookEndXPos = 5
|
||||
case (.black, .queenSide):
|
||||
yPos = 7
|
||||
kingStartXPos = 3
|
||||
rookStartXPos = 7
|
||||
kingEndXPos = 5
|
||||
rookEndXPos = 4
|
||||
kingStartXPos = 4
|
||||
rookStartXPos = 0
|
||||
kingEndXPos = 2
|
||||
rookEndXPos = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,26 @@ public struct BoardLocation : Equatable {
|
||||
|
||||
public var index: Int
|
||||
|
||||
private static var allLocationsBacking: [BoardLocation]?
|
||||
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))
|
||||
if let all = allLocationsBacking {
|
||||
return all
|
||||
}
|
||||
|
||||
return locations
|
||||
else{
|
||||
var locations = [BoardLocation]()
|
||||
|
||||
(0..<64).forEach{
|
||||
locations.append(BoardLocation(index: $0))
|
||||
}
|
||||
|
||||
allLocationsBacking = locations
|
||||
return allLocationsBacking!
|
||||
}
|
||||
}
|
||||
|
||||
public var isDarkSquare: Bool {
|
||||
return (index + y) % 2 == 0
|
||||
}
|
||||
|
||||
public var x: Int {
|
||||
|
||||
@@ -31,7 +31,7 @@ class BoardRaterBoardDominance : BoardRater {
|
||||
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterBoardDominanceWeighting;
|
||||
return rating * configuration.boardRaterBoardDominanceWeighting.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class BoardRaterCenterDominance : BoardRater {
|
||||
}
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterCenterDominanceWeighting
|
||||
return rating * configuration.boardRaterCenterDominanceWeighting.value
|
||||
}
|
||||
|
||||
func dominanceValueFor(location: BoardLocation) -> Double {
|
||||
|
||||
@@ -32,6 +32,6 @@ class BoardRaterCenterFourOccupation: BoardRater {
|
||||
rating += piece.color == color ? value : -value
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterCenterFourOccupationWeighting
|
||||
return rating * configuration.boardRaterCenterFourOccupationWeighting.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class BoardRaterCenterOwnership : BoardRater {
|
||||
rating += (piece.color == color) ? distance : -distance
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterCenterOwnershipWeighting
|
||||
return rating * configuration.boardRaterCenterOwnershipWeighting.value
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class BoardRaterCheckMateOpportunity : BoardRater {
|
||||
}
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterCheckMateOpportunityWeighting
|
||||
return rating * configuration.boardRaterCheckMateOpportunityWeighting.value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class BoardRaterCountPieces : BoardRater {
|
||||
rating += piece.color == color ? piece.value() : -piece.value()
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterCountPiecesWeighting
|
||||
return rating * configuration.boardRaterCountPiecesWeighting.value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ class BoardRaterKingSurroundingPossession : BoardRater {
|
||||
|
||||
// The kings will be able to move to their surrounding locations, so remove them from the board
|
||||
var noKingsBoard = board
|
||||
noKingsBoard.squares[noKingsBoard.getKingLocation(color: .white).index].piece = nil
|
||||
noKingsBoard.squares[noKingsBoard.getKingLocation(color: .black).index].piece = nil
|
||||
noKingsBoard.removePiece(atLocation: noKingsBoard.getKingLocation(color: .white))
|
||||
noKingsBoard.removePiece(atLocation: noKingsBoard.getKingLocation(color: .black))
|
||||
|
||||
// we don't want to encourage the king to move out in to the open
|
||||
rating += Double(8 - ownKingLocations.count) * squareValue * 3
|
||||
|
||||
for location in ownKingLocations {
|
||||
|
||||
@@ -66,7 +69,7 @@ class BoardRaterKingSurroundingPossession : BoardRater {
|
||||
}
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterKingSurroundingPossessionWeighting
|
||||
return rating * configuration.boardRaterKingSurroundingPossessionWeighting.value
|
||||
}
|
||||
|
||||
func locationsSurroundingKing(color: Color, board: Board) -> [BoardLocation] {
|
||||
|
||||
@@ -52,7 +52,7 @@ class BoardRaterPawnProgression : BoardRater {
|
||||
squaresAdvanced = 7 - (location.y + 2)
|
||||
}
|
||||
|
||||
return Double(squaresAdvanced) * configuration.boardRaterPawnProgressionWeighting // <- should probably add some sort of curve
|
||||
return Double(squaresAdvanced) * configuration.boardRaterPawnProgressionWeighting.value // <- should probably add some sort of curve
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,126 +12,243 @@ import Foundation
|
||||
|
||||
class BoardRaterThreatenedPieces : BoardRater {
|
||||
|
||||
/*
|
||||
override func ratingfor(board: Board, color: Color) -> Double {
|
||||
|
||||
var rating = Double(0)
|
||||
|
||||
for location in BoardLocation.all {
|
||||
|
||||
let allPieces = board.squares.flatMap{
|
||||
$0.piece
|
||||
}
|
||||
|
||||
for piece in allPieces {
|
||||
|
||||
guard let piece = board.getPiece(at: location) else {
|
||||
continue;
|
||||
var value = Double(0)
|
||||
|
||||
let threatenedByPieces = getPieces(threatening: piece, onBoard: board)
|
||||
let protectedByPieces = getPieces(protecting: piece, onBoard: board)
|
||||
let isThreatened = threatenedByPieces.count > 0
|
||||
let isProtected = protectedByPieces.count > 0
|
||||
|
||||
switch (isThreatened, isProtected) {
|
||||
case (false, true):
|
||||
value = piece.value()
|
||||
case (true, false):
|
||||
value = -piece.value()
|
||||
case (true, true):
|
||||
let lowestValueThreat = threatenedByPieces.dropFirst().reduce(threatenedByPieces[0].value(), { (value: Double, piece) -> Double in
|
||||
return min(value, piece.value())
|
||||
})
|
||||
|
||||
if lowestValueThreat < piece.value() {
|
||||
value = -piece.value()
|
||||
}
|
||||
case (false, false):
|
||||
break
|
||||
}
|
||||
|
||||
let threatRating = threatRatingForPiece(at: location, board: board, color: color)
|
||||
|
||||
// For a same color, subtract the threat rating (less preferrable move)
|
||||
if piece.color == color {
|
||||
rating -= threatRating * configuration.boardRaterThreatenedPiecesOwnPiecesMultiplier
|
||||
}
|
||||
// For opposite color, add the treat rating (more preferable move)
|
||||
else{
|
||||
rating += threatRating
|
||||
if piece.color == color.opposite() {
|
||||
value = -value
|
||||
}
|
||||
|
||||
rating += (piece.color == color) ? -threatRating : threatRating;
|
||||
rating += value
|
||||
}
|
||||
|
||||
return rating * configuration.boardRaterThreatenedPiecesWeighting
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Returns a more positive rating the more the piece is threatened
|
||||
func threatRatingForPiece(at location: BoardLocation, board: Board, color: Color) -> Double {
|
||||
override func ratingfor(board: Board, color: Color) -> Double {
|
||||
|
||||
guard let piece = board.getPiece(at: location) else{
|
||||
fatalError()
|
||||
}
|
||||
|
||||
var rating = Double(0)
|
||||
|
||||
let threateningPieceLocations = threateningPiecesLocationsforPiece(at: location, on: board)
|
||||
let isBeingThreatened = threateningPieceLocations.isEmpty ? false : true
|
||||
|
||||
let protectingPieceLocations = protectingPiecesLocationsforPiece(at: location, on: board)
|
||||
let isBeingProtected = protectingPieceLocations.isEmpty ? false : true
|
||||
|
||||
for threateningPieceLocation in threateningPieceLocations {
|
||||
|
||||
guard let threateningPiece = board.getPiece(at: threateningPieceLocation) else {
|
||||
continue
|
||||
}
|
||||
|
||||
let pieceIsProtected = (isBeingProtected && piece.value() < threateningPiece.value())
|
||||
|
||||
if !pieceIsProtected {
|
||||
let rating = board.getPieces(color: color)
|
||||
.map{ threatValue(forPiece: $0, onBoard: board) }
|
||||
.reduce(0,+)
|
||||
* configuration.boardRaterThreatenedPiecesWeighting.value
|
||||
|
||||
rating += piece.value()
|
||||
}
|
||||
}
|
||||
|
||||
return rating
|
||||
}
|
||||
|
||||
// MARK - Get protecting / Threatening pieces
|
||||
|
||||
func protectingPiecesLocationsforPiece(at location: BoardLocation, on board: Board) -> [BoardLocation] {
|
||||
func threatValue(forPiece piece: Piece, onBoard board: Board) -> Double {
|
||||
|
||||
var newBoard = board
|
||||
let threatenedByPieces = getPieces(threatening: piece, onBoard: board)
|
||||
let protectedByPieces = getPieces(protecting: piece, onBoard: board)
|
||||
let isThreatened = threatenedByPieces.count > 0
|
||||
let isProtected = protectedByPieces.count > 0
|
||||
|
||||
guard let protectedPiece = board.getPiece(at: location) else {
|
||||
fatalError("Expected board location to contain piece")
|
||||
// Threatened but not protected
|
||||
if isThreatened && !isProtected {
|
||||
return -piece.value() * 3
|
||||
}
|
||||
|
||||
// Change the piece color to be opposite, to simulate if the piece was taken
|
||||
let oppositeColorPiece = Piece(type: protectedPiece.type, color: protectedPiece.color.opposite())
|
||||
newBoard.setPiece(oppositeColorPiece, at: location)
|
||||
|
||||
var pieces = [BoardLocation]()
|
||||
|
||||
for sourceLocation in BoardLocation.all {
|
||||
// Threatened, but protected (only return if the trade is not preferable)
|
||||
if isThreatened && isProtected {
|
||||
|
||||
guard let protectingPiece = newBoard.getPiece(at: sourceLocation) else{
|
||||
continue
|
||||
let lowestValueThreat = threatenedByPieces.lowestPieceValue()
|
||||
|
||||
if lowestValueThreat < piece.value() {
|
||||
return -piece.value()
|
||||
}
|
||||
|
||||
guard protectingPiece.color == protectedPiece.color else{
|
||||
continue
|
||||
}
|
||||
// Here we could bump the value to encourage a good trade?
|
||||
}
|
||||
|
||||
let targetPieces = getPieces(threatenedBy: piece, onBoard: board)
|
||||
for targetPiece in targetPieces {
|
||||
|
||||
if protectingPiece.movement.canPieceMove(fromLocation: sourceLocation, toLocation: location, board: newBoard) {
|
||||
pieces.append(sourceLocation)
|
||||
let isTargetProtected = isPieceProtected(targetPiece, onBoard: board)
|
||||
|
||||
// If it's protected, is it a good trade
|
||||
if isTargetProtected && targetPiece.value() < piece.value() {
|
||||
return 0
|
||||
}
|
||||
else{
|
||||
return targetPiece.value()
|
||||
}
|
||||
}
|
||||
|
||||
return pieces
|
||||
// Nothing much interesting
|
||||
return 0
|
||||
}
|
||||
|
||||
func threateningPiecesLocationsforPiece(at location: BoardLocation, on board: Board) -> [BoardLocation] {
|
||||
// MARK: - Helpers
|
||||
|
||||
func getPieces(protecting piece: Piece, onBoard board: Board) -> [Piece] {
|
||||
|
||||
guard let threatenedPiece = board.getPiece(at: location) else {
|
||||
fatalError("Expected board location to contain piece")
|
||||
var alteredBoard = board
|
||||
alteredBoard.setPiece(piece.withOppositeColor(), at: piece.location)
|
||||
|
||||
return alteredBoard.getPieces(color: piece.color).filter{
|
||||
$0.movement.canPieceMove(fromLocation: $0.location, toLocation: piece.location, board: alteredBoard, accountForCheckState: true)
|
||||
}
|
||||
|
||||
let threatenedColor = threatenedPiece.color
|
||||
|
||||
var pieces = [BoardLocation]()
|
||||
|
||||
for sourceLocation in BoardLocation.all {
|
||||
|
||||
guard let threateningPiece = board.getPiece(at: sourceLocation) else{
|
||||
continue
|
||||
}
|
||||
|
||||
guard threateningPiece.color == threatenedColor.opposite() else{
|
||||
continue
|
||||
}
|
||||
|
||||
if threateningPiece.movement.canPieceMove(fromLocation: sourceLocation, toLocation: location, board: board) {
|
||||
pieces.append(sourceLocation)
|
||||
}
|
||||
}
|
||||
|
||||
return pieces
|
||||
}
|
||||
|
||||
func getPieces(protectedBy piece: Piece, onBoard board: Board) -> [Piece] {
|
||||
|
||||
return board.getPieces(color: piece.color).filter{
|
||||
piece.movement.canPieceMove(fromLocation: piece.location, toLocation: $0.location, board: board, accountForCheckState: true)
|
||||
}
|
||||
}
|
||||
|
||||
func isPieceProtected(_ piece: Piece, onBoard board: Board) -> Bool {
|
||||
|
||||
var alteredBoard = board
|
||||
alteredBoard.setPiece(piece.withOppositeColor(), at: piece.location)
|
||||
|
||||
for square in alteredBoard.squares {
|
||||
|
||||
guard let squarePiece = square.piece else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard squarePiece.color == piece.color else {
|
||||
continue
|
||||
}
|
||||
|
||||
if squarePiece.movement.canPieceMove(fromLocation: squarePiece.location,
|
||||
toLocation: piece.location,
|
||||
board: alteredBoard,
|
||||
accountForCheckState: true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isPieceThreatened(_ piece: Piece, onBoard board: Board) -> Bool {
|
||||
|
||||
for square in board.squares {
|
||||
|
||||
guard let squarePiece = square.piece else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard squarePiece.color == piece.color.opposite() else {
|
||||
continue
|
||||
}
|
||||
|
||||
if squarePiece.movement.canPieceMove(fromLocation: squarePiece.location, toLocation: piece.location, board: board, accountForCheckState: true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func getPieces(threatening piece: Piece, onBoard board: Board) -> [Piece] {
|
||||
|
||||
return board.getPieces(color: piece.color.opposite()).filter{
|
||||
$0.movement.canPieceMove(fromLocation: $0.location, toLocation: piece.location, board: board, accountForCheckState: true)
|
||||
}
|
||||
}
|
||||
|
||||
func getPieces(threatenedBy piece: Piece, onBoard board: Board) -> [Piece] {
|
||||
|
||||
return board.getPieces(color: piece.color.opposite()).filter{
|
||||
piece.movement.canPieceMove(fromLocation: piece.location, toLocation: $0.location, board: board, accountForCheckState: true)
|
||||
}
|
||||
}
|
||||
|
||||
func canPieceMoveToSafety(_ piece: Piece, onBoard board: Board) -> Bool {
|
||||
|
||||
for location in BoardLocation.all {
|
||||
|
||||
if piece.movement.canPieceMove(fromLocation: piece.location, toLocation: location, board: board, accountForCheckState: true) {
|
||||
|
||||
var boardCopy = board
|
||||
boardCopy.movePiece(fromLocation: piece.location, toLocation: location)
|
||||
let movedPiece = boardCopy.getPiece(at: location)!
|
||||
if !isPieceThreatened(movedPiece, onBoard: boardCopy) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection where Iterator.Element == Piece{
|
||||
|
||||
func lowestPieceValue() -> Double {
|
||||
|
||||
if self.count == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var result = self.first!.value()
|
||||
|
||||
for piece in self {
|
||||
|
||||
let pieceValue = piece.value()
|
||||
|
||||
if pieceValue < result {
|
||||
result = pieceValue
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func highestPieceValue() -> Double {
|
||||
|
||||
if self.count == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var result = self.first!.value()
|
||||
|
||||
for piece in self {
|
||||
|
||||
let pieceValue = piece.value()
|
||||
|
||||
if pieceValue > result {
|
||||
result = pieceValue
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ open class Game {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum GameType {
|
||||
case humanVsHuman
|
||||
case humanVsComputer
|
||||
case computerVsComputer
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
open var board = Board(state: .newGame)
|
||||
@@ -38,6 +44,18 @@ open class Game {
|
||||
open var state = Game.State.inProgress
|
||||
open weak var delegate: GameDelegate?
|
||||
|
||||
public var gameType: GameType {
|
||||
|
||||
switch (self.whitePlayer, self.blackPlayer) {
|
||||
case (is Human, is Human):
|
||||
return .humanVsHuman
|
||||
case (is AIPlayer, is AIPlayer):
|
||||
return .computerVsComputer
|
||||
default:
|
||||
return .humanVsComputer
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Init
|
||||
public init(firstPlayer: Player, secondPlayer: Player, board: Board = Board(state: .newGame), colorToMove: Color = .white){
|
||||
|
||||
@@ -72,6 +90,9 @@ extension Game : PlayerDelegate {
|
||||
print("Warning - Wrong player took turn")
|
||||
}
|
||||
|
||||
// Tell delegate we will begin updates
|
||||
delegate?.gameWillBeginUpdates(game: self)
|
||||
|
||||
// Process board operations
|
||||
processBoardOperations(boardOperations: boardOperations)
|
||||
|
||||
@@ -89,6 +110,9 @@ extension Game : PlayerDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
// Tell the delegate that we've ended updates
|
||||
delegate?.gameDidEndUpdates(game: self)
|
||||
|
||||
// Switch to the other player
|
||||
if player === whitePlayer {
|
||||
currentPlayer = blackPlayer
|
||||
@@ -98,6 +122,7 @@ extension Game : PlayerDelegate {
|
||||
}
|
||||
|
||||
self.delegate?.gameDidChangeCurrentPlayer(game: self)
|
||||
|
||||
}
|
||||
|
||||
func processBoardOperations(boardOperations: [BoardOperation]) {
|
||||
|
||||
@@ -19,6 +19,11 @@ open class Human : Player {
|
||||
|
||||
public func movePiece(fromLocation: BoardLocation, toLocation: BoardLocation) throws {
|
||||
|
||||
// Check that the game is in progress
|
||||
guard game.state == .inProgress else {
|
||||
throw MoveError.gameIsNotInProgress
|
||||
}
|
||||
|
||||
// Check that we're the current player
|
||||
guard game.currentPlayer === self else {
|
||||
throw MoveError.notThisPlayersTurn
|
||||
@@ -47,7 +52,7 @@ open class Human : Player {
|
||||
|
||||
// Change the piece
|
||||
let newPiece = self.game.board.squares[pawnLocation.index].piece?.byChangingType(newType: $0)
|
||||
self.game.board.squares[pawnLocation.index].piece = newPiece
|
||||
self.game.board.setPiece(newPiece!, at: pawnLocation)
|
||||
|
||||
// Add a transform piece operation
|
||||
let modifyOperation = BoardOperation(type: .transformPiece, piece: newPiece!, location: pawnLocation)
|
||||
|
||||
@@ -51,6 +51,9 @@ class Opening {
|
||||
|
||||
board.movePiece(fromLocation: locations.fromLocation,
|
||||
toLocation: locations.toLocation)
|
||||
|
||||
//move.board.printBoardPieces()
|
||||
|
||||
}
|
||||
|
||||
// Filter for color
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum Color {
|
||||
public enum Color: Int {
|
||||
case white
|
||||
case black
|
||||
|
||||
@@ -20,13 +20,16 @@ public enum Color {
|
||||
return (self == .white) ? "white" : "black"
|
||||
}
|
||||
|
||||
public func toStringWithCapital() -> String {
|
||||
return (self == .white) ? "White" : "Black"
|
||||
}
|
||||
}
|
||||
|
||||
public struct Piece : Equatable {
|
||||
|
||||
static private var lastAssignedTag = 0
|
||||
|
||||
public enum PieceType {
|
||||
public enum PieceType: Int {
|
||||
case pawn
|
||||
case rook
|
||||
case knight
|
||||
@@ -44,8 +47,9 @@ public struct Piece : Equatable {
|
||||
public var tag: Int!
|
||||
public var hasMoved = false
|
||||
public var canBeTakenByEnPassant = false
|
||||
public internal(set) var location = BoardLocation(index: 0)
|
||||
|
||||
var movement : PieceMovement {
|
||||
var movement : PieceMovement! {
|
||||
return PieceMovement.pieceMovementForPieceType(pieceType: self.type)
|
||||
}
|
||||
|
||||
@@ -81,6 +85,10 @@ public struct Piece : Equatable {
|
||||
let piece = Piece(type: newType, color: color, tag: tag)
|
||||
return piece
|
||||
}
|
||||
|
||||
func withOppositeColor() -> Piece {
|
||||
return Piece(type: type, color: color.opposite())
|
||||
}
|
||||
}
|
||||
|
||||
public func == (left: Piece, right: Piece) -> Bool {
|
||||
|
||||
@@ -10,23 +10,30 @@ import Foundation
|
||||
|
||||
// MARK: - PieceMovement (Base Class)
|
||||
|
||||
let pawnMovement = PieceMovementPawn()
|
||||
let rookMovement = PieceMovementRook()
|
||||
let knightMovement = PieceMovementKnight()
|
||||
let bishopMovement = PieceMovementBishop()
|
||||
let queenMovement = PieceMovementQueen()
|
||||
let kingMovement = PieceMovementKing()
|
||||
|
||||
open class PieceMovement {
|
||||
|
||||
public class func pieceMovementForPieceType(pieceType: Piece.PieceType) -> PieceMovement {
|
||||
|
||||
switch pieceType {
|
||||
case .pawn:
|
||||
return PieceMovementPawn()
|
||||
return pawnMovement
|
||||
case .rook:
|
||||
return PieceMovementRook()
|
||||
return rookMovement
|
||||
case .knight:
|
||||
return PieceMovementKnight()
|
||||
return knightMovement
|
||||
case .bishop:
|
||||
return PieceMovementBishop()
|
||||
return bishopMovement
|
||||
case .queen:
|
||||
return PieceMovementQueen()
|
||||
return queenMovement
|
||||
case .king:
|
||||
return PieceMovementKing()
|
||||
return kingMovement
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +41,63 @@ open class PieceMovement {
|
||||
|
||||
}
|
||||
|
||||
open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board, accountForCheckState: Bool = false) -> Bool {
|
||||
|
||||
if fromLocation == toLocation {
|
||||
return false
|
||||
}
|
||||
|
||||
let canMove = isMovementPossible(fromLocation: fromLocation, toLocation: toLocation, board: board)
|
||||
|
||||
if canMove && accountForCheckState {
|
||||
|
||||
let color = board.getPiece(at: fromLocation)!.color
|
||||
|
||||
var boardCopy = board
|
||||
boardCopy.movePiece(fromLocation: fromLocation, toLocation: toLocation)
|
||||
return boardCopy.isColorInCheck(color: color) ? false : true
|
||||
}
|
||||
else{
|
||||
return canMove
|
||||
}
|
||||
}
|
||||
|
||||
func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board, stride: BoardStride) -> Bool {
|
||||
|
||||
enum Direction: Int{
|
||||
case increasing
|
||||
case decresing
|
||||
case none
|
||||
}
|
||||
|
||||
var strideDirectionX = Direction.none
|
||||
if stride.x < 0 { strideDirectionX = .decresing }
|
||||
if stride.x > 0 { strideDirectionX = .increasing }
|
||||
|
||||
var locationDirectionX = Direction.none
|
||||
if toLocation.x - fromLocation.x < 0 { locationDirectionX = .decresing }
|
||||
if toLocation.x - fromLocation.x > 0 { locationDirectionX = .increasing }
|
||||
|
||||
if strideDirectionX != locationDirectionX {
|
||||
return false
|
||||
}
|
||||
|
||||
var strideDirectionY = Direction.none
|
||||
if stride.y < 0 { strideDirectionY = .decresing }
|
||||
if stride.y > 0 { strideDirectionY = .increasing }
|
||||
|
||||
var locationDirectionY = Direction.none
|
||||
if toLocation.y - fromLocation.y < 0 { locationDirectionY = .decresing }
|
||||
if toLocation.y - fromLocation.y > 0 { locationDirectionY = .increasing }
|
||||
|
||||
if strideDirectionY != locationDirectionY {
|
||||
return false
|
||||
}
|
||||
|
||||
// Make sure cannot take king
|
||||
if let piece = board.getPiece(at: toLocation) {
|
||||
if piece.type == .king {
|
||||
@@ -48,9 +106,7 @@ open class PieceMovement {
|
||||
}
|
||||
|
||||
// Get the moving piece
|
||||
var movingPiece = board.getPiece(at: fromLocation)
|
||||
|
||||
if movingPiece == nil {
|
||||
guard let movingPiece = board.getPiece(at: fromLocation) else {
|
||||
print("Cannot from an index that does not contain a piece")
|
||||
return false
|
||||
}
|
||||
@@ -66,15 +122,15 @@ open class PieceMovement {
|
||||
// If there is a piece on the square
|
||||
if let piece = board.getPiece(at: testLocation) {
|
||||
|
||||
if piece.color == movingPiece!.color {
|
||||
if piece.color == movingPiece.color {
|
||||
return false
|
||||
}
|
||||
|
||||
if piece.color == movingPiece!.color.opposite() && testLocation == toLocation {
|
||||
if piece.color == movingPiece.color.opposite() && testLocation == toLocation {
|
||||
return true
|
||||
}
|
||||
|
||||
if piece.color == movingPiece!.color.opposite() && testLocation != toLocation {
|
||||
if piece.color == movingPiece.color.opposite() && testLocation != toLocation {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -109,16 +165,14 @@ open class PieceMovement {
|
||||
}
|
||||
|
||||
// Check if space is occupied
|
||||
var movingPiece = board.getPiece(at: pieceLocation)
|
||||
|
||||
if movingPiece == nil {
|
||||
print("Cannot from an index that does not contain a piece")
|
||||
guard let movingPiece = board.getPiece(at: pieceLocation) else {
|
||||
print("Cannot move from an index that does not contain a piece")
|
||||
return false
|
||||
}
|
||||
|
||||
if let otherPiece = board.getPiece(at: targetLocation) {
|
||||
|
||||
if otherPiece.color == movingPiece!.color {
|
||||
if otherPiece.color == movingPiece.color {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -132,14 +186,21 @@ open class PieceMovement {
|
||||
|
||||
open class PieceMovementStraightLine: PieceMovement {
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
let strides = [
|
||||
BoardStride(x: 0, y: -1 ), // Down
|
||||
BoardStride(x: 0, y: 1 ), // Up
|
||||
BoardStride(x: -1, y: 0 ), // Left
|
||||
BoardStride(x: 1, y: 0 ) // Right
|
||||
]
|
||||
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
let strides = [
|
||||
BoardStride(x: 0, y: -1 ), // Down
|
||||
BoardStride(x: 0, y: 1 ), // Up
|
||||
BoardStride(x: -1, y: 0 ), // Left
|
||||
BoardStride(x: 1, y: 0 ) // Right
|
||||
]
|
||||
let sameX = fromLocation.x == toLocation.x
|
||||
let sameY = fromLocation.y == toLocation.y
|
||||
|
||||
if !(sameX || sameY) {
|
||||
return false
|
||||
}
|
||||
|
||||
for stride in strides {
|
||||
if canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board, stride: stride) {
|
||||
@@ -156,14 +217,18 @@ open class PieceMovementStraightLine: PieceMovement {
|
||||
|
||||
open class PieceMovementDiagonal: PieceMovement {
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
let strides = [
|
||||
BoardStride(x: 1, y: -1 ), // South East
|
||||
BoardStride(x: -1, y: -1 ), // South West
|
||||
BoardStride(x: 1, y: 1 ), // North East
|
||||
BoardStride(x: -1, y: 1 ) // North West
|
||||
]
|
||||
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
let strides = [
|
||||
BoardStride(x: 1, y: -1 ), // South East
|
||||
BoardStride(x: -1, y: -1 ), // South West
|
||||
BoardStride(x: 1, y: 1 ), // North East
|
||||
BoardStride(x: -1, y: 1 ) // North West
|
||||
]
|
||||
if fromLocation.isDarkSquare != toLocation.isDarkSquare {
|
||||
return false
|
||||
}
|
||||
|
||||
for stride in strides {
|
||||
if canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board, stride: stride) {
|
||||
@@ -183,7 +248,7 @@ open class PieceMovementQueen: PieceMovement {
|
||||
|
||||
let movements : [PieceMovement] = [PieceMovementStraightLine(), PieceMovementDiagonal()]
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
for pieceMovement in movements {
|
||||
|
||||
@@ -203,12 +268,9 @@ open class PieceMovementRook: PieceMovement {
|
||||
|
||||
let straightLineMovement = PieceMovementStraightLine()
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
return straightLineMovement.canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board)
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,12 +280,9 @@ open class PieceMovementBishop: PieceMovement {
|
||||
|
||||
let diagonalMovement = PieceMovementDiagonal()
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
return diagonalMovement.canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board)
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +290,18 @@ open class PieceMovementBishop: PieceMovement {
|
||||
|
||||
open class PieceMovementKnight: PieceMovement {
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
let offsets: [(x: Int, y: Int)] = [
|
||||
(1,2),
|
||||
(2,1),
|
||||
(2,-1),
|
||||
(-2,1),
|
||||
(-1,-2),
|
||||
(-2,-1),
|
||||
(1,-2),
|
||||
(-1,2)
|
||||
]
|
||||
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
// Make sure cannot take king
|
||||
if let piece = board.getPiece(at: toLocation) {
|
||||
@@ -240,22 +310,12 @@ open class PieceMovementKnight: PieceMovement {
|
||||
}
|
||||
}
|
||||
|
||||
let offsets: [(x: Int, y: Int)] = [
|
||||
(1,2),
|
||||
(2,1),
|
||||
(2,-1),
|
||||
(-2,1),
|
||||
(-1,-2),
|
||||
(-2,-1),
|
||||
(1,-2),
|
||||
(-1,2)
|
||||
]
|
||||
|
||||
for offset in offsets {
|
||||
|
||||
let offsetLocation = fromLocation.incrementedBy(x: offset.x, y: offset.y)
|
||||
|
||||
if toLocation == offsetLocation && canPieceOccupySquare(pieceLocation: fromLocation, xOffset: offset.x, yOffset: offset.y, board: board) {
|
||||
if toLocation == offsetLocation
|
||||
&& canPieceOccupySquare(pieceLocation: fromLocation, xOffset: offset.x, yOffset: offset.y, board: board) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -269,7 +329,20 @@ open class PieceMovementKnight: PieceMovement {
|
||||
|
||||
open class PieceMovementPawn: PieceMovement {
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
// Get the moving piece
|
||||
guard let movingPiece = board.getPiece(at: fromLocation) else{
|
||||
return false
|
||||
}
|
||||
|
||||
if movingPiece.color == .white && toLocation.y == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if movingPiece.color == .black && toLocation.y == 7 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Make sure cannot take king
|
||||
if let piece = board.getPiece(at: toLocation) {
|
||||
@@ -278,11 +351,6 @@ open class PieceMovementPawn: PieceMovement {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the moving piece
|
||||
guard let movingPiece = board.getPiece(at: fromLocation) else{
|
||||
return false
|
||||
}
|
||||
|
||||
let color = movingPiece.color
|
||||
|
||||
// ****** Test forward locations ******
|
||||
@@ -290,12 +358,14 @@ open class PieceMovementPawn: PieceMovement {
|
||||
|
||||
// Test one ahead offset
|
||||
let oneAheadStride = (color == .white ? BoardStride(x: 0, y: 1) : BoardStride(x: 0, y: -1))
|
||||
var canMoveOneAhead = true
|
||||
|
||||
ONE_AHEAD: if fromLocation.canIncrementBy(stride: oneAheadStride) {
|
||||
|
||||
let location = fromLocation.incrementedBy(stride: oneAheadStride)
|
||||
|
||||
if let _ = board.getPiece(at: toLocation) {
|
||||
if let _ = board.getPiece(at: location) {
|
||||
canMoveOneAhead = false
|
||||
break ONE_AHEAD
|
||||
}
|
||||
|
||||
@@ -306,31 +376,31 @@ open class PieceMovementPawn: PieceMovement {
|
||||
|
||||
|
||||
// Test two ahead offset
|
||||
var twoAheadStride: BoardStride?
|
||||
|
||||
if color == .white && fromLocation.y == 1 {
|
||||
twoAheadStride = BoardStride(x: 0, y: 2)
|
||||
}
|
||||
else if color == .black && fromLocation.y == 6 {
|
||||
twoAheadStride = BoardStride(x: 0, y: -2)
|
||||
}
|
||||
|
||||
TWO_AHEAD: if let twoAheadStride = twoAheadStride {
|
||||
if canMoveOneAhead {
|
||||
|
||||
let twoAheadLocation = fromLocation.incrementedBy(stride: twoAheadStride)
|
||||
|
||||
if toLocation != twoAheadLocation {
|
||||
break TWO_AHEAD
|
||||
var twoAheadStride: BoardStride?
|
||||
|
||||
if color == .white && fromLocation.y == 1 {
|
||||
twoAheadStride = BoardStride(x: 0, y: 2)
|
||||
}
|
||||
else if color == .black && fromLocation.y == 6 {
|
||||
twoAheadStride = BoardStride(x: 0, y: -2)
|
||||
}
|
||||
|
||||
let oneAheadLocation = fromLocation.incrementedBy(stride: oneAheadStride)
|
||||
|
||||
if board.getPiece(at: oneAheadLocation) == nil && board.getPiece(at: twoAheadLocation) == nil {
|
||||
return true
|
||||
TWO_AHEAD: if let twoAheadStride = twoAheadStride {
|
||||
|
||||
let twoAheadLocation = fromLocation.incrementedBy(stride: twoAheadStride)
|
||||
|
||||
if toLocation != twoAheadLocation {
|
||||
break TWO_AHEAD
|
||||
}
|
||||
|
||||
if board.getPiece(at: twoAheadLocation) == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ****** Test Diagonal locations ******
|
||||
var diagonalStrides = [BoardStride]()
|
||||
|
||||
@@ -391,7 +461,18 @@ open class PieceMovementPawn: PieceMovement {
|
||||
|
||||
open class PieceMovementKing: PieceMovement {
|
||||
|
||||
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
let offsets: [(x: Int, y: Int)] = [
|
||||
(0,1), // North
|
||||
(1,1), // North-East
|
||||
(1,0), // East
|
||||
(1,-1), // South-East
|
||||
(0,-1), // South
|
||||
(-1,-1), // South-West
|
||||
(-1,0), // West
|
||||
(-1,1) // North- West
|
||||
]
|
||||
|
||||
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
|
||||
|
||||
// Make sure cannot take king
|
||||
if let piece = board.getPiece(at: toLocation) {
|
||||
@@ -400,17 +481,6 @@ open class PieceMovementKing: PieceMovement {
|
||||
}
|
||||
}
|
||||
|
||||
let offsets: [(x: Int, y: Int)] = [
|
||||
(0,1), // North
|
||||
(1,1), // North-East
|
||||
(1,0), // East
|
||||
(1,-1), // South-East
|
||||
(0,-1), // South
|
||||
(-1,-1), // South-West
|
||||
(-1,0), // West
|
||||
(-1,1) // North- West
|
||||
]
|
||||
|
||||
for offset in offsets {
|
||||
|
||||
let offsetLocation = fromLocation.incrementedBy(x: offset.x, y: offset.y)
|
||||
|
||||
@@ -41,6 +41,7 @@ open class Player {
|
||||
case pieceUnableToMoveToLocation
|
||||
case playerMustMoveOutOfCheck
|
||||
case cannotMoveInToCheck
|
||||
case gameIsNotInProgress
|
||||
}
|
||||
|
||||
public func canMovePieceWithError(fromLocation: BoardLocation, toLocation: BoardLocation) -> (result: Bool, error: MoveError?) {
|
||||
@@ -69,7 +70,7 @@ open class Player {
|
||||
let inCheckBeforeMove = self.game.board.isColorInCheck(color: self.color)
|
||||
var board = self.game.board
|
||||
board.movePiece(fromLocation: fromLocation, toLocation: toLocation)
|
||||
var inCheckAfterMove = board.isColorInCheck(color: self.color)
|
||||
let inCheckAfterMove = board.isColorInCheck(color: self.color)
|
||||
|
||||
// Return
|
||||
if inCheckBeforeMove && inCheckAfterMove {
|
||||
|
||||
Reference in New Issue
Block a user