├── .gitignore ├── GoTao ├── GoTao.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── GoTao │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Game │ │ ├── GameViewController.swift │ │ └── GobanView.swift │ ├── GameInfo.swift │ ├── HomeViewController.swift │ ├── Info.plist │ ├── Models │ │ ├── Location.swift │ │ ├── Move.swift │ │ ├── MoveGroup.swift │ │ ├── SGFParser.swift │ │ └── StoneType.swift │ ├── StringExtension.swift │ └── statics │ │ ├── 001.sgf │ │ ├── Black.png │ │ ├── White.png │ │ └── board_back.png ├── GoTaoTests │ ├── GoTaoTests.swift │ └── Info.plist └── GoTaoUITests │ ├── GoTaoUITests.swift │ └── Info.plist ├── LICENSE ├── README.md └── images ├── play.gif ├── screen.jpg └── screen.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | 28 | # Carthage 29 | # 30 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 31 | # Carthage/Checkouts 32 | 33 | Carthage/Build 34 | -------------------------------------------------------------------------------- /GoTao/GoTao.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A9298FAA1C31730F00094259 /* SGFParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9298FA91C31730F00094259 /* SGFParser.swift */; }; 11 | A9298FAD1C31810800094259 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9298FAC1C31810800094259 /* StringExtension.swift */; }; 12 | A96C71A81C2FF1D60024C263 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C71A71C2FF1D60024C263 /* GameViewController.swift */; }; 13 | A96C71AA1C2FF4A80024C263 /* GameInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C71A91C2FF4A80024C263 /* GameInfo.swift */; }; 14 | A96C71AC1C3001B50024C263 /* GobanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C71AB1C3001B50024C263 /* GobanView.swift */; }; 15 | A96C71B11C3005040024C263 /* Black.png in Resources */ = {isa = PBXBuildFile; fileRef = A96C71AE1C3005040024C263 /* Black.png */; }; 16 | A96C71B21C3005040024C263 /* board_back.png in Resources */ = {isa = PBXBuildFile; fileRef = A96C71AF1C3005040024C263 /* board_back.png */; }; 17 | A96C71B31C3005040024C263 /* White.png in Resources */ = {isa = PBXBuildFile; fileRef = A96C71B01C3005040024C263 /* White.png */; }; 18 | A9AA0C751C34110E00C76284 /* 001.sgf in Resources */ = {isa = PBXBuildFile; fileRef = A9AA0C741C34110E00C76284 /* 001.sgf */; }; 19 | A9AA0C781C360D9900C76284 /* MoveGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9AA0C771C360D9900C76284 /* MoveGroup.swift */; }; 20 | A9F2779C1C2FEA9900B19300 /* Move.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F277991C2FEA9900B19300 /* Move.swift */; }; 21 | A9F2779D1C2FEA9900B19300 /* StoneType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F2779A1C2FEA9900B19300 /* StoneType.swift */; }; 22 | A9F2779E1C2FEA9900B19300 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F2779B1C2FEA9900B19300 /* Location.swift */; }; 23 | A9F8B6CE1C2FE05400540306 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F8B6CD1C2FE05400540306 /* AppDelegate.swift */; }; 24 | A9F8B6D01C2FE05400540306 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F8B6CF1C2FE05400540306 /* HomeViewController.swift */; }; 25 | A9F8B6D31C2FE05400540306 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9F8B6D11C2FE05400540306 /* Main.storyboard */; }; 26 | A9F8B6D51C2FE05400540306 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9F8B6D41C2FE05400540306 /* Assets.xcassets */; }; 27 | A9F8B6D81C2FE05400540306 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9F8B6D61C2FE05400540306 /* LaunchScreen.storyboard */; }; 28 | A9F8B6E31C2FE05400540306 /* GoTaoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F8B6E21C2FE05400540306 /* GoTaoTests.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | A9F8B6DF1C2FE05400540306 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = A9F8B6C21C2FE05400540306 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = A9F8B6C91C2FE05400540306; 37 | remoteInfo = GoTao; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | A9298FA91C31730F00094259 /* SGFParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SGFParser.swift; path = Models/SGFParser.swift; sourceTree = ""; }; 43 | A9298FAC1C31810800094259 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; 44 | A96C71A71C2FF1D60024C263 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameViewController.swift; path = Game/GameViewController.swift; sourceTree = ""; }; 45 | A96C71A91C2FF4A80024C263 /* GameInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameInfo.swift; sourceTree = ""; }; 46 | A96C71AB1C3001B50024C263 /* GobanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GobanView.swift; path = Game/GobanView.swift; sourceTree = ""; }; 47 | A96C71AE1C3005040024C263 /* Black.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Black.png; sourceTree = ""; }; 48 | A96C71AF1C3005040024C263 /* board_back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = board_back.png; sourceTree = ""; }; 49 | A96C71B01C3005040024C263 /* White.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = White.png; sourceTree = ""; }; 50 | A9AA0C741C34110E00C76284 /* 001.sgf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 001.sgf; sourceTree = ""; }; 51 | A9AA0C771C360D9900C76284 /* MoveGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoveGroup.swift; path = Models/MoveGroup.swift; sourceTree = ""; }; 52 | A9F277991C2FEA9900B19300 /* Move.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Move.swift; path = Models/Move.swift; sourceTree = ""; }; 53 | A9F2779A1C2FEA9900B19300 /* StoneType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StoneType.swift; path = Models/StoneType.swift; sourceTree = ""; }; 54 | A9F2779B1C2FEA9900B19300 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Location.swift; path = Models/Location.swift; sourceTree = ""; }; 55 | A9F8B6CA1C2FE05400540306 /* GoTao.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GoTao.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | A9F8B6CD1C2FE05400540306 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | A9F8B6CF1C2FE05400540306 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 58 | A9F8B6D21C2FE05400540306 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | A9F8B6D41C2FE05400540306 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60 | A9F8B6D71C2FE05400540306 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 61 | A9F8B6D91C2FE05400540306 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | A9F8B6DE1C2FE05400540306 /* GoTaoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GoTaoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | A9F8B6E21C2FE05400540306 /* GoTaoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoTaoTests.swift; sourceTree = ""; }; 64 | A9F8B6E41C2FE05400540306 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | A9F8B6ED1C2FE05400540306 /* GoTaoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoTaoUITests.swift; sourceTree = ""; }; 66 | A9F8B6EF1C2FE05400540306 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | /* End PBXFileReference section */ 68 | 69 | /* Begin PBXFrameworksBuildPhase section */ 70 | A9F8B6C71C2FE05400540306 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | A9F8B6DB1C2FE05400540306 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | A9298FAB1C3180F600094259 /* Extension */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | A9298FAC1C31810800094259 /* StringExtension.swift */, 91 | ); 92 | name = Extension; 93 | sourceTree = ""; 94 | }; 95 | A96C71AD1C3005040024C263 /* statics */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | A96C71AE1C3005040024C263 /* Black.png */, 99 | A96C71AF1C3005040024C263 /* board_back.png */, 100 | A96C71B01C3005040024C263 /* White.png */, 101 | A9AA0C741C34110E00C76284 /* 001.sgf */, 102 | ); 103 | path = statics; 104 | sourceTree = ""; 105 | }; 106 | A9F8B6C11C2FE05400540306 = { 107 | isa = PBXGroup; 108 | children = ( 109 | A9F8B6CC1C2FE05400540306 /* GoTao */, 110 | A9F8B6E11C2FE05400540306 /* GoTaoTests */, 111 | A9F8B6EC1C2FE05400540306 /* GoTaoUITests */, 112 | A9F8B6CB1C2FE05400540306 /* Products */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | A9F8B6CB1C2FE05400540306 /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | A9F8B6CA1C2FE05400540306 /* GoTao.app */, 120 | A9F8B6DE1C2FE05400540306 /* GoTaoTests.xctest */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | A9F8B6CC1C2FE05400540306 /* GoTao */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | A9298FAB1C3180F600094259 /* Extension */, 129 | A96C71AD1C3005040024C263 /* statics */, 130 | A9F8B6FD1C2FE4C600540306 /* Models */, 131 | A9F8B6FC1C2FE4BD00540306 /* Game */, 132 | A9F8B6FB1C2FE4B500540306 /* Home */, 133 | A9F8B6CD1C2FE05400540306 /* AppDelegate.swift */, 134 | A9F8B6D11C2FE05400540306 /* Main.storyboard */, 135 | A9F8B6D41C2FE05400540306 /* Assets.xcassets */, 136 | A9F8B6D61C2FE05400540306 /* LaunchScreen.storyboard */, 137 | A9F8B6D91C2FE05400540306 /* Info.plist */, 138 | ); 139 | path = GoTao; 140 | sourceTree = ""; 141 | }; 142 | A9F8B6E11C2FE05400540306 /* GoTaoTests */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | A9F8B6E21C2FE05400540306 /* GoTaoTests.swift */, 146 | A9F8B6E41C2FE05400540306 /* Info.plist */, 147 | ); 148 | path = GoTaoTests; 149 | sourceTree = ""; 150 | }; 151 | A9F8B6EC1C2FE05400540306 /* GoTaoUITests */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | A9F8B6ED1C2FE05400540306 /* GoTaoUITests.swift */, 155 | A9F8B6EF1C2FE05400540306 /* Info.plist */, 156 | ); 157 | path = GoTaoUITests; 158 | sourceTree = ""; 159 | }; 160 | A9F8B6FB1C2FE4B500540306 /* Home */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | A9F8B6CF1C2FE05400540306 /* HomeViewController.swift */, 164 | ); 165 | name = Home; 166 | sourceTree = ""; 167 | }; 168 | A9F8B6FC1C2FE4BD00540306 /* Game */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | A96C71A71C2FF1D60024C263 /* GameViewController.swift */, 172 | A96C71AB1C3001B50024C263 /* GobanView.swift */, 173 | ); 174 | name = Game; 175 | sourceTree = ""; 176 | }; 177 | A9F8B6FD1C2FE4C600540306 /* Models */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | A9F277991C2FEA9900B19300 /* Move.swift */, 181 | A9F2779A1C2FEA9900B19300 /* StoneType.swift */, 182 | A9F2779B1C2FEA9900B19300 /* Location.swift */, 183 | A9298FA91C31730F00094259 /* SGFParser.swift */, 184 | A96C71A91C2FF4A80024C263 /* GameInfo.swift */, 185 | A9AA0C771C360D9900C76284 /* MoveGroup.swift */, 186 | ); 187 | name = Models; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXGroup section */ 191 | 192 | /* Begin PBXNativeTarget section */ 193 | A9F8B6C91C2FE05400540306 /* GoTao */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = A9F8B6F21C2FE05400540306 /* Build configuration list for PBXNativeTarget "GoTao" */; 196 | buildPhases = ( 197 | A9F8B6C61C2FE05400540306 /* Sources */, 198 | A9F8B6C71C2FE05400540306 /* Frameworks */, 199 | A9F8B6C81C2FE05400540306 /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | ); 205 | name = GoTao; 206 | productName = GoTao; 207 | productReference = A9F8B6CA1C2FE05400540306 /* GoTao.app */; 208 | productType = "com.apple.product-type.application"; 209 | }; 210 | A9F8B6DD1C2FE05400540306 /* GoTaoTests */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = A9F8B6F51C2FE05400540306 /* Build configuration list for PBXNativeTarget "GoTaoTests" */; 213 | buildPhases = ( 214 | A9F8B6DA1C2FE05400540306 /* Sources */, 215 | A9F8B6DB1C2FE05400540306 /* Frameworks */, 216 | A9F8B6DC1C2FE05400540306 /* Resources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | A9F8B6E01C2FE05400540306 /* PBXTargetDependency */, 222 | ); 223 | name = GoTaoTests; 224 | productName = GoTaoTests; 225 | productReference = A9F8B6DE1C2FE05400540306 /* GoTaoTests.xctest */; 226 | productType = "com.apple.product-type.bundle.unit-test"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | A9F8B6C21C2FE05400540306 /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | LastSwiftUpdateCheck = 0720; 235 | LastUpgradeCheck = 1010; 236 | ORGANIZATIONNAME = markNote; 237 | TargetAttributes = { 238 | A9F8B6C91C2FE05400540306 = { 239 | CreatedOnToolsVersion = 7.2; 240 | DevelopmentTeam = 7K8V8WEEN5; 241 | LastSwiftMigration = 1010; 242 | }; 243 | A9F8B6DD1C2FE05400540306 = { 244 | CreatedOnToolsVersion = 7.2; 245 | LastSwiftMigration = 1010; 246 | TestTargetID = A9F8B6C91C2FE05400540306; 247 | }; 248 | }; 249 | }; 250 | buildConfigurationList = A9F8B6C51C2FE05400540306 /* Build configuration list for PBXProject "GoTao" */; 251 | compatibilityVersion = "Xcode 6.3"; 252 | developmentRegion = English; 253 | hasScannedForEncodings = 0; 254 | knownRegions = ( 255 | en, 256 | Base, 257 | ); 258 | mainGroup = A9F8B6C11C2FE05400540306; 259 | productRefGroup = A9F8B6CB1C2FE05400540306 /* Products */; 260 | projectDirPath = ""; 261 | projectRoot = ""; 262 | targets = ( 263 | A9F8B6C91C2FE05400540306 /* GoTao */, 264 | A9F8B6DD1C2FE05400540306 /* GoTaoTests */, 265 | ); 266 | }; 267 | /* End PBXProject section */ 268 | 269 | /* Begin PBXResourcesBuildPhase section */ 270 | A9F8B6C81C2FE05400540306 /* Resources */ = { 271 | isa = PBXResourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | A9F8B6D81C2FE05400540306 /* LaunchScreen.storyboard in Resources */, 275 | A96C71B21C3005040024C263 /* board_back.png in Resources */, 276 | A9AA0C751C34110E00C76284 /* 001.sgf in Resources */, 277 | A96C71B11C3005040024C263 /* Black.png in Resources */, 278 | A96C71B31C3005040024C263 /* White.png in Resources */, 279 | A9F8B6D51C2FE05400540306 /* Assets.xcassets in Resources */, 280 | A9F8B6D31C2FE05400540306 /* Main.storyboard in Resources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | A9F8B6DC1C2FE05400540306 /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXResourcesBuildPhase section */ 292 | 293 | /* Begin PBXSourcesBuildPhase section */ 294 | A9F8B6C61C2FE05400540306 /* Sources */ = { 295 | isa = PBXSourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | A96C71AA1C2FF4A80024C263 /* GameInfo.swift in Sources */, 299 | A9F2779C1C2FEA9900B19300 /* Move.swift in Sources */, 300 | A9298FAD1C31810800094259 /* StringExtension.swift in Sources */, 301 | A9F2779D1C2FEA9900B19300 /* StoneType.swift in Sources */, 302 | A9298FAA1C31730F00094259 /* SGFParser.swift in Sources */, 303 | A9F2779E1C2FEA9900B19300 /* Location.swift in Sources */, 304 | A96C71AC1C3001B50024C263 /* GobanView.swift in Sources */, 305 | A9F8B6D01C2FE05400540306 /* HomeViewController.swift in Sources */, 306 | A96C71A81C2FF1D60024C263 /* GameViewController.swift in Sources */, 307 | A9F8B6CE1C2FE05400540306 /* AppDelegate.swift in Sources */, 308 | A9AA0C781C360D9900C76284 /* MoveGroup.swift in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | A9F8B6DA1C2FE05400540306 /* Sources */ = { 313 | isa = PBXSourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | A9F8B6E31C2FE05400540306 /* GoTaoTests.swift in Sources */, 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | /* End PBXSourcesBuildPhase section */ 321 | 322 | /* Begin PBXTargetDependency section */ 323 | A9F8B6E01C2FE05400540306 /* PBXTargetDependency */ = { 324 | isa = PBXTargetDependency; 325 | target = A9F8B6C91C2FE05400540306 /* GoTao */; 326 | targetProxy = A9F8B6DF1C2FE05400540306 /* PBXContainerItemProxy */; 327 | }; 328 | /* End PBXTargetDependency section */ 329 | 330 | /* Begin PBXVariantGroup section */ 331 | A9F8B6D11C2FE05400540306 /* Main.storyboard */ = { 332 | isa = PBXVariantGroup; 333 | children = ( 334 | A9F8B6D21C2FE05400540306 /* Base */, 335 | ); 336 | name = Main.storyboard; 337 | sourceTree = ""; 338 | }; 339 | A9F8B6D61C2FE05400540306 /* LaunchScreen.storyboard */ = { 340 | isa = PBXVariantGroup; 341 | children = ( 342 | A9F8B6D71C2FE05400540306 /* Base */, 343 | ); 344 | name = LaunchScreen.storyboard; 345 | sourceTree = ""; 346 | }; 347 | /* End PBXVariantGroup section */ 348 | 349 | /* Begin XCBuildConfiguration section */ 350 | A9F8B6F01C2FE05400540306 /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ALWAYS_SEARCH_USER_PATHS = NO; 354 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 355 | CLANG_CXX_LIBRARY = "libc++"; 356 | CLANG_ENABLE_MODULES = YES; 357 | CLANG_ENABLE_OBJC_ARC = YES; 358 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 359 | CLANG_WARN_BOOL_CONVERSION = YES; 360 | CLANG_WARN_COMMA = YES; 361 | CLANG_WARN_CONSTANT_CONVERSION = YES; 362 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 363 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 364 | CLANG_WARN_EMPTY_BODY = YES; 365 | CLANG_WARN_ENUM_CONVERSION = YES; 366 | CLANG_WARN_INFINITE_RECURSION = YES; 367 | CLANG_WARN_INT_CONVERSION = YES; 368 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 369 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 370 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 371 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 372 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 373 | CLANG_WARN_STRICT_PROTOTYPES = YES; 374 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = dwarf; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | ENABLE_TESTABILITY = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu99; 383 | GCC_DYNAMIC_NO_PIC = NO; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | GCC_OPTIMIZATION_LEVEL = 0; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 397 | MTL_ENABLE_DEBUG_INFO = YES; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = iphoneos; 400 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 401 | }; 402 | name = Debug; 403 | }; 404 | A9F8B6F11C2FE05400540306 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_MODULES = YES; 411 | CLANG_ENABLE_OBJC_ARC = YES; 412 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 413 | CLANG_WARN_BOOL_CONVERSION = YES; 414 | CLANG_WARN_COMMA = YES; 415 | CLANG_WARN_CONSTANT_CONVERSION = YES; 416 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 417 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 418 | CLANG_WARN_EMPTY_BODY = YES; 419 | CLANG_WARN_ENUM_CONVERSION = YES; 420 | CLANG_WARN_INFINITE_RECURSION = YES; 421 | CLANG_WARN_INT_CONVERSION = YES; 422 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 423 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 426 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 427 | CLANG_WARN_STRICT_PROTOTYPES = YES; 428 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 429 | CLANG_WARN_UNREACHABLE_CODE = YES; 430 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 431 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 432 | COPY_PHASE_STRIP = NO; 433 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 434 | ENABLE_NS_ASSERTIONS = NO; 435 | ENABLE_STRICT_OBJC_MSGSEND = YES; 436 | GCC_C_LANGUAGE_STANDARD = gnu99; 437 | GCC_NO_COMMON_BLOCKS = YES; 438 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 439 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 440 | GCC_WARN_UNDECLARED_SELECTOR = YES; 441 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 442 | GCC_WARN_UNUSED_FUNCTION = YES; 443 | GCC_WARN_UNUSED_VARIABLE = YES; 444 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 445 | MTL_ENABLE_DEBUG_INFO = NO; 446 | SDKROOT = iphoneos; 447 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 448 | VALIDATE_PRODUCT = YES; 449 | }; 450 | name = Release; 451 | }; 452 | A9F8B6F31C2FE05400540306 /* Debug */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | DEVELOPMENT_TEAM = 7K8V8WEEN5; 457 | INFOPLIST_FILE = GoTao/Info.plist; 458 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 459 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 460 | PRODUCT_BUNDLE_IDENTIFIER = GoTao; 461 | PRODUCT_NAME = "$(TARGET_NAME)"; 462 | SWIFT_VERSION = 4.2; 463 | }; 464 | name = Debug; 465 | }; 466 | A9F8B6F41C2FE05400540306 /* Release */ = { 467 | isa = XCBuildConfiguration; 468 | buildSettings = { 469 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 470 | DEVELOPMENT_TEAM = 7K8V8WEEN5; 471 | INFOPLIST_FILE = GoTao/Info.plist; 472 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 474 | PRODUCT_BUNDLE_IDENTIFIER = GoTao; 475 | PRODUCT_NAME = "$(TARGET_NAME)"; 476 | SWIFT_VERSION = 4.2; 477 | }; 478 | name = Release; 479 | }; 480 | A9F8B6F61C2FE05400540306 /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | BUNDLE_LOADER = "$(TEST_HOST)"; 484 | INFOPLIST_FILE = GoTaoTests/Info.plist; 485 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 486 | PRODUCT_BUNDLE_IDENTIFIER = markNote.GoTaoTests; 487 | PRODUCT_NAME = "$(TARGET_NAME)"; 488 | SWIFT_VERSION = 4.2; 489 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GoTao.app/GoTao"; 490 | }; 491 | name = Debug; 492 | }; 493 | A9F8B6F71C2FE05400540306 /* Release */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | BUNDLE_LOADER = "$(TEST_HOST)"; 497 | INFOPLIST_FILE = GoTaoTests/Info.plist; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 499 | PRODUCT_BUNDLE_IDENTIFIER = markNote.GoTaoTests; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | SWIFT_VERSION = 4.2; 502 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GoTao.app/GoTao"; 503 | }; 504 | name = Release; 505 | }; 506 | /* End XCBuildConfiguration section */ 507 | 508 | /* Begin XCConfigurationList section */ 509 | A9F8B6C51C2FE05400540306 /* Build configuration list for PBXProject "GoTao" */ = { 510 | isa = XCConfigurationList; 511 | buildConfigurations = ( 512 | A9F8B6F01C2FE05400540306 /* Debug */, 513 | A9F8B6F11C2FE05400540306 /* Release */, 514 | ); 515 | defaultConfigurationIsVisible = 0; 516 | defaultConfigurationName = Release; 517 | }; 518 | A9F8B6F21C2FE05400540306 /* Build configuration list for PBXNativeTarget "GoTao" */ = { 519 | isa = XCConfigurationList; 520 | buildConfigurations = ( 521 | A9F8B6F31C2FE05400540306 /* Debug */, 522 | A9F8B6F41C2FE05400540306 /* Release */, 523 | ); 524 | defaultConfigurationIsVisible = 0; 525 | defaultConfigurationName = Release; 526 | }; 527 | A9F8B6F51C2FE05400540306 /* Build configuration list for PBXNativeTarget "GoTaoTests" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | A9F8B6F61C2FE05400540306 /* Debug */, 531 | A9F8B6F71C2FE05400540306 /* Release */, 532 | ); 533 | defaultConfigurationIsVisible = 0; 534 | defaultConfigurationName = Release; 535 | }; 536 | /* End XCConfigurationList section */ 537 | }; 538 | rootObject = A9F8B6C21C2FE05400540306 /* Project object */; 539 | } 540 | -------------------------------------------------------------------------------- /GoTao/GoTao.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GoTao/GoTao.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GoTao/GoTao/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import UIKit 8 | 9 | @UIApplicationMain 10 | class AppDelegate: UIResponder, UIApplicationDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | func applicationWillResignActive(_ application: UIApplication) { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | func applicationDidEnterBackground(_ application: UIApplication) { 26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 28 | } 29 | 30 | func applicationWillEnterForeground(_ application: UIApplication) { 31 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 32 | } 33 | 34 | func applicationDidBecomeActive(_ application: UIApplication) { 35 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 36 | } 37 | 38 | func applicationWillTerminate(_ application: UIApplication) { 39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 40 | } 41 | 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /GoTao/GoTao/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /GoTao/GoTao/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /GoTao/GoTao/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /GoTao/GoTao/Game/GameViewController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import UIKit 8 | 9 | class GameViewController: UIViewController { 10 | @IBOutlet weak var _lblTitle:UILabel! 11 | @IBOutlet weak var _goban:GobanView! 12 | 13 | //var _currentMove = 0 14 | 15 | var _game:GameInfo = GameInfo() 16 | override func viewDidLoad() { 17 | 18 | super.viewDidLoad() 19 | 20 | if let filepath = Bundle.main.path(forResource: "001", ofType: "sgf") { 21 | 22 | //reading 23 | do { 24 | let kifu = try NSString(contentsOfFile: filepath, encoding: String.Encoding.utf8.rawValue) 25 | _game = SGFParser().parse(kifu as String) 26 | 27 | //_goban.moves = _game.allMoves 28 | showMoves() 29 | 30 | } 31 | catch {/* error handling here */} 32 | } 33 | } 34 | 35 | @IBAction func backClick(_ sender:UIBarItem){ 36 | _game.goBack() 37 | showMoves() 38 | } 39 | 40 | @IBAction func forwardClick(_ sender:UIBarItem){ 41 | _game.goForward() 42 | showMoves() 43 | } 44 | func showMoves(){ 45 | if _game.allMoves.count > 0 { 46 | //_goban.moves = _game.currentMoves() 47 | //_game.assignGroups() 48 | _game.moveTocurrent() 49 | _goban.moves = _game.currentMoves() 50 | 51 | _goban.setNeedsDisplay() 52 | _lblTitle.text = "\(_game.playerBlack) v \(_game.playerWhite) \(_game.currentMove + 1) / \(_game.allMoves.count)" 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /GoTao/GoTao/Game/GobanView.swift: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 5 | * https://github.com/marknote/GoTao 6 | */ 7 | 8 | import UIKit 9 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 10 | // Consider refactoring the code to use the non-optional operators. 11 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 12 | switch (lhs, rhs) { 13 | case let (l?, r?): 14 | return l < r 15 | case (nil, _?): 16 | return true 17 | default: 18 | return false 19 | } 20 | } 21 | 22 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 23 | // Consider refactoring the code to use the non-optional operators. 24 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 25 | switch (lhs, rhs) { 26 | case let (l?, r?): 27 | return l > r 28 | default: 29 | return rhs < lhs 30 | } 31 | } 32 | 33 | 34 | class GobanView: UIView { 35 | var moves:[Move]? 36 | 37 | override func draw(_ rect: CGRect) { 38 | 39 | let ctx = UIGraphicsGetCurrentContext() 40 | let rec = ctx?.boundingBoxOfClipPath 41 | let w = rec?.size.width; 42 | //back ground image 43 | let background = UIImage(named:"board_back") 44 | 45 | ctx?.draw(background!.cgImage!, in: rec!) 46 | let space = w! / 20.0 47 | drawLines(ctx!,space:space,w:w!) 48 | drawDots(ctx!,space:space) 49 | if (moves != nil && moves?.count > 0) { 50 | drawMoves(ctx!, stoneSize: space) 51 | } 52 | 53 | } 54 | func drawLines(_ ctx:CGContext,space:CGFloat, w:CGFloat){ 55 | ctx.setStrokeColor(red: 0, green: 0, blue: 0, alpha: 1) 56 | ctx.setLineWidth(0.4) 57 | ctx.beginPath() 58 | 59 | for i in 0...18 60 | { 61 | 62 | ctx.move(to: CGPoint(x: (CGFloat(i + 1))*space, y: space)) 63 | ctx.addLine(to: CGPoint(x: (CGFloat(i + 1))*space, y: w - space)) 64 | ctx.strokePath() 65 | } 66 | for i in 0...18 67 | { 68 | 69 | ctx.move(to: CGPoint(x: space, y: (CGFloat(i + 1))*space)); 70 | ctx.addLine(to: CGPoint(x: w - space, y: (CGFloat(i + 1))*space)); 71 | ctx.strokePath(); 72 | } 73 | 74 | } 75 | func drawDots(_ ctx:CGContext,space:CGFloat){ 76 | for i in 0...2 { 77 | for j in 0...2 { 78 | ctx.beginPath(); 79 | 80 | let frame = CGRect(x:CGFloat(1 + 3 + 6*i) * space - 0.5 * space, 81 | y:CGFloat(1 + 3 + 6*j) * space - 0.5 * space, 82 | width:space, 83 | height:space) 84 | 85 | ctx.addEllipse(in:frame) 86 | 87 | ctx.strokePath(); 88 | } 89 | } 90 | 91 | } 92 | 93 | func drawMoves(_ ctx:CGContext,stoneSize:CGFloat ){ 94 | // chess 95 | let imgBlack = UIImage(named: "Black.png") 96 | let imgWhite = UIImage(named: "White.png") 97 | 98 | let count = moves?.count 99 | let font = UIFont(name: "Helvetica Bold", size: 8.0) 100 | let textColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.8) 101 | let textFontAttributes = [ 102 | NSAttributedString.Key.font : font!, 103 | NSAttributedString.Key.foregroundColor: textColor 104 | ] 105 | 106 | for i in 0.. 0 { 122 | ctx.setStrokeColor(red: 1, green: 0, blue: 0, alpha: 1) 123 | let p = CGPoint(x: (CGFloat(move.location.x) + 0.5)*stoneSize, 124 | y: (CGFloat(move.location.y) + 0.5)*stoneSize); 125 | (move.groupName as NSString).draw(at: p, withAttributes:textFontAttributes) 126 | } 127 | } 128 | 129 | if count > 0 130 | { 131 | let move = moves![count! - 1] 132 | ctx.setLineWidth(1.8) 133 | 134 | ctx.setStrokeColor(red: 0, green: 1, blue: 0, alpha: 1) 135 | 136 | ctx.beginPath() 137 | let frame = CGRect(x:CGFloat(move.location.x) * stoneSize + 0.5 * stoneSize, 138 | y:CGFloat(move.location.y) * stoneSize + 0.5 * stoneSize, 139 | width:stoneSize, 140 | height:stoneSize) 141 | 142 | ctx.addEllipse(in:frame) 143 | 144 | ctx.strokePath() 145 | } 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /GoTao/GoTao/GameInfo.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | class GameInfo: NSObject { 10 | 11 | var title: String = "" 12 | var metaInfo: String = "" 13 | 14 | var playerWhite: String = "" 15 | var playerBlack: String = "" 16 | var rankWhite: String = "" 17 | var rankBlack: String = "" 18 | var result: String = "" 19 | var date:String = "" 20 | 21 | 22 | var allMoves: [Move] = [Move]() 23 | var currentMove: Int = 0 24 | 25 | func goBack(){ 26 | currentMove -= 1 27 | if (currentMove < 0){ 28 | currentMove = 0 29 | } 30 | } 31 | 32 | func goForward(){ 33 | currentMove += 1 34 | if (currentMove > allMoves.count - 1){ 35 | currentMove = allMoves.count - 1 36 | } 37 | } 38 | 39 | 40 | func moveTocurrent(){ 41 | for m in allMoves { 42 | m.isDead = false 43 | m.handOfDead = -1 44 | } 45 | 46 | 47 | _ = playToHand(currentMove) 48 | } 49 | 50 | 51 | func playToHand(_ hand:Int)-> [MoveGroup]{ 52 | // the very beginning 53 | if hand == 0 { 54 | let group0 = MoveGroup() 55 | group0.name = "B0" 56 | group0.addMove(allMoves[0]) 57 | return [group0] 58 | } 59 | 60 | //the current step depends on the last status 61 | var groups = playToHand(hand - 1) 62 | let lastMove = allMoves[hand] 63 | let type = String(lastMove.type.rawValue) //"B" or "W" 64 | let groupsWithSameColor = groups.filter({$0.name.hasPrefix(type)}) 65 | var handled = false 66 | lastMove.groupName = "" 67 | for g in groupsWithSameColor { 68 | for move in g.allMoves { 69 | if move.isConnectedTo(lastMove) { 70 | if lastMove.groupName == "" { 71 | handled = true 72 | g.addMove(lastMove) 73 | } else { 74 | // last move is already in a group, current group needs to be merged into that group 75 | let groupToConnect = groupsWithSameColor.filter({$0.name == lastMove.groupName}).first! 76 | g.mergeTo(groupToConnect) 77 | } 78 | break 79 | } 80 | }// end for move 81 | }//end for g 82 | 83 | if !handled { 84 | let groupNew = MoveGroup() 85 | groupNew.name = "\(lastMove.type.rawValue)\(lastMove.handNumber)" 86 | groupNew.addMove(lastMove) 87 | groups.append(groupNew) 88 | } 89 | // filter out empty groups 90 | let liveGroups = groups.filter({$0.allMoves.count > 0 }) 91 | let allOccupied = occupiedLocations(allMoves.filter({$0.handNumber <= hand && $0.handOfDead == -1})) 92 | // only need to check opposite party 93 | let oppositeGroups = liveGroups.filter({!$0.name.hasPrefix(type)}) 94 | for grp in oppositeGroups{ 95 | let liberty = grp.calculateLiberty(allOccupied) 96 | if liberty == 0 { 97 | for move in grp.allMoves { 98 | move.isDead = true 99 | move.handOfDead = hand 100 | } 101 | grp.isDead = true 102 | } 103 | } 104 | 105 | 106 | return liveGroups.filter({!$0.isDead }) 107 | 108 | } 109 | 110 | 111 | 112 | func currentMoves()->[Move]{ 113 | let moves = allMoves.filter({$0.handNumber <= currentMove && $0.handOfDead == -1}) 114 | return moves 115 | } 116 | 117 | func occupiedLocations(_ moves: [Move])->[Location]{ 118 | var occupied = [Location]() 119 | //let array = currentMoves() 120 | for m in moves{ 121 | if m.isDead { 122 | continue 123 | } 124 | if !occupied.contains(m.location){ 125 | occupied.append(m.location) 126 | } 127 | } 128 | return occupied 129 | } 130 | 131 | 132 | 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /GoTao/GoTao/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import UIKit 8 | 9 | open class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 10 | 11 | var _games:[GameInfo] = [GameInfo]() 12 | @IBOutlet weak var _tblGames:UITableView! 13 | 14 | override open func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | let game1 = GameInfo() 18 | game1.title = "第一届日本最强者决定战吴清源对高川格" 19 | game1.metaInfo = "对弈时间:1957年2月20日" 20 | _games.append(game1) 21 | 22 | let game2 = GameInfo() 23 | game2.title = "MarkNote对弈李昌镐" 24 | game2.metaInfo = "对弈时间:2015年12月28日" 25 | _games.append(game2) 26 | } 27 | 28 | @objc open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 29 | return _games.count 30 | } 31 | 32 | @objc open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 33 | let row = indexPath.row 34 | let game = _games[row] 35 | 36 | let cell = _tblGames.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) 37 | cell.textLabel?.text = game.title 38 | cell.detailTextLabel?.text = game.metaInfo 39 | return cell 40 | 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /GoTao/GoTao/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /GoTao/GoTao/Models/Location.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | class Location: NSObject { 10 | var x:UInt32 11 | var y:UInt32 12 | init(x:UInt32,y:UInt32) { 13 | self.x = x 14 | self.y = y 15 | } 16 | 17 | func distance() ->UInt32{ 18 | return self.x * self.x + self.y * self.y 19 | } 20 | 21 | override func isEqual(_ object: Any?) -> Bool { 22 | if let obj = object as? Location { 23 | return x == obj.x && y == obj.y 24 | } else { 25 | return false 26 | } 27 | } 28 | 29 | func isConnectedTo(_ another:Location)->Bool{ 30 | let deltaX = abs(Int(x) - Int(another.x)) 31 | if deltaX > 1 { 32 | return false 33 | } 34 | let deltaY = abs(Int(self.y) - Int(another.y)) 35 | return deltaX + deltaY == 1 36 | } 37 | 38 | override var description:String { 39 | return "x:\(x) y:\(y)" 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /GoTao/GoTao/Models/Move.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | class Move: NSObject { 10 | 11 | var location: Location 12 | var type:StoneType 13 | var isDead:Bool = false 14 | var groupName = "" 15 | var handNumber = 0 //the order of move 16 | var handOfDead = -1 // in which step the move is caculated to be dead 17 | 18 | 19 | init(type:StoneType,loc:Location,hand:Int) { 20 | self.location = loc 21 | self.type = type 22 | self.handNumber = hand 23 | } 24 | init(step:String, hand:Int){ 25 | let color = StoneType(rawValue:step[step.startIndex]) 26 | let x = step.asciiValueAt(pos: 2) - "a".asciiValue 27 | let y = step.asciiValueAt(pos: 3) - "a".asciiValue 28 | 29 | self.type = color! 30 | self.location = Location(x: x,y: y) 31 | self.handNumber = hand 32 | } 33 | 34 | func isConnectedTo(_ another:Move)->Bool{ 35 | return (self.type == another.type) && self.location.isConnectedTo(another.location) 36 | 37 | } 38 | 39 | 40 | 41 | func calculateLiberty(_ occupied:[Location])->Int{ 42 | var liberty = 4 43 | if (location.x == 0 || location.x == 19 || location.y == 0 || location.y == 19){ 44 | liberty = 3 45 | if (location.x == 0 && location.y == 0 46 | || location.x == 19 && location.y == 0 47 | || location.x == 0 && location.y == 19 48 | || location.x == 19 && location.y == 19 49 | ){ 50 | liberty = 2 51 | } 52 | } 53 | for l in occupied { 54 | if self.location.isEqual(l) { 55 | continue 56 | } 57 | if self.location.isConnectedTo(l) { 58 | liberty -= 1 59 | } 60 | } 61 | return liberty 62 | } 63 | 64 | override var description:String { 65 | return "\(type) x:\(location.x) y:\(location.y)" 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /GoTao/GoTao/Models/MoveGroup.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | class MoveGroup:NSObject{ 10 | 11 | var type:StoneType = .black 12 | var sequence:Int = 0 13 | var name = "" 14 | var allMoves: [Move] = [Move]() 15 | var isDead = false 16 | 17 | 18 | func addMove(_ m:Move){ 19 | 20 | m.groupName = name 21 | allMoves.append(m) 22 | 23 | } 24 | 25 | func calculateLiberty(_ occupied:[Location])->Int{ 26 | return allMoves.reduce(0, {$0 + $1.calculateLiberty(occupied)} ) 27 | } 28 | 29 | func mergeTo(_ groupToMerge:MoveGroup) { 30 | for move in allMoves { 31 | 32 | groupToMerge.addMove(move) 33 | } 34 | allMoves.removeAll() 35 | } 36 | override var description: String { 37 | return name 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GoTao/GoTao/Models/SGFParser.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | open class SGFParser { 10 | 11 | func parse(_ kifu:String)->GameInfo{ 12 | let game = GameInfo() 13 | let nodes = kifu.split(separator: ";") 14 | .map(String.init) 15 | 16 | if nodes.count >= 2 { 17 | let metaNode = nodes[1] 18 | if let pb = metaNode.findValueWithTags("PB[", endTag: "]"){ 19 | game.playerBlack = pb 20 | } 21 | if let pw = metaNode.findValueWithTags("PW[", endTag: "]"){ 22 | game.playerWhite = pw 23 | } 24 | 25 | if let br = metaNode.findValueWithTags("BR[", endTag: "]"){ 26 | game.rankBlack = br 27 | } 28 | if let wr = metaNode.findValueWithTags("WR[", endTag: "]"){ 29 | game.rankWhite = wr 30 | } 31 | if let re = metaNode.findValueWithTags("RE[", endTag: "]"){ 32 | game.result = re 33 | } 34 | if let dt = metaNode.findValueWithTags("DT[", endTag: "]"){ 35 | game.date = dt 36 | } 37 | } 38 | 39 | let moveSteps = nodes 40 | .filter{($0.hasPrefix("B[")||$0.hasPrefix("W["))&&$0.count >= 4 && $0[4] == "]"} 41 | game.allMoves = [Move]() 42 | if moveSteps.count > 0 { 43 | for i in 0...moveSteps.count - 1 { 44 | let move = Move(step: moveSteps[i], hand: i) 45 | game.allMoves.append(move) 46 | } 47 | } 48 | return game 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GoTao/GoTao/Models/StoneType.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | enum StoneType: Character { 10 | case black = "B" 11 | case white = "W" 12 | } 13 | -------------------------------------------------------------------------------- /GoTao/GoTao/StringExtension.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import Foundation 8 | 9 | extension String { 10 | subscript (i: Int) -> Character { 11 | return self[index(startIndex, offsetBy: i)] 12 | } 13 | var asciiValue: UInt32 { 14 | return asciiValueAt(pos: 0) 15 | } 16 | func asciiValueAt(pos: UInt32) -> UInt32 { 17 | guard count > 0 && Int(pos) < count else { return 0 } 18 | return (self[Int(pos)]).unicodeScalarsValue 19 | } 20 | 21 | func findValueWithTags(_ beginTag:String,endTag:String)->String?{ 22 | if let beginLocation = self.range(of: beginTag) { 23 | let startIndex = beginLocation.upperBound 24 | let rest = String(self[startIndex...]) 25 | if let endRang = rest.range(of: endTag){ 26 | return String(rest[.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /GoTao/GoTaoUITests/GoTaoUITests.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2015, MarkNote. (MIT Licensed) 4 | * https://github.com/marknote/GoTao 5 | */ 6 | 7 | import XCTest 8 | 9 | class GoTaoUITests: XCTestCase { 10 | 11 | override func setUp() { 12 | super.setUp() 13 | 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 19 | XCUIApplication().launch() 20 | 21 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 22 | } 23 | 24 | override func tearDown() { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | super.tearDown() 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /GoTao/GoTaoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 MarkNote 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoTao 2 | Swift 3 based iOS app to read game records and play the game of Go, also known as Weiqi. 3 | 4 | Screenshot: 5 | ![screenshot](images/play.gif) 6 | 7 | ## Features 8 | - Parse go game records(aka kifu) 9 | - Render moves on GoBan board 10 | - Assign groups 11 | - Calculate liberty and determine life and death 12 | 13 | 14 | -------------------------------------------------------------------------------- /images/play.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marknote/GoTao/a6c6ee24597565e729cb9f97a5fb8f9c72d503fe/images/play.gif -------------------------------------------------------------------------------- /images/screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marknote/GoTao/a6c6ee24597565e729cb9f97a5fb8f9c72d503fe/images/screen.jpg -------------------------------------------------------------------------------- /images/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marknote/GoTao/a6c6ee24597565e729cb9f97a5fb8f9c72d503fe/images/screen.png --------------------------------------------------------------------------------