1 Commits

Author SHA1 Message Date
Steve Barnegren 29cb7535a4 Added init method for ascii board 2017-02-01 08:08:30 +00:00
45 changed files with 564 additions and 1369 deletions
-1
View File
@@ -1 +0,0 @@
3.0
+4 -42
View File
@@ -7,14 +7,11 @@
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 */; };
@@ -36,6 +33,7 @@
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 */; };
@@ -44,13 +42,6 @@
/* 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 */;
@@ -74,20 +65,6 @@
};
/* 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>"; };
@@ -101,7 +78,6 @@
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>"; };
@@ -133,7 +109,6 @@
buildActionMask = 2147483647;
files = (
67A9CA3C1DE64E2B00510FB8 /* SwiftChess.framework in Frameworks */,
676C911B1E478A3A00985A4F /* SwiftChess.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -210,7 +185,6 @@
67B73A9E1E154C1E00C19176 /* AIPlayerTests.swift */,
67F7791F1E1B923B00885B89 /* AIConfigurationTests.swift */,
67F9DB741E1AD3BB00C7EC5A /* AIBehaviourTests.swift */,
676902571E432380007C76D7 /* PerformanceTests.swift */,
67FD86901E4128F00023335C /* OpeningsTests.swift */,
67D54A621DE9768200C12258 /* BoardRaters */,
67D54A551DE7680E00C12258 /* Tests.swift */,
@@ -273,12 +247,10 @@
67A9CA101DE64DAA00510FB8 /* Sources */,
67A9CA111DE64DAA00510FB8 /* Frameworks */,
67A9CA121DE64DAA00510FB8 /* Resources */,
676C911F1E478A3A00985A4F /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
676C911E1E478A3A00985A4F /* PBXTargetDependency */,
);
name = SwiftChessExample;
productName = SwiftChessExample;
@@ -315,7 +287,6 @@
TargetAttributes = {
67A9CA131DE64DAA00510FB8 = {
CreatedOnToolsVersion = 8.0;
DevelopmentTeam = VAA3W4LPY2;
ProvisioningStyle = Automatic;
};
67A9CA261DE64DAA00510FB8 = {
@@ -416,14 +387,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 */,
676902581E432380007C76D7 /* PerformanceTests.swift in Sources */,
67F779201E1B923B00885B89 /* AIConfigurationTests.swift in Sources */,
67D54A641DE976A900C12258 /* BoardRaterCountPiecesTests.swift in Sources */,
67A3EB161E3A926C00F6F01B /* BoardScenarios.swift in Sources */,
09AE32551E03D71D00A149FE /* BoardRaterPawnProgressionTests.swift in Sources */,
09A4C0291E013ECB000CFBF4 /* BoardRaterThreatenedPiecesTests.swift in Sources */,
67D54A5F1DE7683300C12258 /* PieceMovementTests.swift in Sources */,
67F779261E1C32A400885B89 /* BoardRaterCenterFourOccupationTests.swift in Sources */,
67F9DB6E1E1AC8DC00C7EC5A /* BoardRaterCheckMateOpportunityTests.swift in Sources */,
@@ -433,11 +404,6 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
676C911E1E478A3A00985A4F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = SwiftChess;
targetProxy = 676C911D1E478A3A00985A4F /* PBXContainerItemProxy */;
};
67A9CA291DE64DAA00510FB8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 67A9CA131DE64DAA00510FB8 /* SwiftChessExample */;
@@ -555,13 +521,11 @@
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";
@@ -576,12 +540,10 @@
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";
@@ -1,82 +0,0 @@
<?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>
@@ -4,37 +4,6 @@
<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>
+3 -21
View File
@@ -1,11 +1,8 @@
<?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>
<?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">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -42,24 +39,20 @@
<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"/>
@@ -106,50 +99,41 @@
<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"/>
@@ -157,12 +141,10 @@
<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"/>
+3 -8
View File
@@ -16,7 +16,6 @@ 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!
@@ -61,9 +60,6 @@ class GameViewController: UIViewController {
addPieceView(at: location.x, y: location.y, piece: piece)
}
// Activity Indicator
activityIndicator.hidesWhenStopped = true
// Update castle buttons visibility
updateCastleButtonsVisibility()
@@ -75,7 +71,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.makeMoveAsync()
player.makeMove()
}
}
}
@@ -364,7 +360,7 @@ extension GameViewController: GameDelegate {
}
func gameDidEndUpdates(game: Game) {
activityIndicator.stopAnimating()
// do nothing
}
func gameWonByPlayer(game: Game, player: Player) {
@@ -398,8 +394,7 @@ extension GameViewController: GameDelegate {
func tellAIToTakeGo() {
if let player = game.currentPlayer as? AIPlayer {
activityIndicator.startAnimating();
player.makeMoveAsync()
player.makeMove()
}
}
+3 -3
View File
@@ -27,7 +27,7 @@ class MenuViewController: UIViewController {
print("Player vs AI button pressed")
let whitePlayer = Human(color: .white)
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
let blackPlayer = AIPlayer(color: .black)
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, configuration: AIConfiguration(difficulty: .hard))
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
let whitePlayer = AIPlayer(color: .white)
let blackPlayer = AIPlayer(color: .black)
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer)
startGame(game: game)
+51 -41
View File
@@ -31,8 +31,8 @@ class AIBehaviourTests: XCTestCase {
func makeGameWithBoard(board: Board, colorToMove: Color) -> Game {
let whitePlayer = AIPlayer(color: .white, configuration: AIConfiguration(difficulty: .hard))
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
let whitePlayer = AIPlayer(color: .white)
let blackPlayer = AIPlayer(color: .black)
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
@@ -102,7 +102,7 @@ class AIBehaviourTests: XCTestCase {
return
}
player.makeMoveSync()
player.makeMove()
// If there is not piece at the location, then test has passed
guard let piece = game.board.getPiece(at: location) else {
@@ -118,53 +118,63 @@ class AIBehaviourTests: XCTestCase {
XCTFail("Black moved bishop")
}
func testBlackShouldTradePawnForQueen() {
func test_ScenarioTwo_BlackShouldTradeKnight() {
let board = ASCIIBoard(pieces: "g p - - - - - -" +
"p p - - - - - -" +
// In this example, the black knight at (2, 1) can either take the white rook at (0,0), or trade itself for the white queen at (0,2)
// Both of these are good moves
let board = ASCIIBoard(pieces: "- r - - q b k r" +
"p - - g - - p p" +
"- - - - b p - -" +
"- - - - p - - -" +
"- - - - - Q - -" +
"- - - - B - - -" +
"- - - - - - - -" +
"P P - - - - - -" +
"G P - - - - - -" )
"- - - p P - - -" +
"Q - - - - - - -" +
"P P k P K P P P" +
"R K B G - - - R" )
let queenLocation = board.locationOfCharacter("Q")
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
let queenLocation = BoardLocation(x: 0, y: 2)
let rookLocation = BoardLocation(x: 0, y: 0)
guard let player = game.blackPlayer as? AIPlayer else {
XCTFail()
let game = makeGameWithBoard(board: board.board, colorToMove: .black)
guard let player = game.currentPlayer as? AIPlayer else {
XCTFail("Expected an AI Player")
return
}
player.makeMoveSync()
player.makeMove()
XCTAssertEqual(game.board.getPiece(at: queenLocation)?.type, .pawn)
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))")
}
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)
}
}
+8 -8
View File
@@ -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, configuration: AIConfiguration(difficulty: .hard))
let blackPlayer = AIPlayer(color: .black, configuration: AIConfiguration(difficulty: .hard))
let whitePlayer = AIPlayer(color: .white)
let blackPlayer = AIPlayer(color: .black)
let game = Game(firstPlayer: whitePlayer, secondPlayer: blackPlayer, board: board, colorToMove: colorToMove)
return game
-27
View File
@@ -156,33 +156,6 @@ 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(difficulty: .hard));
self.boardRater = BoardRaterBoardDominance(configuration: AIConfiguration());
}
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(difficulty: .hard))
boardRater = BoardRaterCenterDominance(configuration: AIConfiguration())
}
override func tearDown() {
@@ -24,7 +24,7 @@ class BoardRaterCenterFourOccupationTests: XCTestCase {
func defaultBoardRater() -> BoardRaterCenterFourOccupation {
let configuration = AIConfiguration(difficulty: .hard)
let configuration = AIConfiguration()
let boardRater = BoardRaterCenterFourOccupation(configuration: configuration)
return boardRater
}
@@ -16,7 +16,7 @@ class BoardRaterCenterOwnershipTests: XCTestCase {
override func setUp() {
super.setUp()
boardRater = BoardRaterCenterOwnership(configuration: AIConfiguration(difficulty: .hard))
boardRater = BoardRaterCenterOwnership(configuration: AIConfiguration())
}
override func tearDown() {
@@ -16,7 +16,7 @@ class BoardRaterCheckMateOpportunityTests: XCTestCase {
override func setUp() {
super.setUp()
boardRater = BoardRaterCheckMateOpportunity(configuration: AIConfiguration(difficulty: .hard))
boardRater = BoardRaterCheckMateOpportunity(configuration: AIConfiguration())
}
override func tearDown() {
@@ -17,7 +17,7 @@ class BoardRaterCountPiecesTests: XCTestCase {
super.setUp()
// Initiailise a new board rater for each test
boardRater = BoardRaterCountPieces(configuration: AIConfiguration(difficulty: .hard));
boardRater = BoardRaterCountPieces(configuration: AIConfiguration());
}
override func tearDown() {
@@ -16,7 +16,7 @@ class BoardRaterKingSurroundingPossessionTests: XCTestCase {
override func setUp() {
super.setUp()
boardRater = BoardRaterKingSurroundingPossession(configuration: AIConfiguration(difficulty: .hard))
boardRater = BoardRaterKingSurroundingPossession(configuration: AIConfiguration())
}
override func tearDown() {
@@ -357,57 +357,34 @@ class BoardRaterKingSurroundingPossessionTests: XCTestCase {
XCTAssertGreaterThan(rating, 0)
}
func testThatPiecesSurroundingOpponentKingResultsInMoreNegativeRatingForWhite() {
func testThatPiecesSurroundingOpponentKingResultsInNegativeRatingForWhite() {
let openKingBoard = ASCIIBoard(pieces: "- - - - g - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - G - - -" )
let board = ASCIIBoard(pieces: "- - - p g p - -" +
"- - - p p p - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - G - - -" )
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)
let rating = boardRater.ratingfor(board: board.board, color: .white)
XCTAssertLessThan(rating, 0)
}
func testThatPiecesSurroundingOpponentKingResultsInMoreNegativeRatingForBlack() {
func testThatPiecesSurroundingOpponentKingResultsInNegativeRatingForBlack() {
let openKingBoard = ASCIIBoard(pieces: "- - - - g - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - G - - -" )
let board = ASCIIBoard(pieces: "- - - - g - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
"- - - P P P - -" +
"- - - P G P - -" )
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)
let rating = boardRater.ratingfor(board: board.board, color: .black)
XCTAssertLessThan(rating, 0)
}
@@ -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(difficulty: .hard))
boardRater = BoardRaterPawnProgression(configuration: AIConfiguration())
}
@@ -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(difficulty: .hard))
boardRater = BoardRaterThreatenedPieces(configuration: AIConfiguration())
}
override func tearDown() {
@@ -54,9 +54,9 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
let rating = boardRater.ratingfor(board: board.board, color: .white)
XCTAssertLessThan(rating, 0)
XCTAssert(rating < 0);
}
func testBoardRaterThreatenedPiecesReturnsPositiveValueIfThreateningOpponant() {
let board = ASCIIBoard(pieces: "- - - - - - - -" +
@@ -70,13 +70,11 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
let rating = boardRater.ratingfor(board: board.board, color: .white)
XCTAssertGreaterThan(rating, 0)
XCTAssert(rating > 0);
}
func testBoardRaterThreatenedPiecesReturnsHigherThreatValueForHigherValuePieces() {
/*
let queenBoard = ASCIIBoard(pieces: "- - - - - - - -" +
"- - - - - - - -" +
"- - - - - - - -" +
@@ -111,13 +109,32 @@ 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: "- - - - - - - -" +
"- - - - - - - -" +
@@ -145,8 +162,6 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
let badTradeRating = boardRater.threatRatingForPiece(at: rookLocation, board: badTradeBoard.board, color: .white)
XCTAssert(goodTradeRating < badTradeRating);
*/
}
// MARK - Get protected pieces tests
@@ -171,9 +186,9 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let protectingLocations = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
onBoard: board.board).map{ $0.location }
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// Check all of the expected locations appeared in the protecting locations array
for expectedIndex in expectedIndexes {
@@ -214,13 +229,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- B - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// None of the pieces are protecting the queen, so expect count to be zero
XCTAssertTrue(protectingPieces.count == 0)
XCTAssertTrue(protectingLocations.count == 0)
}
func testGetProtectingPiecesDoesntReturnThreateningPiecesOfOppositeColor() {
@@ -235,13 +249,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - b - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// The black pieces cannot protect the white queen, so expect count to be zero
XCTAssertTrue(protectingPieces.count == 0)
XCTAssertTrue(protectingLocations.count == 0)
}
func testGetProtectingPiecesDoesntReturnPawnsMovingStraightAhead() {
@@ -256,13 +269,11 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
on: board.board)
XCTAssertTrue(protectingPieces.count == 0)
XCTAssertTrue(protectingLocations.count == 0)
}
func testGetProtectingPiecesReturnsPawnsMovingDiagonally() {
@@ -277,12 +288,11 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let protectingPieces = boardRater.getPieces(protecting: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let protectingLocations = boardRater.protectingPiecesLocationsforPiece(at: queenLocation,
on: board.board)
XCTAssertTrue(protectingPieces.count == 1)
XCTAssertTrue(protectingLocations.count == 1)
}
// MARK - Get threatening pieces tests
@@ -307,10 +317,9 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let threateningLocations = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard).map{ $0.location }
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// Check all of the expected locations appeared in the protecting locations array
for expectedIndex in expectedIndexes {
@@ -351,13 +360,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- b - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// None of the pieces are threatening the queen, so expect count to be zero
XCTAssertTrue(threateningPieces.count == 0)
XCTAssertTrue(threateningLocations.count == 0)
}
func testGetThreateningPiecesDoesntReturnProtectingPiecesOfSameColor() {
@@ -372,13 +380,12 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - B - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("Q"))
let gameBoard = board.board
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
on: board.board)
// The white pieces cannot threaten the white queen, so expect count to be zero
XCTAssertTrue(threateningPieces.count == 0)
XCTAssertTrue(threateningLocations.count == 0)
}
func testGetThreateningPiecesDoesntReturnPawnsMovingStraightAhead() {
@@ -393,12 +400,11 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("q"))
let gameBoard = board.board
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
on: board.board)
XCTAssertTrue(threateningPieces.count == 0)
XCTAssertTrue(threateningLocations.count == 0)
}
func testGetThreateningPiecesReturnsPawnsMovingDiagonally() {
@@ -413,12 +419,11 @@ class BoardRaterThreatenedPiecesTests: XCTestCase {
"- - - - - - - -" )
let queenLocation = BoardLocation(index: board.indexOfCharacter("q"))
let gameBoard = board.board
let threateningPieces = boardRater.getPieces(threatening: gameBoard.getPiece(at: queenLocation)!,
onBoard: gameBoard)
let threateningLocations = boardRater.threateningPiecesLocationsforPiece(at: queenLocation,
on: board.board)
XCTAssertTrue(threateningPieces.count == 1)
XCTAssertTrue(threateningLocations.count == 1)
}
-86
View File
@@ -138,17 +138,6 @@ 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);
@@ -1022,81 +1011,6 @@ 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)
}
}
}
+4
View File
@@ -107,4 +107,8 @@ class GameTests: XCTestCase {
XCTAssertTrue(game.state == Game.State.won(color: .black))
}
}
-226
View File
@@ -1,226 +0,0 @@
//
// 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)
}
}
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
//
import XCTest
@testable import SwiftChess
import SwiftChess
class PieceMovementTests: XCTestCase {
+8 -108
View File
@@ -1,126 +1,26 @@
# SwiftChess
[![Version](https://img.shields.io/cocoapods/v/SBAutoLayout.svg?style=flat)](http://cocoapods.org/pods/SwiftChess)
[![License](https://img.shields.io/cocoapods/l/SBAutoLayout.svg?style=flat)](http://cocoapods.org/pods/SwiftChess)
[![Platform](https://img.shields.io/cocoapods/p/SBAutoLayout.svg?style=flat)](http://cocoapods.org/pods/SwiftChess)
SwiftChess is a chess engine written in Swift.
![swiftchess](https://cloud.githubusercontent.com/assets/6288713/24018928/d90f9182-0a8d-11e7-808c-a96bcb998462.gif)
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.
## Features
SwiftChess also includes a complete AI implementation.
- 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
There's no documentation for now, as the public api is still in flux, but there is a complete example project for iOS.
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.
SwiftChess also has a reasonably comprehensive set of unit tests.
## Example
The example application contains a complete implementation of swift chess.
Run `Example/Example.xcodeproj`
## 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)
```
The example app can run *player vs player*, *player vs AI*, *AI vs AI* matches
## Author
Follow me on twitter [@SteveBarnegren](https://twitter.com/stevebarnegren)
Steve Barnegren
[Follow me on Twitter](https://twitter.com/stevebarnegren)
## License
+11 -3
View File
@@ -9,7 +9,7 @@
Pod::Spec.new do |s|
s.name = 'SwiftChess'
s.version = '0.1.0'
s.summary = 'Chess engine written in Swift'
s.summary = 'A short description of SwiftChess.'
# 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,17 +18,25 @@ Pod::Spec.new do |s|
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
Chess engine written in Swift
TODO: Add long description of the pod here.
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/stevebarnegren'
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
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
+44 -55
View File
@@ -8,62 +8,51 @@
import Foundation
public struct AIConfiguration {
public enum Difficulty: Int {
case easy
case medium
case hard
}
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
struct AIConfiguration {
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)
// BoardRater - Count Pieces
var boardRaterCountPiecesWeighting: Double!
// 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()
}
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
}
}
+19 -46
View File
@@ -11,13 +11,11 @@ import Foundation
open class AIPlayer : Player {
let boardRaters: [BoardRater]!
public var configuration: AIConfiguration!
let boardRaters : [BoardRater]!
let configuration = AIConfiguration() // <-- We should pass this in eventually
var openingMoves = [OpeningMove]()
public init(color: Color, configuration: AIConfiguration){
self.configuration = configuration
public init(color: Color){
self.boardRaters = [
BoardRaterCountPieces(configuration: configuration),
@@ -37,29 +35,17 @@ open class AIPlayer : Player {
self.color = color
}
public func makeMoveAsync() {
public func makeMove() {
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
}
print("\n\n****** Make Move ******");
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
@@ -73,10 +59,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
@@ -90,11 +76,7 @@ open class AIPlayer : Player {
operations.append(transformOperation)
}
let strongGame = self.game!
DispatchQueue.main.async {
strongGame.playerDidMakeMove(player: self, boardOperations: operations)
}
self.game.playerDidMakeMove(player: self, boardOperations: operations)
}
func openingMoveForBoard(_ board: Board) -> Move? {
@@ -103,14 +85,11 @@ open class AIPlayer : Player {
$0.board == board
}
//print("Num opening moves`; \(possibleMoves.count)")
guard possibleMoves.count > 0 else{
return nil
}
let index = Int(arc4random_uniform(UInt32(possibleMoves.count)))
let openingMove = possibleMoves[index]
let openingMove = possibleMoves[Int(arc4random()) % possibleMoves.count]
return Move(type: .singlePiece(sourceLocation: openingMove.fromLocation,
targetLocation: openingMove.toLocation),
@@ -149,17 +128,12 @@ open class AIPlayer : Player {
}
// Rate
var rating = ratingForBoard(resultBoard)
// reduce rating if suicide
if resultBoard.canColorMoveAnyPieceToLocation(color: color.opposite(), location: targetLocation) {
rating -= (abs(rating) * configuration.suicideMultipler.value);
}
print("(\(sourceLocation.x),\(sourceLocation.y)) -> (\(targetLocation.x),\(targetLocation.y))")
let rating = ratingForBoard(resultBoard)
let move = Move(type: .singlePiece(sourceLocation: sourceLocation, targetLocation: targetLocation),
rating: rating)
possibleMoves.append(move)
// print("Rating: \(rating)")
print("Rating: \(rating)")
}
}
@@ -181,7 +155,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 {
@@ -226,8 +200,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
}
@@ -266,13 +240,12 @@ 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.setPiece(newPiece!, at: location)
newBoard.squares[location.index].piece = newPiece
let rating = ratingForBoard(newBoard)
if rating > bestRating {
+100 -6
View File
@@ -32,6 +32,103 @@ public struct ASCIIBoard {
let artString: String
var stringContainsColors: Bool!
public init(
// Row 8
_ a8: Character,
_ b8: Character,
_ c8: Character,
_ d8: Character,
_ e8: Character,
_ f8: Character,
_ g8: Character,
_ h8: Character,
// Row 7
_ a7: Character,
_ b7: Character,
_ c7: Character,
_ d7: Character,
_ e7: Character,
_ f7: Character,
_ g7: Character,
_ h7: Character,
// Row 6
_ a6: Character,
_ b6: Character,
_ c6: Character,
_ d6: Character,
_ e6: Character,
_ f6: Character,
_ g6: Character,
_ h6: Character,
// Row 5
_ a5: Character,
_ b5: Character,
_ c5: Character,
_ d5: Character,
_ e5: Character,
_ f5: Character,
_ g5: Character,
_ h5: Character,
// Row 4
_ a4: Character,
_ b4: Character,
_ c4: Character,
_ d4: Character,
_ e4: Character,
_ f4: Character,
_ g4: Character,
_ h4: Character,
// Row 3
_ a3: Character,
_ b3: Character,
_ c3: Character,
_ d3: Character,
_ e3: Character,
_ f3: Character,
_ g3: Character,
_ h3: Character,
// Row 2
_ a2: Character,
_ b2: Character,
_ c2: Character,
_ d2: Character,
_ e2: Character,
_ f2: Character,
_ g2: Character,
_ h2: Character,
// Row 1
_ a1: Character,
_ b1: Character,
_ c1: Character,
_ d1: Character,
_ e1: Character,
_ f1: Character,
_ g1: Character,
_ h1: Character
){
var inputAsString =
"\(a8) \(b8) \(c8) \(d8) \(e8) \(f8) \(g8) \(h8)" +
"\(a7) \(b7) \(c7) \(d7) \(e7) \(f7) \(g7) \(h7)" +
"\(a6) \(b6) \(c6) \(d6) \(e6) \(f6) \(g6) \(h6)" +
"\(a5) \(b5) \(c5) \(d5) \(e5) \(f5) \(g5) \(h5)" +
"\(a4) \(b4) \(c4) \(d4) \(e4) \(f4) \(g4) \(h4)" +
"\(a3) \(b3) \(c3) \(d3) \(e3) \(f3) \(g3) \(h3)" +
"\(a2) \(b2) \(c2) \(d2) \(e2) \(f2) \(g2) \(h2)" +
"\(a1) \(b1) \(c1) \(d1) \(e1) \(f1) \(g1) \(h1)";
// Transform
inputAsString = transformASCIIBoardInput(inputAsString)
// Check string format
assert(inputAsString.characters.count == 64, "ASCII board art must be 128 characters long")
self.artString = inputAsString
self.stringContainsColors = false
}
public init(pieces artString: String) {
var artString = artString
@@ -66,17 +163,14 @@ public struct ASCIIBoard {
var board = Board(state: .empty)
// Clear all pieces on the board
BoardLocation.all.forEach{
board.removePiece(atLocation: $0)
(0..<64).forEach{
board.squares[$0].piece = nil;
}
// Setup pieces from ascii art
(0..<64).forEach{
let character = boardArt[boardArt.characters.index(boardArt.startIndex, offsetBy: $0)]
if let piece = pieceFromCharacter(character) {
board.setPiece(piece, at: BoardLocation(index: $0))
}
board.squares[$0].piece = pieceFromCharacter(character)
}
return board
+33 -42
View File
@@ -27,9 +27,9 @@ public func ==(lhs: Square, rhs: Square) -> Bool {
case (.none, .none):
return true
case (.some, .none):
return false
return true
case (.none, .some):
return false
return true
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 : Equatable {
public struct Board {
public enum InitialState {
case empty
case newGame
}
public private(set) var squares = [Square]()
public var squares = [Square]()
// MARK: - Init
public init(state: InitialState) {
@@ -63,56 +63,48 @@ public struct Board : Equatable {
mutating func setupForNewGame() {
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)
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)
// Setup white pawn row
for i in 8...15 {
setPieceAtIndex(Piece(type: .pawn, color: .white), i)
squares[i].piece = Piece(type: .pawn, color: .white)
}
// Setup black bottom row
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)
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)
// Setup black pawn row
for i in 48...55 {
setPieceAtIndex(Piece(type: .pawn, color: .black), i)
squares[i].piece = Piece(type: .pawn, color: .black)
}
}
// 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 {
@@ -121,7 +113,7 @@ public struct Board : Equatable {
var operations = [BoardOperation]()
guard let movingPiece = getPiece(at: fromLocation) else {
guard var movingPiece = getPiece(at: fromLocation) else {
fatalError("There is no piece on at (\(fromLocation.x),\(fromLocation.y))")
}
@@ -134,7 +126,6 @@ public struct Board : Equatable {
}
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
@@ -479,16 +470,16 @@ public struct Board : Equatable {
rookEndXPos = 3
case (.black, .kingSide):
yPos = 7
kingStartXPos = 4
rookStartXPos = 7
kingEndXPos = 6
rookEndXPos = 5
kingStartXPos = 3
rookStartXPos = 0
kingEndXPos = 1
rookEndXPos = 2
case (.black, .queenSide):
yPos = 7
kingStartXPos = 4
rookStartXPos = 0
kingEndXPos = 2
rookEndXPos = 3
kingStartXPos = 3
rookStartXPos = 7
kingEndXPos = 5
rookEndXPos = 4
}
}
}
+7 -17
View File
@@ -23,26 +23,16 @@ public struct BoardLocation : Equatable {
public var index: Int
private static var allLocationsBacking: [BoardLocation]?
public static var all: [BoardLocation] {
if let all = allLocationsBacking {
return all
// 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))
}
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
return locations
}
public var x: Int {
@@ -31,7 +31,7 @@ class BoardRaterBoardDominance : BoardRater {
}
return rating * configuration.boardRaterBoardDominanceWeighting.value;
return rating * configuration.boardRaterBoardDominanceWeighting;
}
}
@@ -34,7 +34,7 @@ class BoardRaterCenterDominance : BoardRater {
}
}
return rating * configuration.boardRaterCenterDominanceWeighting.value
return rating * configuration.boardRaterCenterDominanceWeighting
}
func dominanceValueFor(location: BoardLocation) -> Double {
@@ -32,6 +32,6 @@ class BoardRaterCenterFourOccupation: BoardRater {
rating += piece.color == color ? value : -value
}
return rating * configuration.boardRaterCenterFourOccupationWeighting.value
return rating * configuration.boardRaterCenterFourOccupationWeighting
}
}
@@ -29,7 +29,7 @@ class BoardRaterCenterOwnership : BoardRater {
rating += (piece.color == color) ? distance : -distance
}
return rating * configuration.boardRaterCenterOwnershipWeighting.value
return rating * configuration.boardRaterCenterOwnershipWeighting
}
@@ -46,7 +46,7 @@ class BoardRaterCheckMateOpportunity : BoardRater {
}
}
return rating * configuration.boardRaterCheckMateOpportunityWeighting.value
return rating * configuration.boardRaterCheckMateOpportunityWeighting
}
}
@@ -23,7 +23,7 @@ class BoardRaterCountPieces : BoardRater {
rating += piece.color == color ? piece.value() : -piece.value()
}
return rating * configuration.boardRaterCountPiecesWeighting.value
return rating * configuration.boardRaterCountPiecesWeighting
}
}
@@ -20,11 +20,8 @@ class BoardRaterKingSurroundingPossession : BoardRater {
// The kings will be able to move to their surrounding locations, so remove them from the board
var noKingsBoard = board
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
noKingsBoard.squares[noKingsBoard.getKingLocation(color: .white).index].piece = nil
noKingsBoard.squares[noKingsBoard.getKingLocation(color: .black).index].piece = nil
for location in ownKingLocations {
@@ -69,7 +66,7 @@ class BoardRaterKingSurroundingPossession : BoardRater {
}
}
return rating * configuration.boardRaterKingSurroundingPossessionWeighting.value
return rating * configuration.boardRaterKingSurroundingPossessionWeighting
}
func locationsSurroundingKing(color: Color, board: Board) -> [BoardLocation] {
@@ -52,7 +52,7 @@ class BoardRaterPawnProgression : BoardRater {
squaresAdvanced = 7 - (location.y + 2)
}
return Double(squaresAdvanced) * configuration.boardRaterPawnProgressionWeighting.value // <- should probably add some sort of curve
return Double(squaresAdvanced) * configuration.boardRaterPawnProgressionWeighting // <- should probably add some sort of curve
}
}
@@ -12,243 +12,126 @@ import Foundation
class BoardRaterThreatenedPieces : BoardRater {
/*
override func ratingfor(board: Board, color: Color) -> Double {
var rating = Double(0)
let allPieces = board.squares.flatMap{
$0.piece
}
for piece in allPieces {
for location in BoardLocation.all {
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
guard let piece = board.getPiece(at: location) else {
continue;
}
if piece.color == color.opposite() {
value = -value
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
}
rating += value
rating += (piece.color == color) ? -threatRating : threatRating;
}
return rating * configuration.boardRaterThreatenedPiecesWeighting
}
*/
override func ratingfor(board: Board, color: Color) -> Double {
// Returns a more positive rating the more the piece is threatened
func threatRatingForPiece(at location: BoardLocation, board: Board, color: Color) -> Double {
let rating = board.getPieces(color: color)
.map{ threatValue(forPiece: $0, onBoard: board) }
.reduce(0,+)
* configuration.boardRaterThreatenedPiecesWeighting.value
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 {
rating += piece.value()
}
}
return rating
}
func threatValue(forPiece piece: Piece, onBoard board: Board) -> Double {
let threatenedByPieces = getPieces(threatening: piece, onBoard: board)
let protectedByPieces = getPieces(protecting: piece, onBoard: board)
let isThreatened = threatenedByPieces.count > 0
let isProtected = protectedByPieces.count > 0
// Threatened but not protected
if isThreatened && !isProtected {
return -piece.value() * 3
}
// Threatened, but protected (only return if the trade is not preferable)
if isThreatened && isProtected {
let lowestValueThreat = threatenedByPieces.lowestPieceValue()
if lowestValueThreat < piece.value() {
return -piece.value()
}
// Here we could bump the value to encourage a good trade?
}
let targetPieces = getPieces(threatenedBy: piece, onBoard: board)
for targetPiece in targetPieces {
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()
}
}
// Nothing much interesting
return 0
}
// MARK - Get protecting / Threatening pieces
// MARK: - Helpers
func getPieces(protecting piece: Piece, onBoard board: Board) -> [Piece] {
func protectingPiecesLocationsforPiece(at location: BoardLocation, on board: Board) -> [BoardLocation] {
var alteredBoard = board
alteredBoard.setPiece(piece.withOppositeColor(), at: piece.location)
var newBoard = board
return alteredBoard.getPieces(color: piece.color).filter{
$0.movement.canPieceMove(fromLocation: $0.location, toLocation: piece.location, board: alteredBoard, accountForCheckState: true)
guard let protectedPiece = board.getPiece(at: location) else {
fatalError("Expected board location to contain piece")
}
}
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 {
// 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 alteredBoard = board
alteredBoard.setPiece(piece.withOppositeColor(), at: piece.location)
var pieces = [BoardLocation]()
for square in alteredBoard.squares {
for sourceLocation in BoardLocation.all {
guard let squarePiece = square.piece else {
guard let protectingPiece = newBoard.getPiece(at: sourceLocation) else{
continue
}
guard squarePiece.color == piece.color else {
guard protectingPiece.color == protectedPiece.color else{
continue
}
if squarePiece.movement.canPieceMove(fromLocation: squarePiece.location,
toLocation: piece.location,
board: alteredBoard,
accountForCheckState: true) {
return true
if protectingPiece.movement.canPieceMove(fromLocation: sourceLocation, toLocation: location, board: newBoard) {
pieces.append(sourceLocation)
}
}
return false
return pieces
}
func isPieceThreatened(_ piece: Piece, onBoard board: Board) -> Bool {
func threateningPiecesLocationsforPiece(at location: BoardLocation, on board: Board) -> [BoardLocation] {
for square in board.squares {
guard let threatenedPiece = board.getPiece(at: location) else {
fatalError("Expected board location to contain piece")
}
let threatenedColor = threatenedPiece.color
var pieces = [BoardLocation]()
for sourceLocation in BoardLocation.all {
guard let squarePiece = square.piece else {
guard let threateningPiece = board.getPiece(at: sourceLocation) else{
continue
}
guard squarePiece.color == piece.color.opposite() else {
guard threateningPiece.color == threatenedColor.opposite() else{
continue
}
if squarePiece.movement.canPieceMove(fromLocation: squarePiece.location, toLocation: piece.location, board: board, accountForCheckState: true) {
return true
if threateningPiece.movement.canPieceMove(fromLocation: sourceLocation, toLocation: location, board: board) {
pieces.append(sourceLocation)
}
}
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)
}
return pieces
}
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
}
}
-25
View File
@@ -29,12 +29,6 @@ open class Game {
}
}
}
public enum GameType {
case humanVsHuman
case humanVsComputer
case computerVsComputer
}
// MARK: Properties
open var board = Board(state: .newGame)
@@ -44,18 +38,6 @@ 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){
@@ -90,9 +72,6 @@ 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)
@@ -110,9 +89,6 @@ 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
@@ -122,7 +98,6 @@ extension Game : PlayerDelegate {
}
self.delegate?.gameDidChangeCurrentPlayer(game: self)
}
func processBoardOperations(boardOperations: [BoardOperation]) {
+1 -6
View File
@@ -19,11 +19,6 @@ 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
@@ -52,7 +47,7 @@ open class Human : Player {
// Change the piece
let newPiece = self.game.board.squares[pawnLocation.index].piece?.byChangingType(newType: $0)
self.game.board.setPiece(newPiece!, at: pawnLocation)
self.game.board.squares[pawnLocation.index].piece = newPiece
// Add a transform piece operation
let modifyOperation = BoardOperation(type: .transformPiece, piece: newPiece!, location: pawnLocation)
-3
View File
@@ -51,9 +51,6 @@ class Opening {
board.movePiece(fromLocation: locations.fromLocation,
toLocation: locations.toLocation)
//move.board.printBoardPieces()
}
// Filter for color
+3 -11
View File
@@ -8,7 +8,7 @@
import Foundation
public enum Color: Int {
public enum Color {
case white
case black
@@ -20,16 +20,13 @@ public enum Color: Int {
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: Int {
public enum PieceType {
case pawn
case rook
case knight
@@ -47,9 +44,8 @@ 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)
}
@@ -85,10 +81,6 @@ 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 {
+92 -162
View File
@@ -10,30 +10,23 @@ 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 pawnMovement
return PieceMovementPawn()
case .rook:
return rookMovement
return PieceMovementRook()
case .knight:
return knightMovement
return PieceMovementKnight()
case .bishop:
return bishopMovement
return PieceMovementBishop()
case .queen:
return queenMovement
return PieceMovementQueen()
case .king:
return kingMovement
return PieceMovementKing()
}
}
@@ -41,63 +34,12 @@ open class PieceMovement {
}
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 {
open func canPieceMove(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 {
@@ -106,7 +48,9 @@ open class PieceMovement {
}
// Get the moving piece
guard let movingPiece = board.getPiece(at: fromLocation) else {
var movingPiece = board.getPiece(at: fromLocation)
if movingPiece == nil {
print("Cannot from an index that does not contain a piece")
return false
}
@@ -122,15 +66,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
}
}
@@ -165,14 +109,16 @@ open class PieceMovement {
}
// Check if space is occupied
guard let movingPiece = board.getPiece(at: pieceLocation) else {
print("Cannot move from an index that does not contain a piece")
var movingPiece = board.getPiece(at: pieceLocation)
if movingPiece == nil {
print("Cannot 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
}
}
@@ -186,21 +132,14 @@ open class PieceMovement {
open class PieceMovementStraightLine: PieceMovement {
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 {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
let sameX = fromLocation.x == toLocation.x
let sameY = fromLocation.y == toLocation.y
if !(sameX || sameY) {
return false
}
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
]
for stride in strides {
if canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board, stride: stride) {
@@ -217,18 +156,14 @@ open class PieceMovementStraightLine: PieceMovement {
open class PieceMovementDiagonal: PieceMovement {
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 {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
if fromLocation.isDarkSquare != toLocation.isDarkSquare {
return false
}
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
]
for stride in strides {
if canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board, stride: stride) {
@@ -248,7 +183,7 @@ open class PieceMovementQueen: PieceMovement {
let movements : [PieceMovement] = [PieceMovementStraightLine(), PieceMovementDiagonal()]
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
for pieceMovement in movements {
@@ -268,9 +203,12 @@ open class PieceMovementRook: PieceMovement {
let straightLineMovement = PieceMovementStraightLine()
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
return straightLineMovement.canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board)
return false
}
}
@@ -280,9 +218,12 @@ open class PieceMovementBishop: PieceMovement {
let diagonalMovement = PieceMovementDiagonal()
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
return diagonalMovement.canPieceMove(fromLocation: fromLocation, toLocation: toLocation, board: board)
return false
}
}
@@ -290,18 +231,7 @@ open class PieceMovementBishop: PieceMovement {
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)
]
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
// Make sure cannot take king
if let piece = board.getPiece(at: toLocation) {
@@ -310,12 +240,22 @@ 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
}
}
@@ -329,20 +269,7 @@ open class PieceMovementKnight: PieceMovement {
open class PieceMovementPawn: PieceMovement {
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
}
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
// Make sure cannot take king
if let piece = board.getPiece(at: toLocation) {
@@ -351,6 +278,11 @@ 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 ******
@@ -358,14 +290,12 @@ 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: location) {
canMoveOneAhead = false
if let _ = board.getPiece(at: toLocation) {
break ONE_AHEAD
}
@@ -376,31 +306,31 @@ open class PieceMovementPawn: PieceMovement {
// Test two ahead offset
if canMoveOneAhead {
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 {
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 twoAheadLocation = fromLocation.incrementedBy(stride: twoAheadStride)
if toLocation != twoAheadLocation {
break TWO_AHEAD
}
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
}
let oneAheadLocation = fromLocation.incrementedBy(stride: oneAheadStride)
if board.getPiece(at: oneAheadLocation) == nil && board.getPiece(at: twoAheadLocation) == nil {
return true
}
}
// ****** Test Diagonal locations ******
var diagonalStrides = [BoardStride]()
@@ -461,18 +391,7 @@ open class PieceMovementPawn: PieceMovement {
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
]
override func isMovementPossible(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
override open func canPieceMove(fromLocation: BoardLocation, toLocation: BoardLocation, board: Board) -> Bool {
// Make sure cannot take king
if let piece = board.getPiece(at: toLocation) {
@@ -481,6 +400,17 @@ 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)
+1 -2
View File
@@ -41,7 +41,6 @@ open class Player {
case pieceUnableToMoveToLocation
case playerMustMoveOutOfCheck
case cannotMoveInToCheck
case gameIsNotInProgress
}
public func canMovePieceWithError(fromLocation: BoardLocation, toLocation: BoardLocation) -> (result: Bool, error: MoveError?) {
@@ -70,7 +69,7 @@ open class Player {
let inCheckBeforeMove = self.game.board.isColorInCheck(color: self.color)
var board = self.game.board
board.movePiece(fromLocation: fromLocation, toLocation: toLocation)
let inCheckAfterMove = board.isColorInCheck(color: self.color)
var inCheckAfterMove = board.isColorInCheck(color: self.color)
// Return
if inCheckBeforeMove && inCheckAfterMove {