Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
1.1 --- a/English.lproj/MainMenu.nib/designable.nib Thu May 29 15:04:06 2008 -0700
1.2 +++ b/English.lproj/MainMenu.nib/designable.nib Thu Jul 03 17:44:30 2008 -0700
1.3 @@ -2,14 +2,15 @@
1.4 <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02">
1.5 <data>
1.6 <int key="IBDocument.SystemTarget">0</int>
1.7 - <string key="IBDocument.SystemVersion">9C31</string>
1.8 - <string key="IBDocument.InterfaceBuilderVersion">644</string>
1.9 - <string key="IBDocument.AppKitVersion">949.26</string>
1.10 + <string key="IBDocument.SystemVersion">9D34</string>
1.11 + <string key="IBDocument.InterfaceBuilderVersion">667</string>
1.12 + <string key="IBDocument.AppKitVersion">949.33</string>
1.13 <string key="IBDocument.HIToolboxVersion">352.00</string>
1.14 <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
1.15 <bool key="EncodedWithXMLCoder">YES</bool>
1.16 - <integer value="380"/>
1.17 <integer value="372"/>
1.18 + <integer value="407"/>
1.19 + <integer value="410"/>
1.20 </object>
1.21 <object class="NSArray" key="IBDocument.PluginDependencies">
1.22 <bool key="EncodedWithXMLCoder">YES</bool>
1.23 @@ -461,17 +462,6 @@
1.24 <reference key="NSOnImage" ref="484312224"/>
1.25 <reference key="NSMixedImage" ref="580230904"/>
1.26 </object>
1.27 - <object class="NSMenuItem" id="154289436">
1.28 - <reference key="NSMenu" ref="330268596"/>
1.29 - <bool key="NSIsDisabled">YES</bool>
1.30 - <bool key="NSIsSeparator">YES</bool>
1.31 - <string key="NSTitle"/>
1.32 - <string key="NSKeyEquiv"/>
1.33 - <int key="NSKeyEquivModMask">1048576</int>
1.34 - <int key="NSMnemonicLoc">2147483647</int>
1.35 - <reference key="NSOnImage" ref="484312224"/>
1.36 - <reference key="NSMixedImage" ref="580230904"/>
1.37 - </object>
1.38 <object class="NSMenuItem" id="630661937">
1.39 <reference key="NSMenu" ref="330268596"/>
1.40 <string key="NSTitle">Checkers</string>
1.41 @@ -512,6 +502,26 @@
1.42 <reference key="NSMixedImage" ref="580230904"/>
1.43 <int key="NSTag">3</int>
1.44 </object>
1.45 + <object class="NSMenuItem" id="154289436">
1.46 + <reference key="NSMenu" ref="330268596"/>
1.47 + <bool key="NSIsDisabled">YES</bool>
1.48 + <bool key="NSIsSeparator">YES</bool>
1.49 + <string key="NSTitle"/>
1.50 + <string key="NSKeyEquiv"/>
1.51 + <int key="NSKeyEquivModMask">1048576</int>
1.52 + <int key="NSMnemonicLoc">2147483647</int>
1.53 + <reference key="NSOnImage" ref="484312224"/>
1.54 + <reference key="NSMixedImage" ref="580230904"/>
1.55 + </object>
1.56 + <object class="NSMenuItem" id="913771137">
1.57 + <reference key="NSMenu" ref="330268596"/>
1.58 + <string key="NSTitle">Remote Opponent</string>
1.59 + <string key="NSKeyEquiv">r</string>
1.60 + <int key="NSKeyEquivModMask">1048576</int>
1.61 + <int key="NSMnemonicLoc">2147483647</int>
1.62 + <reference key="NSOnImage" ref="484312224"/>
1.63 + <reference key="NSMixedImage" ref="580230904"/>
1.64 + </object>
1.65 </object>
1.66 </object>
1.67 </object>
1.68 @@ -671,7 +681,7 @@
1.69 <reference key="NSSuperview" ref="439893737"/>
1.70 <bool key="NSEnabled">YES</bool>
1.71 <object class="NSSliderCell" key="NSCell" id="359916377">
1.72 - <int key="NSCellFlags">-2080244224</int>
1.73 + <int key="NSCellFlags">-2079981824</int>
1.74 <int key="NSCellFlags2">0</int>
1.75 <string key="NSContents"/>
1.76 <object class="NSFont" key="NSSupport">
1.77 @@ -690,13 +700,194 @@
1.78 <bool key="NSVertical">NO</bool>
1.79 </object>
1.80 </object>
1.81 + <object class="NSTextField" id="317712412">
1.82 + <reference key="NSNextResponder" ref="439893737"/>
1.83 + <int key="NSvFlags">292</int>
1.84 + <string key="NSFrame">{{55, 10}, {103, 17}}</string>
1.85 + <reference key="NSSuperview" ref="439893737"/>
1.86 + <bool key="NSEnabled">YES</bool>
1.87 + <object class="NSTextFieldCell" key="NSCell" id="786497560">
1.88 + <int key="NSCellFlags">68288064</int>
1.89 + <int key="NSCellFlags2">272630784</int>
1.90 + <string key="NSContents">Game Timeline:</string>
1.91 + <object class="NSFont" key="NSSupport" id="538625186">
1.92 + <string key="NSName">LucidaGrande</string>
1.93 + <double key="NSSize">1.300000e+01</double>
1.94 + <int key="NSfFlags">1044</int>
1.95 + </object>
1.96 + <reference key="NSControlView" ref="317712412"/>
1.97 + <object class="NSColor" key="NSBackgroundColor" id="637971919">
1.98 + <int key="NSColorSpace">6</int>
1.99 + <string key="NSCatalogName">System</string>
1.100 + <string key="NSColorName">controlColor</string>
1.101 + <object class="NSColor" key="NSColor">
1.102 + <int key="NSColorSpace">3</int>
1.103 + <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
1.104 + </object>
1.105 + </object>
1.106 + <object class="NSColor" key="NSTextColor" id="247842606">
1.107 + <int key="NSColorSpace">6</int>
1.108 + <string key="NSCatalogName">System</string>
1.109 + <string key="NSColorName">controlTextColor</string>
1.110 + <object class="NSColor" key="NSColor" id="122938274">
1.111 + <int key="NSColorSpace">3</int>
1.112 + <bytes key="NSWhite">MAA</bytes>
1.113 + </object>
1.114 + </object>
1.115 + </object>
1.116 + </object>
1.117 </object>
1.118 <string key="NSFrameSize">{1084, 617}</string>
1.119 <reference key="NSSuperview"/>
1.120 </object>
1.121 <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
1.122 + <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
1.123 <string key="NSFrameAutosaveName"/>
1.124 </object>
1.125 + <object class="NSWindowTemplate" id="679114231">
1.126 + <int key="NSWindowStyleMask">9</int>
1.127 + <int key="NSWindowBacking">2</int>
1.128 + <string key="NSWindowRect">{{196, 385}, {773, 125}}</string>
1.129 + <int key="NSWTFlags">-1543503872</int>
1.130 + <string key="NSWindowTitle">sheet</string>
1.131 + <string key="NSWindowClass">NSPanel</string>
1.132 + <nil key="NSViewClass"/>
1.133 + <object class="NSView" key="NSWindowView" id="658832775">
1.134 + <reference key="NSNextResponder"/>
1.135 + <int key="NSvFlags">256</int>
1.136 + <object class="NSMutableArray" key="NSSubviews">
1.137 + <bool key="EncodedWithXMLCoder">YES</bool>
1.138 + <object class="NSTextField" id="307591001">
1.139 + <reference key="NSNextResponder" ref="658832775"/>
1.140 + <int key="NSvFlags">266</int>
1.141 + <string key="NSFrame">{{157, 93}, {596, 19}}</string>
1.142 + <reference key="NSSuperview" ref="658832775"/>
1.143 + <bool key="NSEnabled">YES</bool>
1.144 + <object class="NSTextFieldCell" key="NSCell" id="541619463">
1.145 + <int key="NSCellFlags">-2072904127</int>
1.146 + <int key="NSCellFlags2">272761856</int>
1.147 + <string key="NSContents"/>
1.148 + <object class="NSFont" key="NSSupport" id="26">
1.149 + <string key="NSName">LucidaGrande</string>
1.150 + <double key="NSSize">1.100000e+01</double>
1.151 + <int key="NSfFlags">3100</int>
1.152 + </object>
1.153 + <reference key="NSControlView" ref="307591001"/>
1.154 + <bool key="NSDrawsBackground">YES</bool>
1.155 + <object class="NSColor" key="NSBackgroundColor" id="848173719">
1.156 + <int key="NSColorSpace">6</int>
1.157 + <string key="NSCatalogName">System</string>
1.158 + <string key="NSColorName">textBackgroundColor</string>
1.159 + <object class="NSColor" key="NSColor">
1.160 + <int key="NSColorSpace">3</int>
1.161 + <bytes key="NSWhite">MQA</bytes>
1.162 + </object>
1.163 + </object>
1.164 + <object class="NSColor" key="NSTextColor" id="855823417">
1.165 + <int key="NSColorSpace">6</int>
1.166 + <string key="NSCatalogName">System</string>
1.167 + <string key="NSColorName">textColor</string>
1.168 + <reference key="NSColor" ref="122938274"/>
1.169 + </object>
1.170 + </object>
1.171 + </object>
1.172 + <object class="NSTextField" id="661635616">
1.173 + <reference key="NSNextResponder" ref="658832775"/>
1.174 + <int key="NSvFlags">268</int>
1.175 + <string key="NSFrame">{{17, 92}, {135, 17}}</string>
1.176 + <reference key="NSSuperview" ref="658832775"/>
1.177 + <bool key="NSEnabled">YES</bool>
1.178 + <object class="NSTextFieldCell" key="NSCell" id="262221757">
1.179 + <int key="NSCellFlags">68288064</int>
1.180 + <int key="NSCellFlags2">71304192</int>
1.181 + <string key="NSContents">Your move was:</string>
1.182 + <reference key="NSSupport" ref="538625186"/>
1.183 + <reference key="NSControlView" ref="661635616"/>
1.184 + <reference key="NSBackgroundColor" ref="637971919"/>
1.185 + <reference key="NSTextColor" ref="247842606"/>
1.186 + </object>
1.187 + </object>
1.188 + <object class="NSTextField" id="545842217">
1.189 + <reference key="NSNextResponder" ref="658832775"/>
1.190 + <int key="NSvFlags">266</int>
1.191 + <string key="NSFrame">{{157, 63}, {596, 19}}</string>
1.192 + <reference key="NSSuperview" ref="658832775"/>
1.193 + <bool key="NSEnabled">YES</bool>
1.194 + <object class="NSTextFieldCell" key="NSCell" id="120902258">
1.195 + <int key="NSCellFlags">-1804468671</int>
1.196 + <int key="NSCellFlags2">272761856</int>
1.197 + <string key="NSContents"/>
1.198 + <reference key="NSSupport" ref="26"/>
1.199 + <reference key="NSControlView" ref="545842217"/>
1.200 + <bool key="NSDrawsBackground">YES</bool>
1.201 + <reference key="NSBackgroundColor" ref="848173719"/>
1.202 + <reference key="NSTextColor" ref="855823417"/>
1.203 + </object>
1.204 + </object>
1.205 + <object class="NSTextField" id="874656297">
1.206 + <reference key="NSNextResponder" ref="658832775"/>
1.207 + <int key="NSvFlags">268</int>
1.208 + <string key="NSFrame">{{17, 62}, {135, 17}}</string>
1.209 + <reference key="NSSuperview" ref="658832775"/>
1.210 + <bool key="NSEnabled">YES</bool>
1.211 + <object class="NSTextFieldCell" key="NSCell" id="338639883">
1.212 + <int key="NSCellFlags">68288064</int>
1.213 + <int key="NSCellFlags2">71304192</int>
1.214 + <string type="base64-UTF8" key="NSContents">T3Bwb25lbnTigJlzIG1vdmUgaXM6A</string>
1.215 + <reference key="NSSupport" ref="538625186"/>
1.216 + <reference key="NSControlView" ref="874656297"/>
1.217 + <reference key="NSBackgroundColor" ref="637971919"/>
1.218 + <reference key="NSTextColor" ref="247842606"/>
1.219 + </object>
1.220 + </object>
1.221 + <object class="NSButton" id="313391357">
1.222 + <reference key="NSNextResponder" ref="658832775"/>
1.223 + <int key="NSvFlags">265</int>
1.224 + <string key="NSFrame">{{663, 12}, {97, 32}}</string>
1.225 + <reference key="NSSuperview" ref="658832775"/>
1.226 + <int key="NSTag">1</int>
1.227 + <bool key="NSEnabled">YES</bool>
1.228 + <object class="NSButtonCell" key="NSCell" id="868968858">
1.229 + <int key="NSCellFlags">67239424</int>
1.230 + <int key="NSCellFlags2">134217728</int>
1.231 + <string key="NSContents">Continue</string>
1.232 + <reference key="NSSupport" ref="538625186"/>
1.233 + <reference key="NSControlView" ref="313391357"/>
1.234 + <int key="NSButtonFlags">-2038284033</int>
1.235 + <int key="NSButtonFlags2">129</int>
1.236 + <string key="NSAlternateContents"/>
1.237 + <string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
1.238 + <int key="NSPeriodicDelay">200</int>
1.239 + <int key="NSPeriodicInterval">25</int>
1.240 + </object>
1.241 + </object>
1.242 + <object class="NSButton" id="1027728059">
1.243 + <reference key="NSNextResponder" ref="658832775"/>
1.244 + <int key="NSvFlags">265</int>
1.245 + <string key="NSFrame">{{566, 12}, {97, 32}}</string>
1.246 + <reference key="NSSuperview" ref="658832775"/>
1.247 + <bool key="NSEnabled">YES</bool>
1.248 + <object class="NSButtonCell" key="NSCell" id="466169840">
1.249 + <int key="NSCellFlags">67239424</int>
1.250 + <int key="NSCellFlags2">134217728</int>
1.251 + <string key="NSContents">Quit</string>
1.252 + <reference key="NSSupport" ref="538625186"/>
1.253 + <reference key="NSControlView" ref="1027728059"/>
1.254 + <int key="NSButtonFlags">-2038284033</int>
1.255 + <int key="NSButtonFlags2">268435585</int>
1.256 + <string key="NSAlternateContents"/>
1.257 + <string key="NSKeyEquivalent">q</string>
1.258 + <int key="NSPeriodicDelay">200</int>
1.259 + <int key="NSPeriodicInterval">25</int>
1.260 + </object>
1.261 + </object>
1.262 + </object>
1.263 + <string key="NSFrameSize">{773, 125}</string>
1.264 + <reference key="NSSuperview"/>
1.265 + </object>
1.266 + <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
1.267 + <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
1.268 + </object>
1.269 </object>
1.270 <object class="IBObjectContainer" key="IBDocument.Objects">
1.271 <object class="NSMutableArray" key="connectionRecords">
1.272 @@ -997,6 +1188,86 @@
1.273 </object>
1.274 <int key="connectionID">404</int>
1.275 </object>
1.276 + <object class="IBConnectionRecord">
1.277 + <object class="IBActionConnection" key="connection">
1.278 + <string key="label">toggleRemoteOpponent:</string>
1.279 + <reference key="source" ref="1060834180"/>
1.280 + <reference key="destination" ref="913771137"/>
1.281 + </object>
1.282 + <int key="connectionID">408</int>
1.283 + </object>
1.284 + <object class="IBConnectionRecord">
1.285 + <object class="IBOutletConnection" key="connection">
1.286 + <string key="label">_myMoveURLField</string>
1.287 + <reference key="source" ref="1060834180"/>
1.288 + <reference key="destination" ref="307591001"/>
1.289 + </object>
1.290 + <int key="connectionID">425</int>
1.291 + </object>
1.292 + <object class="IBConnectionRecord">
1.293 + <object class="IBOutletConnection" key="connection">
1.294 + <string key="label">_opponentsMoveURLField</string>
1.295 + <reference key="source" ref="1060834180"/>
1.296 + <reference key="destination" ref="545842217"/>
1.297 + </object>
1.298 + <int key="connectionID">426</int>
1.299 + </object>
1.300 + <object class="IBConnectionRecord">
1.301 + <object class="IBOutletConnection" key="connection">
1.302 + <string key="label">_opponentsMoveSheet</string>
1.303 + <reference key="source" ref="1060834180"/>
1.304 + <reference key="destination" ref="679114231"/>
1.305 + </object>
1.306 + <int key="connectionID">427</int>
1.307 + </object>
1.308 + <object class="IBConnectionRecord">
1.309 + <object class="IBActionConnection" key="connection">
1.310 + <string key="label">dismissSheet:</string>
1.311 + <reference key="source" ref="1060834180"/>
1.312 + <reference key="destination" ref="313391357"/>
1.313 + </object>
1.314 + <int key="connectionID">428</int>
1.315 + </object>
1.316 + <object class="IBConnectionRecord">
1.317 + <object class="IBActionConnection" key="connection">
1.318 + <string key="label">dismissSheet:</string>
1.319 + <reference key="source" ref="1060834180"/>
1.320 + <reference key="destination" ref="1027728059"/>
1.321 + </object>
1.322 + <int key="connectionID">429</int>
1.323 + </object>
1.324 + <object class="IBConnectionRecord">
1.325 + <object class="IBOutletConnection" key="connection">
1.326 + <string key="label">nextKeyView</string>
1.327 + <reference key="source" ref="307591001"/>
1.328 + <reference key="destination" ref="545842217"/>
1.329 + </object>
1.330 + <int key="connectionID">430</int>
1.331 + </object>
1.332 + <object class="IBConnectionRecord">
1.333 + <object class="IBOutletConnection" key="connection">
1.334 + <string key="label">nextKeyView</string>
1.335 + <reference key="source" ref="545842217"/>
1.336 + <reference key="destination" ref="313391357"/>
1.337 + </object>
1.338 + <int key="connectionID">431</int>
1.339 + </object>
1.340 + <object class="IBConnectionRecord">
1.341 + <object class="IBOutletConnection" key="connection">
1.342 + <string key="label">nextKeyView</string>
1.343 + <reference key="source" ref="313391357"/>
1.344 + <reference key="destination" ref="1027728059"/>
1.345 + </object>
1.346 + <int key="connectionID">432</int>
1.347 + </object>
1.348 + <object class="IBConnectionRecord">
1.349 + <object class="IBOutletConnection" key="connection">
1.350 + <string key="label">nextKeyView</string>
1.351 + <reference key="source" ref="1027728059"/>
1.352 + <reference key="destination" ref="307591001"/>
1.353 + </object>
1.354 + <int key="connectionID">433</int>
1.355 + </object>
1.356 </object>
1.357 <object class="IBMutableOrderedSet" key="objectRecords">
1.358 <object class="NSArray" key="orderedObjects">
1.359 @@ -1455,6 +1726,7 @@
1.360 <bool key="EncodedWithXMLCoder">YES</bool>
1.361 <reference ref="1060834180"/>
1.362 <reference ref="210596633"/>
1.363 + <reference ref="317712412"/>
1.364 </object>
1.365 <reference key="parent" ref="972006081"/>
1.366 </object>
1.367 @@ -1490,9 +1762,10 @@
1.368 <reference ref="394352720"/>
1.369 <reference ref="538211827"/>
1.370 <reference ref="739065818"/>
1.371 - <reference ref="154289436"/>
1.372 <reference ref="778498721"/>
1.373 <reference ref="630661937"/>
1.374 + <reference ref="154289436"/>
1.375 + <reference ref="913771137"/>
1.376 </object>
1.377 <reference key="parent" ref="517895433"/>
1.378 </object>
1.379 @@ -1517,11 +1790,6 @@
1.380 <reference key="parent" ref="330268596"/>
1.381 </object>
1.382 <object class="IBObjectRecord">
1.383 - <int key="objectID">387</int>
1.384 - <reference key="object" ref="154289436"/>
1.385 - <reference key="parent" ref="330268596"/>
1.386 - </object>
1.387 - <object class="IBObjectRecord">
1.388 <int key="objectID">393</int>
1.389 <reference key="object" ref="778498721"/>
1.390 <reference key="parent" ref="330268596"/>
1.391 @@ -1540,6 +1808,137 @@
1.392 <reference key="object" ref="359916377"/>
1.393 <reference key="parent" ref="210596633"/>
1.394 </object>
1.395 + <object class="IBObjectRecord">
1.396 + <int key="objectID">405</int>
1.397 + <reference key="object" ref="317712412"/>
1.398 + <object class="NSMutableArray" key="children">
1.399 + <bool key="EncodedWithXMLCoder">YES</bool>
1.400 + <reference ref="786497560"/>
1.401 + </object>
1.402 + <reference key="parent" ref="439893737"/>
1.403 + </object>
1.404 + <object class="IBObjectRecord">
1.405 + <int key="objectID">406</int>
1.406 + <reference key="object" ref="786497560"/>
1.407 + <reference key="parent" ref="317712412"/>
1.408 + </object>
1.409 + <object class="IBObjectRecord">
1.410 + <int key="objectID">387</int>
1.411 + <reference key="object" ref="154289436"/>
1.412 + <reference key="parent" ref="330268596"/>
1.413 + </object>
1.414 + <object class="IBObjectRecord">
1.415 + <int key="objectID">407</int>
1.416 + <reference key="object" ref="913771137"/>
1.417 + <reference key="parent" ref="330268596"/>
1.418 + </object>
1.419 + <object class="IBObjectRecord">
1.420 + <int key="objectID">409</int>
1.421 + <reference key="object" ref="679114231"/>
1.422 + <object class="NSMutableArray" key="children">
1.423 + <bool key="EncodedWithXMLCoder">YES</bool>
1.424 + <reference ref="658832775"/>
1.425 + </object>
1.426 + <reference key="parent" ref="1049"/>
1.427 + </object>
1.428 + <object class="IBObjectRecord">
1.429 + <int key="objectID">410</int>
1.430 + <reference key="object" ref="658832775"/>
1.431 + <object class="NSMutableArray" key="children">
1.432 + <bool key="EncodedWithXMLCoder">YES</bool>
1.433 + <reference ref="307591001"/>
1.434 + <reference ref="661635616"/>
1.435 + <reference ref="545842217"/>
1.436 + <reference ref="874656297"/>
1.437 + <reference ref="313391357"/>
1.438 + <reference ref="1027728059"/>
1.439 + </object>
1.440 + <reference key="parent" ref="679114231"/>
1.441 + </object>
1.442 + <object class="IBObjectRecord">
1.443 + <int key="objectID">411</int>
1.444 + <reference key="object" ref="307591001"/>
1.445 + <object class="NSMutableArray" key="children">
1.446 + <bool key="EncodedWithXMLCoder">YES</bool>
1.447 + <reference ref="541619463"/>
1.448 + </object>
1.449 + <reference key="parent" ref="658832775"/>
1.450 + </object>
1.451 + <object class="IBObjectRecord">
1.452 + <int key="objectID">412</int>
1.453 + <reference key="object" ref="541619463"/>
1.454 + <reference key="parent" ref="307591001"/>
1.455 + </object>
1.456 + <object class="IBObjectRecord">
1.457 + <int key="objectID">415</int>
1.458 + <reference key="object" ref="661635616"/>
1.459 + <object class="NSMutableArray" key="children">
1.460 + <bool key="EncodedWithXMLCoder">YES</bool>
1.461 + <reference ref="262221757"/>
1.462 + </object>
1.463 + <reference key="parent" ref="658832775"/>
1.464 + </object>
1.465 + <object class="IBObjectRecord">
1.466 + <int key="objectID">416</int>
1.467 + <reference key="object" ref="262221757"/>
1.468 + <reference key="parent" ref="661635616"/>
1.469 + </object>
1.470 + <object class="IBObjectRecord">
1.471 + <int key="objectID">417</int>
1.472 + <reference key="object" ref="545842217"/>
1.473 + <object class="NSMutableArray" key="children">
1.474 + <bool key="EncodedWithXMLCoder">YES</bool>
1.475 + <reference ref="120902258"/>
1.476 + </object>
1.477 + <reference key="parent" ref="658832775"/>
1.478 + </object>
1.479 + <object class="IBObjectRecord">
1.480 + <int key="objectID">418</int>
1.481 + <reference key="object" ref="874656297"/>
1.482 + <object class="NSMutableArray" key="children">
1.483 + <bool key="EncodedWithXMLCoder">YES</bool>
1.484 + <reference ref="338639883"/>
1.485 + </object>
1.486 + <reference key="parent" ref="658832775"/>
1.487 + </object>
1.488 + <object class="IBObjectRecord">
1.489 + <int key="objectID">419</int>
1.490 + <reference key="object" ref="338639883"/>
1.491 + <reference key="parent" ref="874656297"/>
1.492 + </object>
1.493 + <object class="IBObjectRecord">
1.494 + <int key="objectID">420</int>
1.495 + <reference key="object" ref="120902258"/>
1.496 + <reference key="parent" ref="545842217"/>
1.497 + </object>
1.498 + <object class="IBObjectRecord">
1.499 + <int key="objectID">421</int>
1.500 + <reference key="object" ref="313391357"/>
1.501 + <object class="NSMutableArray" key="children">
1.502 + <bool key="EncodedWithXMLCoder">YES</bool>
1.503 + <reference ref="868968858"/>
1.504 + </object>
1.505 + <reference key="parent" ref="658832775"/>
1.506 + </object>
1.507 + <object class="IBObjectRecord">
1.508 + <int key="objectID">422</int>
1.509 + <reference key="object" ref="868968858"/>
1.510 + <reference key="parent" ref="313391357"/>
1.511 + </object>
1.512 + <object class="IBObjectRecord">
1.513 + <int key="objectID">423</int>
1.514 + <reference key="object" ref="1027728059"/>
1.515 + <object class="NSMutableArray" key="children">
1.516 + <bool key="EncodedWithXMLCoder">YES</bool>
1.517 + <reference ref="466169840"/>
1.518 + </object>
1.519 + <reference key="parent" ref="658832775"/>
1.520 + </object>
1.521 + <object class="IBObjectRecord">
1.522 + <int key="objectID">424</int>
1.523 + <reference key="object" ref="466169840"/>
1.524 + <reference key="parent" ref="1027728059"/>
1.525 + </object>
1.526 </object>
1.527 </object>
1.528 <object class="NSMutableDictionary" key="flattenedProperties">
1.529 @@ -1642,7 +2041,6 @@
1.530 <string>371.IBWindowTemplateEditedContentRect</string>
1.531 <string>371.NSWindowTemplate.visibleAtLaunch</string>
1.532 <string>371.editorWindowContentRectSynchronizationRect</string>
1.533 - <string>371.lastResizeAction</string>
1.534 <string>372.IBPluginDependency</string>
1.535 <string>375.IBPluginDependency</string>
1.536 <string>376.IBPluginDependency</string>
1.537 @@ -1659,6 +2057,26 @@
1.538 <string>393.IBPluginDependency</string>
1.539 <string>401.IBPluginDependency</string>
1.540 <string>402.IBPluginDependency</string>
1.541 + <string>405.IBPluginDependency</string>
1.542 + <string>406.IBPluginDependency</string>
1.543 + <string>407.IBPluginDependency</string>
1.544 + <string>409.IBEditorWindowLastContentRect</string>
1.545 + <string>409.IBPluginDependency</string>
1.546 + <string>409.IBWindowTemplateEditedContentRect</string>
1.547 + <string>409.NSWindowTemplate.visibleAtLaunch</string>
1.548 + <string>410.IBPluginDependency</string>
1.549 + <string>411.IBPluginDependency</string>
1.550 + <string>412.IBPluginDependency</string>
1.551 + <string>415.IBPluginDependency</string>
1.552 + <string>416.IBPluginDependency</string>
1.553 + <string>417.IBPluginDependency</string>
1.554 + <string>418.IBPluginDependency</string>
1.555 + <string>419.IBPluginDependency</string>
1.556 + <string>420.IBPluginDependency</string>
1.557 + <string>421.IBPluginDependency</string>
1.558 + <string>422.IBPluginDependency</string>
1.559 + <string>423.IBPluginDependency</string>
1.560 + <string>424.IBPluginDependency</string>
1.561 <string>5.IBPluginDependency</string>
1.562 <string>5.ImportedFromIB2</string>
1.563 <string>56.IBPluginDependency</string>
1.564 @@ -1684,6 +2102,7 @@
1.565 <string>79.ImportedFromIB2</string>
1.566 <string>80.IBPluginDependency</string>
1.567 <string>80.ImportedFromIB2</string>
1.568 + <string>81.IBEditorWindowLastContentRect</string>
1.569 <string>81.IBPluginDependency</string>
1.570 <string>81.ImportedFromIB2</string>
1.571 <string>81.editorWindowContentRectSynchronizationRect</string>
1.572 @@ -1787,30 +2206,17 @@
1.573 <string>{{319, 763}, {234, 73}}</string>
1.574 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.575 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.576 - <string>{{85, 58}, {1084, 617}}</string>
1.577 + <string>{{249, 66}, {1084, 617}}</string>
1.578 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.579 - <string>{{85, 58}, {1084, 617}}</string>
1.580 + <string>{{249, 66}, {1084, 617}}</string>
1.581 <reference ref="9"/>
1.582 <string>{{117, 199}, {1084, 587}}</string>
1.583 - <object class="NSDictionary">
1.584 - <bool key="EncodedWithXMLCoder">YES</bool>
1.585 - <object class="NSMutableArray" key="dict.sortedKeys">
1.586 - <bool key="EncodedWithXMLCoder">YES</bool>
1.587 - <string>IBResizeActionFinalFrame</string>
1.588 - <string>IBResizeActionInitialFrame</string>
1.589 - </object>
1.590 - <object class="NSMutableArray" key="dict.values">
1.591 - <bool key="EncodedWithXMLCoder">YES</bool>
1.592 - <string>{{60, 145}, {1084, 617}}</string>
1.593 - <string>{{60, 175}, {1084, 587}}</string>
1.594 - </object>
1.595 - </object>
1.596 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.597 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.598 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.599 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.600 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.601 - <string>{{313, 712}, {205, 113}}</string>
1.602 + <string>{{313, 692}, {205, 133}}</string>
1.603 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.604 <string>{{319, 721}, {205, 113}}</string>
1.605 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.606 @@ -1822,6 +2228,26 @@
1.607 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.608 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.609 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.610 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.611 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.612 + <string>{{213, 731}, {773, 125}}</string>
1.613 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.614 + <string>{{213, 731}, {773, 125}}</string>
1.615 + <integer value="0"/>
1.616 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.617 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.618 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.619 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.620 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.621 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.622 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.623 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.624 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.625 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.626 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.627 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.628 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.629 + <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.630 <reference ref="9"/>
1.631 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.632 <reference ref="9"/>
1.633 @@ -1846,6 +2272,7 @@
1.634 <reference ref="9"/>
1.635 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.636 <reference ref="9"/>
1.637 + <string>{{227, 622}, {199, 203}}</string>
1.638 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
1.639 <reference ref="9"/>
1.640 <string>{{233, 633}, {199, 203}}</string>
1.641 @@ -1877,7 +2304,7 @@
1.642 </object>
1.643 </object>
1.644 <nil key="sourceID"/>
1.645 - <int key="maxID">404</int>
1.646 + <int key="maxID">433</int>
1.647 </object>
1.648 <object class="IBClassDescriber" key="IBDocument.Classes">
1.649 <object class="NSMutableArray" key="referencedPartialClassDescriptions">
1.650 @@ -1901,9 +2328,11 @@
1.651 <bool key="EncodedWithXMLCoder">YES</bool>
1.652 <object class="NSMutableArray" key="dict.sortedKeys">
1.653 <bool key="EncodedWithXMLCoder">YES</bool>
1.654 + <string>dismissSheet:</string>
1.655 <string>enterFullScreen:</string>
1.656 <string>redo:</string>
1.657 <string>startGameFromMenu:</string>
1.658 + <string>toggleRemoteOpponent:</string>
1.659 <string>undo:</string>
1.660 </object>
1.661 <object class="NSMutableArray" key="dict.values">
1.662 @@ -1912,11 +2341,26 @@
1.663 <string>id</string>
1.664 <string>id</string>
1.665 <string>id</string>
1.666 + <string>id</string>
1.667 + <string>id</string>
1.668 </object>
1.669 </object>
1.670 <object class="NSMutableDictionary" key="outlets">
1.671 - <string key="NS.key.0">_turnSlider</string>
1.672 - <string key="NS.object.0">NSSlider</string>
1.673 + <bool key="EncodedWithXMLCoder">YES</bool>
1.674 + <object class="NSMutableArray" key="dict.sortedKeys">
1.675 + <bool key="EncodedWithXMLCoder">YES</bool>
1.676 + <string>_myMoveURLField</string>
1.677 + <string>_opponentsMoveSheet</string>
1.678 + <string>_opponentsMoveURLField</string>
1.679 + <string>_turnSlider</string>
1.680 + </object>
1.681 + <object class="NSMutableArray" key="dict.values">
1.682 + <bool key="EncodedWithXMLCoder">YES</bool>
1.683 + <string>NSTextField</string>
1.684 + <string>NSPanel</string>
1.685 + <string>NSTextField</string>
1.686 + <string>NSSlider</string>
1.687 + </object>
1.688 </object>
1.689 <object class="IBClassDescriptionSource" key="sourceIdentifier">
1.690 <string key="majorKey">IBProjectSource</string>
2.1 Binary file English.lproj/MainMenu.nib/keyedobjects.nib has changed
3.1 --- a/GeekGameBoard.xcodeproj/project.pbxproj Thu May 29 15:04:06 2008 -0700
3.2 +++ b/GeekGameBoard.xcodeproj/project.pbxproj Thu Jul 03 17:44:30 2008 -0700
3.3 @@ -41,7 +41,6 @@
3.4 27CCAABD0CB92A9F001CFE24 /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAABC0CB92A9F001CFE24 /* Card.m */; };
3.5 27CCABBF0CB9496B001CFE24 /* Bit.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCABBE0CB9496B001CFE24 /* Bit.m */; };
3.6 27CCAC750CB95C2B001CFE24 /* PlayingCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */; };
3.7 - 27D014C00D8DFB4500615ADD /* Game-Persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */; };
3.8 27D4F1260CCF011200923605 /* Stack.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D4F1250CCF011200923605 /* Stack.m */; };
3.9 27DFC4410CCD01B7005E34CE /* GoGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27DFC4400CCD01B7005E34CE /* GoGame.m */; };
3.10 27F230B90CD1A61B006939C1 /* KlondikeGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F230B80CD1A61B006939C1 /* KlondikeGame.m */; };
3.11 @@ -110,8 +109,6 @@
3.12 27CCABBE0CB9496B001CFE24 /* Bit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Bit.m; sourceTree = "<group>"; };
3.13 27CCAC730CB95C2B001CFE24 /* PlayingCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayingCard.h; sourceTree = "<group>"; };
3.14 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayingCard.m; sourceTree = "<group>"; };
3.15 - 27D014BE0D8DFB4500615ADD /* Game-Persistence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Game-Persistence.h"; sourceTree = "<group>"; };
3.16 - 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Game-Persistence.m"; sourceTree = "<group>"; };
3.17 27D4F1240CCF011200923605 /* Stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Stack.h; sourceTree = "<group>"; };
3.18 27D4F1250CCF011200923605 /* Stack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Stack.m; sourceTree = "<group>"; };
3.19 27DFC43F0CCD01B7005E34CE /* GoGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GoGame.h; sourceTree = "<group>"; };
3.20 @@ -229,8 +226,6 @@
3.21 children = (
3.22 27275C490CC700F2009C4C6C /* Game.h */,
3.23 27275C4A0CC700F2009C4C6C /* Game.m */,
3.24 - 27D014BE0D8DFB4500615ADD /* Game-Persistence.h */,
3.25 - 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */,
3.26 27275C900CC7C578009C4C6C /* TicTacToeGame.h */,
3.27 27275C910CC7C578009C4C6C /* TicTacToeGame.m */,
3.28 2734B4EE0CCA5BDB0070C008 /* CheckersGame.h */,
3.29 @@ -402,7 +397,6 @@
3.30 27C999C30D81185E005AFD4F /* GGBUtils.m in Sources */,
3.31 279F4D870D8606C200B32DBF /* GGBLayer.m in Sources */,
3.32 279F4D880D8606C200B32DBF /* GGBTextLayer.m in Sources */,
3.33 - 27D014C00D8DFB4500615ADD /* Game-Persistence.m in Sources */,
3.34 );
3.35 runOnlyForDeploymentPostprocessing = 0;
3.36 };
4.1 Binary file Resources/Gingko/Blue.png has changed
5.1 Binary file Resources/Gingko/Gold.png has changed
6.1 Binary file Resources/Gingko/Green.png has changed
7.1 Binary file Resources/Gingko/Red.png has changed
8.1 Binary file Resources/Gingko/Violet.png has changed
9.1 --- a/Source/Bit.m Thu May 29 15:04:06 2008 -0700
9.2 +++ b/Source/Bit.m Thu Jul 03 17:44:30 2008 -0700
9.3 @@ -22,6 +22,7 @@
9.4 */
9.5 #import "Bit.h"
9.6 #import "Game.h"
9.7 +#import "Player.h"
9.8 #import "QuartzUtils.h"
9.9
9.10
10.1 --- a/Source/BitHolder.h Thu May 29 15:04:06 2008 -0700
10.2 +++ b/Source/BitHolder.h Thu Jul 03 17:44:30 2008 -0700
10.3 @@ -30,6 +30,9 @@
10.4 /** Current Bit, or nil if empty */
10.5 @property (retain) Bit* bit;
10.6
10.7 +/** Sets current Bit to nil, triggering its "destroy" animation */
10.8 +- (void) destroyBit;
10.9 +
10.10 /** Conveniences for comparing self.bit with nil */
10.11 @property (readonly, getter=isEmpty) BOOL empty;
10.12
11.1 --- a/Source/BitHolder.m Thu May 29 15:04:06 2008 -0700
11.2 +++ b/Source/BitHolder.m Thu Jul 03 17:44:30 2008 -0700
11.3 @@ -57,6 +57,14 @@
11.4 }
11.5 }
11.6
11.7 +- (void) destroyBit
11.8 +{
11.9 + if( _bit ) {
11.10 + [_bit destroy];
11.11 + setObj(&_bit,nil);
11.12 + }
11.13 +}
11.14 +
11.15 - (BOOL) isEmpty {return self.bit==nil;}
11.16
11.17 @synthesize highlighted=_highlighted;
12.1 --- a/Source/BoardUIView.m Thu May 29 15:04:06 2008 -0700
12.2 +++ b/Source/BoardUIView.m Thu Jul 03 17:44:30 2008 -0700
12.3 @@ -68,7 +68,7 @@
12.4 [rootLayer addSublayer: _gameboard];
12.5 [_gameboard release];
12.6
12.7 - _game = [[gameClass alloc] initWithBoard: _gameboard];
12.8 + _game = [[gameClass alloc] initNewGameWithBoard: _gameboard];
12.9 }
12.10
12.11
13.1 --- a/Source/BoardView.h Thu May 29 15:04:06 2008 -0700
13.2 +++ b/Source/BoardView.h Thu Jul 03 17:44:30 2008 -0700
13.3 @@ -31,6 +31,7 @@
13.4 @private
13.5 Game *_game; // Current Game
13.6 GGBLayer *_gameboard; // Game's main layer
13.7 + NSSize _oldSize;
13.8
13.9 // Used during mouse-down tracking:
13.10 NSPoint _dragStartPos; // Starting position of mouseDown
13.11 @@ -48,13 +49,17 @@
13.12 NSDragOperation _viewDropOp; // Current drag operation
13.13 }
13.14
13.15 +@property (retain) Game *game;
13.16 +@property (readonly) CALayer *gameboard;
13.17 +
13.18 - (void) startGameNamed: (NSString*)gameClassName;
13.19
13.20 +- (void) createGameBoard;
13.21 +
13.22 +- (void) reverseBoard;
13.23 +
13.24 - (IBAction) enterFullScreen: (id)sender;
13.25
13.26 -@property (readonly) Game *game;
13.27 -@property (readonly) CALayer *gameboard;
13.28 -
13.29 - (CGRect) gameBoardFrame;
13.30
13.31 @end
14.1 --- a/Source/BoardView.m Thu May 29 15:04:06 2008 -0700
14.2 +++ b/Source/BoardView.m Thu Jul 03 17:44:30 2008 -0700
14.3 @@ -24,6 +24,7 @@
14.4 #import "Bit.h"
14.5 #import "BitHolder.h"
14.6 #import "Game.h"
14.7 +#import "Player.h"
14.8 #import "QuartzUtils.h"
14.9 #import "GGBUtils.h"
14.10
14.11 @@ -36,7 +37,7 @@
14.12 @implementation BoardView
14.13
14.14
14.15 -@synthesize game=_game, gameboard=_gameboard;
14.16 +@synthesize gameboard=_gameboard;
14.17
14.18
14.19 - (void) dealloc
14.20 @@ -46,20 +47,63 @@
14.21 }
14.22
14.23
14.24 +- (void) _removeGameBoard
14.25 +{
14.26 + if( _gameboard ) {
14.27 + RemoveImmediately(_gameboard);
14.28 + _gameboard = nil;
14.29 + }
14.30 +}
14.31 +
14.32 +- (void) createGameBoard
14.33 +{
14.34 + [self _removeGameBoard];
14.35 + _gameboard = [[CALayer alloc] init];
14.36 + _gameboard.frame = [self gameBoardFrame];
14.37 + _gameboard.autoresizingMask = kCALayerMinXMargin | kCALayerMaxXMargin | kCALayerMinYMargin | kCALayerMaxYMargin;
14.38 +
14.39 + // Tell the game to set up the board:
14.40 + _game.board = _gameboard;
14.41 +
14.42 + [self.layer addSublayer: _gameboard];
14.43 + [_gameboard release];
14.44 +}
14.45 +
14.46 +
14.47 +- (void) reverseBoard
14.48 +{
14.49 + [_gameboard setValue: [NSNumber numberWithDouble: M_PI]
14.50 + forKeyPath: @"transform.rotation"];
14.51 +}
14.52 +
14.53 +
14.54 +- (Game*) game
14.55 +{
14.56 + return _game;
14.57 +}
14.58 +
14.59 +- (void) setGame: (Game*)game
14.60 +{
14.61 + if( game!=_game ) {
14.62 + setObj(&_game,game);
14.63 + [self createGameBoard];
14.64 + }
14.65 +}
14.66 +
14.67 - (void) startGameNamed: (NSString*)gameClassName
14.68 {
14.69 - if( _gameboard ) {
14.70 - [_gameboard removeFromSuperlayer];
14.71 - _gameboard = nil;
14.72 + Class gameClass = NSClassFromString(gameClassName);
14.73 + Game *game = [[gameClass alloc] init];
14.74 + if( game ) {
14.75 + self.game = game;
14.76 + [game release];
14.77 }
14.78 - _gameboard = [[CALayer alloc] init];
14.79 - _gameboard.frame = [self gameBoardFrame];
14.80 - _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
14.81 - [self.layer addSublayer: _gameboard];
14.82 - [_gameboard release];
14.83 -
14.84 - Class gameClass = NSClassFromString(gameClassName);
14.85 - setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]);
14.86 +}
14.87 +
14.88 +
14.89 +- (BOOL) canMakeMove
14.90 +{
14.91 + return (_game && _game.currentPlayer.local && _game.currentTurnNo==_game.maxTurnNo);
14.92 }
14.93
14.94
14.95 @@ -72,18 +116,46 @@
14.96 - (void)resetCursorRects
14.97 {
14.98 [super resetCursorRects];
14.99 - [self addCursorRect: self.bounds cursor: [NSCursor openHandCursor]];
14.100 + if( self.canMakeMove )
14.101 + [self addCursorRect: self.bounds cursor: [NSCursor openHandCursor]];
14.102 }
14.103
14.104
14.105 - (IBAction) enterFullScreen: (id)sender
14.106 {
14.107 + [self _removeGameBoard];
14.108 if( self.isInFullScreenMode ) {
14.109 [self exitFullScreenModeWithOptions: nil];
14.110 } else {
14.111 [self enterFullScreenMode: self.window.screen
14.112 withOptions: nil];
14.113 }
14.114 + [self createGameBoard];
14.115 +}
14.116 +
14.117 +
14.118 +- (void)viewWillStartLiveResize
14.119 +{
14.120 + [super viewWillStartLiveResize];
14.121 + _oldSize = self.frame.size;
14.122 +}
14.123 +
14.124 +- (void)setFrameSize:(NSSize)newSize
14.125 +{
14.126 + [super setFrameSize: newSize];
14.127 + if( _oldSize.width > 0.0f ) {
14.128 + CGAffineTransform xform = _gameboard.affineTransform;
14.129 + xform.a = xform.d = MIN(newSize.width,newSize.height)/MIN(_oldSize.width,_oldSize.height);
14.130 + _gameboard.affineTransform = xform;
14.131 + } else
14.132 + [self createGameBoard];
14.133 +}
14.134 +
14.135 +- (void)viewDidEndLiveResize
14.136 +{
14.137 + [super viewDidEndLiveResize];
14.138 + _oldSize.width = _oldSize.height = 0.0f;
14.139 + [self createGameBoard];
14.140 }
14.141
14.142
14.143 @@ -149,6 +221,11 @@
14.144
14.145 - (void) mouseDown: (NSEvent*)ev
14.146 {
14.147 + if( ! self.canMakeMove ) {
14.148 + NSBeep();
14.149 + return;
14.150 + }
14.151 +
14.152 BOOL placing = NO;
14.153 _dragStartPos = ev.locationInWindow;
14.154 _dragBit = (Bit*) [self hitTestPoint: _dragStartPos
15.1 --- a/Source/CheckersGame.h Thu May 29 15:04:06 2008 -0700
15.2 +++ b/Source/CheckersGame.h Thu Jul 03 17:44:30 2008 -0700
15.3 @@ -20,7 +20,7 @@
15.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
15.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15.6 */
15.7 -#import "Game.h"
15.8 +#import "Game+Protected.h"
15.9 @class Grid;
15.10
15.11
16.1 --- a/Source/CheckersGame.m Thu May 29 15:04:06 2008 -0700
16.2 +++ b/Source/CheckersGame.m Thu Jul 03 17:44:30 2008 -0700
16.3 @@ -30,6 +30,26 @@
16.4 @implementation CheckersGame
16.5
16.6
16.7 +- (id) init
16.8 +{
16.9 + self = [super init];
16.10 + if (self != nil) {
16.11 + _cells = [[NSMutableArray alloc] init];
16.12 + [self setNumberOfPlayers: 2];
16.13 +
16.14 + PreloadSound(@"Tink");
16.15 + PreloadSound(@"Funk");
16.16 + PreloadSound(@"Blow");
16.17 + PreloadSound(@"Pop");
16.18 + }
16.19 + return self;
16.20 +}
16.21 +
16.22 +- (CGImageRef) iconForPlayer: (int)playerNum
16.23 +{
16.24 + return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
16.25 +}
16.26 +
16.27 - (Piece*) pieceForPlayer: (int)playerNum
16.28 {
16.29 Piece *p = [[Piece alloc] initWithImageNamed: (playerNum==0 ?@"Green.png" :@"Red.png")
16.30 @@ -39,10 +59,18 @@
16.31 return [p autorelease];
16.32 }
16.33
16.34 -- (Grid*) x_makeGrid
16.35 +- (void) makeKing: (Piece*)piece
16.36 +{
16.37 + piece.scale = 1.4;
16.38 + [piece setValue: @"King" forKey: @"King"];
16.39 + piece.name = piece.owner.index ?@"4" :@"3";
16.40 +}
16.41 +
16.42 +- (void) setUpBoard
16.43 {
16.44 RectGrid *grid = [[[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds] autorelease];
16.45 _grid = grid;
16.46 + [_board addSublayer: _grid];
16.47 CGPoint pos = _grid.position;
16.48 pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
16.49 grid.position = pos;
16.50 @@ -53,31 +81,11 @@
16.51 grid.lineColor = nil;
16.52
16.53 [grid addAllCells];
16.54 + [_cells removeAllObjects];
16.55 for( int i=0; i<32; i++ ) {
16.56 int row = i/4;
16.57 [_cells addObject: [_grid cellAtRow: row column: 2*(i%4) + (row&1)]];
16.58 }
16.59 - self.stateString = @"111111111111--------222222222222";
16.60 - return grid;
16.61 -}
16.62 -
16.63 -
16.64 -- (id) initWithBoard: (GGBLayer*)board
16.65 -{
16.66 - self = [super initWithBoard: board];
16.67 - if (self != nil) {
16.68 - [self setNumberOfPlayers: 2];
16.69 - _cells = [[NSMutableArray alloc] init];
16.70 - [self x_makeGrid];
16.71 - [board addSublayer: _grid];
16.72 - [self nextPlayer];
16.73 -
16.74 - PreloadSound(@"Tink");
16.75 - PreloadSound(@"Funk");
16.76 - PreloadSound(@"Blow");
16.77 - PreloadSound(@"Pop");
16.78 - }
16.79 - return self;
16.80 }
16.81
16.82 - (void) dealloc
16.83 @@ -88,6 +96,11 @@
16.84 }
16.85
16.86
16.87 +- (NSString*) initialStateString
16.88 +{
16.89 + return @"111111111111--------222222222222";
16.90 +}
16.91 +
16.92 - (NSString*) stateString
16.93 {
16.94 unichar state[_cells.count];
16.95 @@ -108,11 +121,15 @@
16.96 int i = 0;
16.97 for( GridCell *cell in _cells ) {
16.98 Piece *piece;
16.99 - switch( [state characterAtIndex: i++] ) {
16.100 - case '1': piece = [self pieceForPlayer: 0]; _numPieces[0]++; break;
16.101 - case '2': piece = [self pieceForPlayer: 1]; _numPieces[1]++; break;
16.102 - default: piece = nil; break;
16.103 - }
16.104 + int which = [state characterAtIndex: i++] - '1';
16.105 + if( which >=0 && which < 4 ) {
16.106 + int player = (which & 1);
16.107 + piece = [self pieceForPlayer: player];
16.108 + _numPieces[player]++;
16.109 + if( which & 2 )
16.110 + [self makeKing: piece];
16.111 + } else
16.112 + piece = nil;
16.113 cell.bit = piece;
16.114 }
16.115 }
16.116 @@ -134,9 +151,11 @@
16.117 Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
16.118 int playerIndex = self.currentPlayer.index;
16.119
16.120 - if( self.currentMove.length==0 )
16.121 - [self.currentMove appendString: src.name];
16.122 - [self.currentMove appendString: dst.name];
16.123 + Turn *turn = self.currentTurn;
16.124 + if( turn.move.length==0 )
16.125 + [turn addToMove: src.name];
16.126 + [turn addToMove: @"-"];
16.127 + [turn addToMove: dst.name];
16.128
16.129 BOOL isKing = ([bit valueForKey: @"King"] != nil);
16.130 PlaySound(isKing ?@"Funk" :@"Tink");
16.131 @@ -145,8 +164,8 @@
16.132 if( dst.row == (playerIndex ?0 :7) )
16.133 if( ! isKing ) {
16.134 PlaySound(@"Blow");
16.135 - bit.scale = 1.4;
16.136 - [bit setValue: @"King" forKey: @"King"];
16.137 + [self makeKing: (Piece*)bit];
16.138 + [turn addToMove: @"*"];
16.139 // don't set isKing flag - piece can't jump again after being kinged.
16.140 }
16.141
16.142 @@ -163,9 +182,9 @@
16.143
16.144 if( capture ) {
16.145 PlaySound(@"Pop");
16.146 - Bit *bit = capture.bit;
16.147 - _numPieces[bit.owner.index]--;
16.148 - [bit destroy];
16.149 + _numPieces[capture.bit.owner.index]--;
16.150 + [capture destroyBit];
16.151 + [turn addToMove: @"!"];
16.152
16.153 // Now check if another capture is possible. If so, don't end the turn:
16.154 if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
16.155 @@ -192,16 +211,15 @@
16.156
16.157 - (BOOL) applyMoveString: (NSString*)move
16.158 {
16.159 - int length = move.length;
16.160 - if( length<4 || (length&1) )
16.161 - return NO;
16.162 GridCell *src = nil;
16.163 - for( int i=0; i<length; i+=2 ) {
16.164 - NSString *ident = [move substringWithRange: NSMakeRange(i,2)];
16.165 + for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
16.166 + while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
16.167 + ident = [ident substringToIndex: ident.length-1];
16.168 GridCell *dst = [_grid cellWithName: ident];
16.169 - if( i > 0 )
16.170 - if( ! [self animateMoveFrom: src to: dst] )
16.171 - return NO;
16.172 + if( dst == nil )
16.173 + return NO;
16.174 + if( src && ! [self animateMoveFrom: src to: dst] )
16.175 + return NO;
16.176 src = dst;
16.177 }
16.178 return YES;
17.1 --- a/Source/DemoBoardView.h Thu May 29 15:04:06 2008 -0700
17.2 +++ b/Source/DemoBoardView.h Thu Jul 03 17:44:30 2008 -0700
17.3 @@ -29,11 +29,12 @@
17.4 {
17.5 CATextLayer *_headline;
17.6 IBOutlet NSSlider *_turnSlider;
17.7 + IBOutlet NSPanel *_opponentsMoveSheet;
17.8 + IBOutlet NSTextField *_myMoveURLField, *_opponentsMoveURLField;
17.9 }
17.10
17.11 - (IBAction) undo: (id)sender;
17.12 - (IBAction) redo: (id)sender;
17.13 - (IBAction) startGameFromMenu: (id)sender;
17.14 -- (IBAction) enterFullScreen: (id)sender;
17.15
17.16 @end
18.1 --- a/Source/DemoBoardView.m Thu May 29 15:04:06 2008 -0700
18.2 +++ b/Source/DemoBoardView.m Thu Jul 03 17:44:30 2008 -0700
18.3 @@ -26,15 +26,6 @@
18.4 #import "QuartzUtils.h"
18.5
18.6
18.7 -/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION!
18.8 - ** This sample application uses Objective-C 2.0 garbage collection.
18.9 - ** Therefore, the source code in this file does NOT perform manual object memory management.
18.10 - ** If you reuse any of this code in a process that isn't garbage collected, you will need to
18.11 - ** add all necessary retain/release/autorelease calls, and implement -dealloc methods,
18.12 - ** otherwise unpleasant leakage will occur!
18.13 - **/
18.14 -
18.15 -
18.16 @implementation DemoBoardView
18.17
18.18
18.19 @@ -46,6 +37,14 @@
18.20 static NSString* sCurrentGameName = @"CheckersGame";
18.21
18.22
18.23 +- (IBAction) toggleRemoteOpponent: (id)sender
18.24 +{
18.25 + NSAssert(self.game.currentTurn==0,@"Game has already begun");
18.26 + Player *opponent = [self.game.players objectAtIndex: 1];
18.27 + opponent.local = !opponent.local;
18.28 +}
18.29 +
18.30 +
18.31 - (void) startGameNamed: (NSString*)gameClassName
18.32 {
18.33 [super startGameNamed: gameClassName];
18.34 @@ -112,19 +111,26 @@
18.35 {
18.36 Game *game = self.game;
18.37 if( object == game ) {
18.38 - NSLog(@"maxTurn = %u, currentTurn=%u", self.game.maxTurn,self.game.currentTurn);
18.39 + NSLog(@"maxTurn = %u, currentTurn = %u",
18.40 + self.game.maxTurn,self.game.currentTurn);
18.41 + NSLog(@"Game state = '%@'", self.game.stateString);
18.42 +
18.43 _turnSlider.maxValue = self.game.maxTurn;
18.44 _turnSlider.numberOfTickMarks = self.game.maxTurn+1;
18.45
18.46 Player *p = game.winner;
18.47 NSString *msg;
18.48 if( p ) {
18.49 + // The game is won
18.50 [[NSSound soundNamed: @"Sosumi"] play];
18.51 - msg = @"%@ wins! Congratulations!";
18.52 + if( self.game.local )
18.53 + msg = @"%@ wins! Congratulations!";
18.54 + else
18.55 + msg = p.local ?@"You Win! Congratulations!" :@"You Lose ... :-(";
18.56 } else {
18.57 + // Otherwise go on to the next turn:
18.58 p = game.currentPlayer;
18.59 msg = @"Your turn, %@";
18.60 - NSLog(@"Game state = '%@'", self.game.stateString);
18.61 }
18.62 _headline.string = [NSString stringWithFormat: msg, p.name];
18.63 }
18.64 @@ -149,13 +155,6 @@
18.65 }
18.66
18.67
18.68 -- (IBAction) enterFullScreen: (id)sender
18.69 -{
18.70 - [super enterFullScreen: sender];
18.71 - [self startGameNamed: sCurrentGameName]; // restart game so it'll use the new size
18.72 -}
18.73 -
18.74 -
18.75 #pragma mark -
18.76 #pragma mark NSAPPLICATION DELEGATE:
18.77
19.1 --- a/Source/GGBLayer.h Thu May 29 15:04:06 2008 -0700
19.2 +++ b/Source/GGBLayer.h Thu Jul 03 17:44:30 2008 -0700
19.3 @@ -47,3 +47,6 @@
19.4 /** Disables animations until EndDisableAnimations is called. */
19.5 void BeginDisableAnimations(void);
19.6 void EndDisableAnimations(void);
19.7 +
19.8 +CGColorRef GetEffectiveBackground( CALayer *layer );
19.9 +
20.1 --- a/Source/GGBLayer.m Thu May 29 15:04:06 2008 -0700
20.2 +++ b/Source/GGBLayer.m Thu Jul 03 17:44:30 2008 -0700
20.3 @@ -288,3 +288,12 @@
20.4 }
20.5
20.6
20.7 +CGColorRef GetEffectiveBackground( CALayer *layer )
20.8 +{
20.9 + for( ; layer; layer=layer.superlayer ) {
20.10 + CGColorRef bg = layer.backgroundColor;
20.11 + if( bg )
20.12 + return bg;
20.13 + }
20.14 + return nil;
20.15 +}
21.1 --- a/Source/GGBTextLayer.m Thu May 29 15:04:06 2008 -0700
21.2 +++ b/Source/GGBTextLayer.m Thu Jul 03 17:44:30 2008 -0700
21.3 @@ -51,13 +51,17 @@
21.4 label.foregroundColor = kBlackColor;
21.5
21.6 NSString *mode;
21.7 - if( align & kCALayerWidthSizable )
21.8 + if( (align & (kCALayerMinXMargin | kCALayerMaxXMargin)) == (kCALayerMinXMargin | kCALayerMaxXMargin) )
21.9 mode = @"center";
21.10 - else if( align & kCALayerMinXMargin )
21.11 - mode = @"right";
21.12 - else
21.13 - mode = @"left";
21.14 - align |= kCALayerWidthSizable;
21.15 + else {
21.16 + if( align & kCALayerWidthSizable )
21.17 + mode = @"center";
21.18 + else if( align & kCALayerMinXMargin )
21.19 + mode = @"right";
21.20 + else
21.21 + mode = @"left";
21.22 + align |= kCALayerWidthSizable;
21.23 + }
21.24 label.alignmentMode = mode;
21.25
21.26 // Get the bounds of the interior of the superlayer:
22.1 --- a/Source/GGBUtils.h Thu May 29 15:04:06 2008 -0700
22.2 +++ b/Source/GGBUtils.h Thu Jul 03 17:44:30 2008 -0700
22.3 @@ -29,6 +29,7 @@
22.4 /** Just like setObj except that it _copies_ the new value. */
22.5 void setObjCopy( id<NSCopying> *variable, id<NSCopying> newValue );
22.6
22.7 +void DelayFor( NSTimeInterval interval );
22.8
22.9 void PreloadSound( NSString* name );
22.10 void PlaySound( NSString* name );
23.1 --- a/Source/GGBUtils.m Thu May 29 15:04:06 2008 -0700
23.2 +++ b/Source/GGBUtils.m Thu Jul 03 17:44:30 2008 -0700
23.3 @@ -42,6 +42,16 @@
23.4 }
23.5
23.6
23.7 +void DelayFor( NSTimeInterval interval )
23.8 +{
23.9 + NSDate *end = [NSDate dateWithTimeIntervalSinceNow: interval];
23.10 + while( [end timeIntervalSinceNow] > 0 ) {
23.11 + if( ! [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: end] )
23.12 + break;
23.13 + }
23.14 +}
23.15 +
23.16 +
23.17 #if TARGET_OS_IPHONE
23.18 static SystemSoundID GetSound( NSString *name )
23.19 {
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/Source/Game+Protected.h Thu Jul 03 17:44:30 2008 -0700
24.3 @@ -0,0 +1,65 @@
24.4 +//
24.5 +// Game+Protected.h
24.6 +// YourMove
24.7 +//
24.8 +// Created by Jens Alfke on 7/3/08.
24.9 +// Copyright 2008 Jens Alfke. All rights reserved.
24.10 +//
24.11 +
24.12 +
24.13 +#import "Game.h"
24.14 +#import "Player.h"
24.15 +#import "Turn.h"
24.16 +#import "Bit.h"
24.17 +#import "BitHolder.h"
24.18 +
24.19 +
24.20 +/** Game API for subclasses to use / override */
24.21 +@interface Game (Protected)
24.22 +
24.23 +/** Should return a string describing the initial state of a new game.
24.24 + The default value is an empty string. */
24.25 +- (NSString*) initialStateString;
24.26 +
24.27 +
24.28 +#pragma mark Abstract methods for subclasses to implement:
24.29 +
24.30 +/** Called by -setBoard: Should all all necessary Grids/Pieces/Cards/etc. to _board.
24.31 + This method is always called during initialization of a new Game, and may be called
24.32 + again afterwards, for example if the board area is resized. */
24.33 +- (void) setUpBoard;
24.34 +
24.35 +/** Should return the winning player, if the current position is a win, else nil.
24.36 + Default implementation returns nil. */
24.37 +- (Player*) checkForWinner;
24.38 +
24.39 +
24.40 +#pragma mark Protected methods for subclasses to call:
24.41 +
24.42 +/** Sets the number of players in the game. Subclass initializers should call this. */
24.43 +- (void) setNumberOfPlayers: (unsigned)n;
24.44 +
24.45 +/** Animate a piece moving from src to dst. Used in implementing -applyMoveString:. */
24.46 +- (BOOL) animateMoveFrom: (CALayer<BitHolder>*)src to: (CALayer<BitHolder>*)dst;
24.47 +
24.48 +/** Animate a piece being placed in dst. Used in implementing -applyMoveString:. */
24.49 +- (BOOL) animatePlacementIn: (CALayer<BitHolder>*)dst;
24.50 +
24.51 +/** Checks for a winner and advances to the next player. */
24.52 +- (void) endTurn;
24.53 +
24.54 +@end
24.55 +
24.56 +
24.57 +/** Optional Game API for tracking the history of a game, and being able to replay moves. */
24.58 +@interface Game (State)
24.59 +
24.60 +/** A string describing the current state of the game (the positions of all pieces,
24.61 + orderings of cards, player scores, ... */
24.62 +@property (copy) NSString* stateString;
24.63 +
24.64 +/** Add a move to the game based on the contents of the string.
24.65 + The string must have been returned by -currentMove at some point. */
24.66 +- (BOOL) applyMoveString: (NSString*)move;
24.67 +
24.68 +@end
25.1 --- a/Source/Game-Persistence.h Thu May 29 15:04:06 2008 -0700
25.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
25.3 @@ -1,20 +0,0 @@
25.4 -//
25.5 -// Game-Persistence.h
25.6 -// GeekGameBoard
25.7 -//
25.8 -// Created by Jens Alfke on 3/16/08.
25.9 -// Copyright 2008 __MyCompanyName__. All rights reserved.
25.10 -//
25.11 -
25.12 -#import "Game.h"
25.13 -
25.14 -
25.15 -@interface Game (Persistence) <NSCoding>
25.16 -
25.17 -+ (Game*) gameWithURL: (NSURL*)url;
25.18 -
25.19 -- (NSURL*) asURL;
25.20 -
25.21 -- (BOOL) addMoveFromURL: (NSURL*)url;
25.22 -
25.23 -@end
26.1 --- a/Source/Game-Persistence.m Thu May 29 15:04:06 2008 -0700
26.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
26.3 @@ -1,115 +0,0 @@
26.4 -//
26.5 -// Game-Persistence.m
26.6 -// GeekGameBoard
26.7 -//
26.8 -// Created by Jens Alfke on 3/16/08.
26.9 -// Copyright 2008 __MyCompanyName__. All rights reserved.
26.10 -//
26.11 -
26.12 -#import "Game-Persistence.h"
26.13 -
26.14 -
26.15 -static NSDictionary* parseURLFields( NSURL* url );
26.16 -
26.17 -
26.18 -@implementation Game (Persistence)
26.19 -
26.20 -
26.21 -static NSMutableDictionary *sPersistentGames;
26.22 -
26.23 -
26.24 -- (id) initWithCoder: (NSCoder*)decoder
26.25 -{
26.26 - self = [self init];
26.27 - if( self ) {
26.28 - _players = [[decoder decodeObjectForKey: @"players"] mutableCopy];
26.29 - _states = [[decoder decodeObjectForKey: @"states"] mutableCopy];
26.30 - _moves = [[decoder decodeObjectForKey: @"moves"] mutableCopy];
26.31 - self.currentTurn = self.maxTurn;
26.32 - }
26.33 - return self;
26.34 -}
26.35 -
26.36 -
26.37 -- (void) encodeWithCoder: (NSCoder*)coder
26.38 -{
26.39 - [coder encodeObject: _players forKey: @"players"];
26.40 - [coder encodeObject: _states forKey: @"states"];
26.41 - [coder encodeObject: _moves forKey: @"moves"];
26.42 -}
26.43 -
26.44 -
26.45 -- (NSURL*) asURL
26.46 -{
26.47 - return [NSURL URLWithString:
26.48 - [NSString stringWithFormat: @"game:type=%@&id=%@&turn=%u&move=%@",
26.49 - [[self class] identifier], _uniqueID, self.currentTurn,_moves.lastObject]];
26.50 -}
26.51 -
26.52 -
26.53 -+ (Game*) gameWithURL: (NSURL*)url
26.54 -{
26.55 - if( 0 != [@"game" caseInsensitiveCompare: url.scheme] )
26.56 - return nil;
26.57 - NSDictionary *fields = parseURLFields(url);
26.58 - NSString *type = [fields objectForKey: @"type"];
26.59 - NSString *uuid = [fields objectForKey: @"id"];
26.60 - int turn = [[fields objectForKey: @"turn"] intValue];
26.61 - if( !type || !uuid || turn<=0 )
26.62 - return nil;
26.63 -
26.64 - Game *game = [sPersistentGames objectForKey: uuid];
26.65 - if( game ) {
26.66 - if( ![type isEqualToString: [[game class] identifier]] )
26.67 - return nil;
26.68 - } else if( turn == 1 ) {
26.69 - Class gameClass = NSClassFromString( [type stringByAppendingString: @"Game"] );
26.70 - if( ! gameClass || ! [gameClass isSubclassOfClass: [Game class]] )
26.71 - return nil;
26.72 - game = [[gameClass alloc] initWithUniqueID: uuid];
26.73 - [game setNumberOfPlayers: 2];
26.74 - if( ! sPersistentGames )
26.75 - sPersistentGames = [[NSMutableDictionary alloc] init];
26.76 - [sPersistentGames setObject: game forKey: uuid];
26.77 - [game release];
26.78 - }
26.79 - return game;
26.80 -}
26.81 -
26.82 -
26.83 -- (BOOL) addMoveFromURL: (NSURL*)url
26.84 -{
26.85 - NSDictionary *fields = parseURLFields(url);
26.86 - NSString *uuid = [fields objectForKey: @"id"];
26.87 - NSString *move = [fields objectForKey: @"move"];
26.88 - int turn = [[fields objectForKey: @"turn"] intValue];
26.89 - return [uuid isEqualToString: self.uniqueID]
26.90 - && turn==self.currentTurn
26.91 - && move.length > 0
26.92 - && [self applyMoveString: move];
26.93 -}
26.94 -
26.95 -
26.96 -
26.97 -@end
26.98 -
26.99 -
26.100 -
26.101 -static NSDictionary* parseURLFields( NSURL* url )
26.102 -{
26.103 - // Parse the URL into key-value pairs:
26.104 - NSMutableDictionary *fields = [NSMutableDictionary dictionary];
26.105 - for( NSString *field in [url.resourceSpecifier componentsSeparatedByString: @"&"] ) {
26.106 - NSRange e = [field rangeOfString: @"="];
26.107 - NSString *key, *value;
26.108 - if( e.length>0 ) {
26.109 - key = [field substringToIndex: e.location];
26.110 - value = [field substringFromIndex: NSMaxRange(e)];
26.111 - } else {
26.112 - key= field;
26.113 - value = @"";
26.114 - }
26.115 - [fields setObject: value forKey: key];
26.116 - }
26.117 - return fields;
26.118 -}
27.1 --- a/Source/Game.h Thu May 29 15:04:06 2008 -0700
27.2 +++ b/Source/Game.h Thu Jul 03 17:44:30 2008 -0700
27.3 @@ -20,56 +20,72 @@
27.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27.6 */
27.7 -@class GGBLayer, Bit, BitHolder, Player;
27.8 +
27.9 +#import <Foundation/Foundation.h>
27.10 +@class GGBLayer, Bit, BitHolder, Player, Turn;
27.11 @protocol BitHolder;
27.12
27.13
27.14 /** Abstract superclass. Keeps track of the rules and turns of a game. */
27.15 -@interface Game : NSObject
27.16 +@interface Game : NSObject <NSCoding>
27.17 {
27.18 - NSString *_uniqueID;
27.19 GGBLayer *_board;
27.20 NSArray *_players;
27.21 - Player *_currentPlayer, *_winner;
27.22 - NSMutableString *_currentMove;
27.23 - NSMutableArray *_states, *_moves;
27.24 - unsigned _currentTurn;
27.25 + Player *_winner;
27.26 + NSMutableArray *_turns;
27.27 + unsigned _currentTurnNo;
27.28 + NSMutableDictionary *_extraValues;
27.29 + BOOL _requireConfirmation;
27.30 }
27.31
27.32 -/** Returns the name used to identify this game in URLs.
27.33 - (By default it just returns the class name with the "Game" suffix removed.) */
27.34 +#pragma mark Class properties:
27.35 +
27.36 +/** The name used to identify this class of game in URLs.
27.37 + (By default it just returns the class name with the "Game" suffix removed.) */
27.38 + (NSString*) identifier;
27.39
27.40 -/** Returns the human-readable name of this game.
27.41 +/** The human-readable name of this class of game.
27.42 (By default it just returns the class name with the "Game" suffix removed.) */
27.43 + (NSString*) displayName;
27.44
27.45 +/** Is this game's board wider than it's high? */
27.46 + (BOOL) landscapeOriented;
27.47
27.48 +
27.49 +/** Designated initializer: override this if your subclass needs additional initialization. */
27.50 +- (id) init;
27.51 +
27.52 +/** Convenience initializer that calls -init, -setBoard:, and -nextTurn. */
27.53 +- (id) initNewGameWithBoard: (GGBLayer*)board;
27.54 +
27.55 +/** NSCoding initializer. Calls -init, but then restores saved payers, states, moves. */
27.56 +- (id) initWithCoder: (NSCoder*)decoder;
27.57 +
27.58 +/** NSCoding method to save Game to an archive. */
27.59 +- (void) encodeWithCoder: (NSCoder*)coder;
27.60 +
27.61 +
27.62 @property (readonly, copy) NSArray *players;
27.63 -@property (readonly) Player *currentPlayer, *winner;
27.64 +@property (readonly) Player *currentPlayer, *winner, *remotePlayer;
27.65 +@property (readonly, getter=isLocal) BOOL local; // Are all players local?
27.66
27.67 -@property (readonly) NSArray *states, *moves;
27.68 -@property (readonly) unsigned maxTurn;
27.69 -@property unsigned currentTurn;
27.70 +@property (retain) GGBLayer *board; // The root layer for the game.
27.71 +
27.72 +@property (readonly) NSArray *turns;
27.73 +@property (readonly) Turn *currentTurn, *latestTurn;
27.74 +@property (readonly) unsigned maxTurnNo;
27.75 +@property unsigned currentTurnNo;
27.76 @property (readonly) BOOL isLatestTurn;
27.77
27.78 +@property BOOL requireConfirmation;
27.79 +- (void) cancelCurrentTurn;
27.80 +- (void) confirmCurrentTurn;
27.81
27.82 -/** A globally-unique string assigned to this game instance, to help networked players identify it. */
27.83 -@property (readonly) NSString* uniqueID;
27.84
27.85 -- (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst;
27.86 +#pragma mark Methods for subclasses to implement:
27.87
27.88 -
27.89 -- (id) initWithUniqueID: (NSString*)uniqueID;
27.90 -- (id) init;
27.91 -
27.92 -
27.93 -// Methods for subclasses to implement:
27.94 -
27.95 -/** Designated initializer. After calling the superclass implementation,
27.96 - it should add the necessary Grids, Pieces, Cards, Decks etc. to the board. */
27.97 -- (id) initWithBoard: (GGBLayer*)board;
27.98 +/** An icon for a player (usually the same as the image of the player's pieces.) */
27.99 +- (CGImageRef) iconForPlayer: (int)playerIndex;
27.100
27.101
27.102 /** Should return YES if it is legal for the given bit to be moved from its current holder.
27.103 @@ -96,60 +112,4 @@
27.104 Default implementation always returns YES. */
27.105 - (BOOL) clickedBit: (Bit*)bit;
27.106
27.107 -/** Should return the winning player, if the current position is a win, else nil.
27.108 - Default implementation returns nil. */
27.109 -- (Player*) checkForWinner;
27.110 -
27.111 -/** A string describing the current state of the game (the positions of all pieces,
27.112 - orderings of cards, player scores, ... */
27.113 -@property (copy) NSString* stateString;
27.114 -
27.115 -/** Add a move to the game based on the contents of the string. */
27.116 -- (BOOL) applyMoveString: (NSString*)move;
27.117 -
27.118 -
27.119 -// Protected methods for subclasses to call:
27.120 -
27.121 -/** Sets the number of players in the game. Subclass initializers should call this. */
27.122 -- (void) setNumberOfPlayers: (unsigned)n;
27.123 -
27.124 -/** The current move in progress. Append text to it as the user makes moves. */
27.125 -@property (readonly) NSMutableString* currentMove;
27.126 -
27.127 -/** Advance to the next player, when a turn is over. */
27.128 -- (void) nextPlayer;
27.129 -
27.130 -/** Checks for a winner and advances to the next player. */
27.131 -- (void) endTurn;
27.132 -
27.133 @end
27.134 -
27.135 -
27.136 -
27.137 -/** A mostly-passive object used to represent a player. */
27.138 -@interface Player : NSObject <NSCoding>
27.139 -{
27.140 - Game *_game;
27.141 - NSString *_name;
27.142 -}
27.143 -
27.144 -- (id) initWithGame: (Game*)game;
27.145 -
27.146 -@property (readonly) Game *game;
27.147 -@property (copy) NSString *name;
27.148 -@property (readonly) int index;
27.149 -@property (readonly, getter=isCurrent) BOOL current;
27.150 -@property (readonly, getter=isFriendly) BOOL friendly;
27.151 -@property (readonly, getter=isUnfriendly) BOOL unfriendly;
27.152 -@property (readonly) Player *nextPlayer, *previousPlayer;
27.153 -
27.154 -@end
27.155 -
27.156 -
27.157 -
27.158 -@interface CALayer (Game)
27.159 -
27.160 -/** Called on any CALayer in the game's layer tree, will return the current Game object. */
27.161 -@property (readonly) Game *game;
27.162 -
27.163 -@end
28.1 --- a/Source/Game.m Thu May 29 15:04:06 2008 -0700
28.2 +++ b/Source/Game.m Thu Jul 03 17:44:30 2008 -0700
28.3 @@ -20,71 +20,60 @@
28.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28.6 */
28.7 -#import "Game.h"
28.8 -#import "Bit.h"
28.9 -#import "BitHolder.h"
28.10 +#import "Game+Protected.h"
28.11 #import "QuartzUtils.h"
28.12 +#import "GGBUtils.h"
28.13
28.14
28.15 @interface Game ()
28.16 @property (copy) NSArray *players;
28.17 -@property (assign) Player *currentPlayer, *winner;
28.18 +@property (assign) Player *winner;
28.19 +- (void) _startTurn;
28.20 @end
28.21
28.22
28.23 @implementation Game
28.24
28.25
28.26 -+ (NSString*) identifier
28.27 +- (id) init
28.28 {
28.29 - NSString* name = [self description];
28.30 - if( [name hasSuffix: @"Game"] )
28.31 - name = [name substringToIndex: name.length-4];
28.32 - return name;
28.33 -}
28.34 -
28.35 -
28.36 -+ (NSString*) displayName
28.37 -{
28.38 - return [self identifier];
28.39 -}
28.40 -
28.41 -
28.42 -- (id) initWithUniqueID: (NSString*)uuid
28.43 -{
28.44 - NSParameterAssert(uuid);
28.45 self = [super init];
28.46 if (self != nil) {
28.47 - _uniqueID = [uuid copy];
28.48 - _board = [[GGBLayer alloc] init];
28.49 - // Store a pointer to myself as the value of the "Game" property
28.50 - // of my root layer. (CALayers can have arbitrary KV properties stored into them.)
28.51 - // This is used by the -[CALayer game] category method defined below, to find the Game.
28.52 - [_board setValue: self forKey: @"Game"];
28.53 -
28.54 - _currentMove = [[NSMutableString alloc] init];
28.55 + // Don't create _turns till -initWithCoder or -setNumberOfPlayers:.
28.56 }
28.57 return self;
28.58 }
28.59
28.60 -- (id) init
28.61 +
28.62 +- (id) initWithCoder: (NSCoder*)decoder
28.63 {
28.64 - CFUUIDRef uuidRef = CFUUIDCreate(NULL);
28.65 - NSString* uuid = (id) CFUUIDCreateString(NULL,uuidRef);
28.66 - self = [self initWithUniqueID: uuid];
28.67 - CFRelease(uuid);
28.68 - CFRelease(uuidRef);
28.69 + self = [self init];
28.70 + if( self ) {
28.71 + _players = [[decoder decodeObjectForKey: @"players"] mutableCopy];
28.72 + _winner = [decoder decodeObjectForKey: @"winner"];
28.73 + _turns = [[decoder decodeObjectForKey: @"turns"] mutableCopy];
28.74 + _extraValues = [[decoder decodeObjectForKey: @"extraValues"] mutableCopy];
28.75 + self.currentTurnNo = self.maxTurnNo;
28.76 + }
28.77 return self;
28.78 }
28.79
28.80 -- (id) initWithBoard: (GGBLayer*)board
28.81 +
28.82 +- (void) encodeWithCoder: (NSCoder*)coder
28.83 +{
28.84 + [coder encodeObject: _players forKey: @"players"];
28.85 + [coder encodeObject: _winner forKey: @"winner"];
28.86 + [coder encodeObject: _turns forKey: @"turns"];
28.87 + [coder encodeObject: _extraValues forKey: @"extraValues"];
28.88 +}
28.89 +
28.90 +
28.91 +- (id) initNewGameWithBoard: (GGBLayer*)board
28.92 {
28.93 self = [self init];
28.94 - if (self != nil) {
28.95 - _states = [[NSMutableArray alloc] init];
28.96 - _moves = [[NSMutableArray alloc] init];
28.97 - _board = [board retain];
28.98 - [board setValue: self forKey: @"Game"];
28.99 + if( self ) {
28.100 + self.board = board;
28.101 + NSAssert1(_players && _turns, @"%@ failed to set numberOfPlayers",self);
28.102 }
28.103 return self;
28.104 }
28.105 @@ -94,15 +83,67 @@
28.106 {
28.107 [_board release];
28.108 [_players release];
28.109 - [_currentMove release];
28.110 - [_states release];
28.111 - [_moves release];
28.112 + [_turns release];
28.113 + [_extraValues release];
28.114 [super dealloc];
28.115 }
28.116
28.117
28.118 -@synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner,
28.119 - currentMove=_currentMove, states=_states, moves=_moves, uniqueID=_uniqueID;
28.120 +@synthesize players=_players, winner=_winner, turns=_turns, requireConfirmation=_requireConfirmation;
28.121 +
28.122 +
28.123 +- (id)valueForUndefinedKey:(NSString *)key
28.124 +{
28.125 + return [_extraValues objectForKey: key];
28.126 +}
28.127 +
28.128 +- (void)setValue:(id)value forUndefinedKey:(NSString *)key
28.129 +{
28.130 + if( ! _extraValues )
28.131 + _extraValues = [[NSMutableDictionary alloc] init];
28.132 + if( value )
28.133 + [_extraValues setObject: value forKey: key];
28.134 + else
28.135 + [_extraValues removeObjectForKey: key];
28.136 +}
28.137 +
28.138 +
28.139 +#pragma mark -
28.140 +#pragma mark BOARD:
28.141 +
28.142 +
28.143 +- (void) setUpBoard
28.144 +{
28.145 + NSAssert1(NO,@"%@ forgot to implement -setUpBoard",[self class]);
28.146 +}
28.147 +
28.148 +- (GGBLayer*) board
28.149 +{
28.150 + return _board;
28.151 +}
28.152 +
28.153 +- (void) setBoard: (GGBLayer*)board
28.154 +{
28.155 + setObj(&_board,board);
28.156 + // Store a pointer to myself as the value of the "Game" property
28.157 + // of my root layer. (CALayers can have arbitrary KV properties stored into them.)
28.158 + // This is used by the -[CALayer game] category method defined below, to find the Game.
28.159 + [_board setValue: self forKey: @"Game"];
28.160 +
28.161 + BeginDisableAnimations();
28.162 +
28.163 + // Tell the game to add the necessary bits to the board:
28.164 + [self setUpBoard];
28.165 +
28.166 + // Re-apply the current state to set up the pieces/cards:
28.167 + self.stateString = [[_turns objectAtIndex: _currentTurnNo] boardState];
28.168 +
28.169 + EndDisableAnimations();
28.170 +}
28.171 +
28.172 +
28.173 +#pragma mark -
28.174 +#pragma mark PLAYERS:
28.175
28.176
28.177 - (void) setNumberOfPlayers: (unsigned)n
28.178 @@ -114,105 +155,184 @@
28.179 [players addObject: player];
28.180 [player release];
28.181 }
28.182 + self.players = players;
28.183 self.winner = nil;
28.184 - self.currentPlayer = nil;
28.185 - self.players = players;
28.186 +
28.187 + Turn *turn = [[Turn alloc] initStartOfGame: self];
28.188 + setObj(&_turns, [NSMutableArray arrayWithObject: turn]);
28.189 + [turn release];
28.190 + [self _startTurn];
28.191 }
28.192
28.193 -
28.194 -- (void) addToMove: (NSString*)str;
28.195 +- (Player*) remotePlayer
28.196 {
28.197 - [_currentMove appendString: str];
28.198 + for( Player *player in _players )
28.199 + if( ! player.local )
28.200 + return player;
28.201 + return nil;
28.202 }
28.203
28.204 -
28.205 -- (BOOL) _rememberState
28.206 +- (BOOL) isLocal
28.207 {
28.208 - if( self.isLatestTurn ) {
28.209 - [_states addObject: self.stateString];
28.210 - return YES;
28.211 - } else
28.212 - return NO;
28.213 + return self.remotePlayer == nil;
28.214 }
28.215
28.216 +- (Player*) currentPlayer
28.217 +{
28.218 + return self.currentTurn.player;
28.219 +}
28.220
28.221 -- (void) nextPlayer
28.222 ++ (NSArray*) keyPathsForValuesAffectingCurrentPlayer {return [NSArray arrayWithObject: @"currentTurn"];}
28.223 +
28.224 +
28.225 +#pragma mark -
28.226 +#pragma mark TURNS:
28.227 +
28.228 +
28.229 +- (Turn*) currentTurn
28.230 {
28.231 - BOOL latestTurn = [self _rememberState];
28.232 - if( ! _currentPlayer ) {
28.233 - NSLog(@"*** The %@ Begins! ***", self.class);
28.234 - self.currentPlayer = [_players objectAtIndex: 0];
28.235 - } else {
28.236 - self.currentPlayer = _currentPlayer.nextPlayer;
28.237 - if( latestTurn ) {
28.238 - [self willChangeValueForKey: @"currentTurn"];
28.239 - _currentTurn++;
28.240 - [self didChangeValueForKey: @"currentTurn"];
28.241 - }
28.242 - }
28.243 - NSLog(@"Current player is %@",_currentPlayer);
28.244 + return [_turns objectAtIndex: _currentTurnNo];
28.245 +}
28.246 +
28.247 +- (Turn*) latestTurn
28.248 +{
28.249 + return [_turns lastObject];
28.250 +}
28.251 +
28.252 ++ (NSArray*) keyPathsForValuesAffectingCurrentTurn {return [NSArray arrayWithObject: @"currentTurnNo"];}
28.253 ++ (NSArray*) keyPathsForValuesAffectingLatestTurn {return [NSArray arrayWithObject: @"turns"];}
28.254 +
28.255 +
28.256 +- (void) _startTurn
28.257 +{
28.258 + Turn *lastTurn = [_turns lastObject];
28.259 + NSAssert(lastTurn.status==kTurnFinished,@"Can't _startTurn till previous turn is finished");
28.260 + Turn *newTurn = [[Turn alloc] initWithPlayer: lastTurn.nextPlayer];
28.261 +
28.262 + [self willChangeValueForKey: @"turns"];
28.263 + [_turns addObject: newTurn];
28.264 + [self willChangeValueForKey: @"turns"];
28.265 + [newTurn release];
28.266 + self.currentTurnNo = _turns.count-1;
28.267 }
28.268
28.269
28.270 - (void) endTurn
28.271 {
28.272 - NSLog(@"--- End of turn (move was '%@')", _currentMove);
28.273 - if( self.isLatestTurn ) {
28.274 - NSString *move = [[_currentMove copy] autorelease];
28.275 - [_currentMove setString: @""];
28.276 - [self willChangeValueForKey: @"maxTurn"];
28.277 - [_moves addObject: move];
28.278 - [self didChangeValueForKey: @"maxTurn"];
28.279 - }
28.280 + Turn *curTurn = self.currentTurn;
28.281 + if( curTurn.isLatestTurn && ! curTurn.replaying ) {
28.282 + curTurn.status = kTurnComplete;
28.283 + NSLog(@"--- End of %@", curTurn);
28.284 +
28.285 + Player *winner = [self checkForWinner];
28.286 + if( winner ) {
28.287 + NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner);
28.288 + self.winner = winner;
28.289 + }
28.290 +
28.291 + if( ! _requireConfirmation || !curTurn.player.local )
28.292 + [self confirmCurrentTurn];
28.293
28.294 - Player *winner = [self checkForWinner];
28.295 - if( winner ) {
28.296 - NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner);
28.297 - [self _rememberState];
28.298 - self.winner = winner;
28.299 - } else
28.300 - [self nextPlayer];
28.301 -}
28.302 -
28.303 -
28.304 -#pragma mark -
28.305 -#pragma mark STORED TURNS:
28.306 -
28.307 -
28.308 -- (unsigned) maxTurn
28.309 -{
28.310 - return _moves.count;
28.311 -}
28.312 -
28.313 -- (unsigned) currentTurn
28.314 -{
28.315 - return _currentTurn;
28.316 -}
28.317 -
28.318 -- (void) setCurrentTurn: (unsigned)turn
28.319 -{
28.320 - NSParameterAssert(turn<=self.maxTurn);
28.321 - if( turn != _currentTurn ) {
28.322 - if( turn==_currentTurn+1 ) {
28.323 - [self applyMoveString: [_moves objectAtIndex: _currentTurn]];
28.324 - } else {
28.325 - BeginDisableAnimations();
28.326 - self.stateString = [_states objectAtIndex: turn];
28.327 - EndDisableAnimations();
28.328 - }
28.329 - _currentTurn = turn;
28.330 - self.currentPlayer = [_players objectAtIndex: (turn % _players.count)];
28.331 + [[NSNotificationCenter defaultCenter] postNotificationName: kTurnCompleteNotification
28.332 + object: curTurn];
28.333 }
28.334 }
28.335
28.336 +- (void) cancelCurrentTurn
28.337 +{
28.338 + Turn *curTurn = self.currentTurn;
28.339 + if( curTurn.status > kTurnEmpty && curTurn.status < kTurnFinished ) {
28.340 + if( _winner )
28.341 + self.winner = nil;
28.342 + if( _board )
28.343 + self.stateString = curTurn.previousTurn.boardState;
28.344 + curTurn.status = kTurnEmpty;
28.345 + }
28.346 +}
28.347 +
28.348 +- (void) confirmCurrentTurn
28.349 +{
28.350 + Turn *curTurn = self.currentTurn;
28.351 + if( curTurn.status == kTurnComplete ) {
28.352 + curTurn.status = kTurnFinished;
28.353 + if( ! _winner )
28.354 + [self _startTurn];
28.355 + }
28.356 +}
28.357 +
28.358
28.359 - (BOOL) isLatestTurn
28.360 {
28.361 - return _currentTurn == MAX(_states.count,1)-1;
28.362 + return _currentTurnNo == _turns.count-1;
28.363 }
28.364
28.365 +- (unsigned) maxTurnNo
28.366 +{
28.367 + return _turns.count-1;
28.368 +}
28.369
28.370 -- (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst
28.371 ++ (NSArray*) keyPathsForValuesAffectingIsLatestTurn {return [NSArray arrayWithObjects: @"currentTurnNo",@"turns",nil];}
28.372 ++ (NSArray*) keyPathsForValuesAffectingMaxTurnNo {return [NSArray arrayWithObjects: @"turns",nil];}
28.373 +
28.374 +- (unsigned) currentTurnNo
28.375 +{
28.376 + return _currentTurnNo;
28.377 +}
28.378 +
28.379 +
28.380 +#pragma mark -
28.381 +#pragma mark REPLAYING TURNS:
28.382 +
28.383 +
28.384 +- (void) setCurrentTurnNo: (unsigned)turnNo
28.385 +{
28.386 + NSParameterAssert(turnNo<=self.maxTurnNo);
28.387 + unsigned oldTurnNo = _currentTurnNo;
28.388 + if( turnNo != oldTurnNo ) {
28.389 + if( _board ) {
28.390 + Turn *turn = [_turns objectAtIndex: turnNo];
28.391 + NSString *state;
28.392 + if( turn.status == kTurnEmpty )
28.393 + state = turn.previousTurn.boardState;
28.394 + else
28.395 + state = turn.boardState;
28.396 + NSAssert1(state,@"empty boardState at turn #%i",turnNo);
28.397 + _currentTurnNo = turnNo;
28.398 + if( turnNo==oldTurnNo+1 ) {
28.399 + NSString *move = turn.move;
28.400 + if( move ) {
28.401 + NSLog(@"Reapplying move '%@'",move);
28.402 + turn.replaying = YES;
28.403 + @try{
28.404 + if( ! [self applyMoveString: move] ) {
28.405 + _currentTurnNo = oldTurnNo;
28.406 + NSBeep();
28.407 + NSLog(@"WARNING: %@ failed to apply stored move '%@'!", self,move);
28.408 + return;
28.409 + }
28.410 + }@finally{
28.411 + turn.replaying = NO;
28.412 + }
28.413 + }
28.414 + } else {
28.415 + NSLog(@"Reapplying state '%@'",state);
28.416 + BeginDisableAnimations();
28.417 + self.stateString = state;
28.418 + EndDisableAnimations();
28.419 + }
28.420 + if( ! [self.stateString isEqual: state] ) {
28.421 + _currentTurnNo = oldTurnNo;
28.422 + NSBeep();
28.423 + NSLog(@"WARNING: %@ failed to apply stored state '%@'!", self,state);
28.424 + return;
28.425 + }
28.426 + } else
28.427 + _currentTurnNo = turnNo;
28.428 + }
28.429 +}
28.430 +
28.431 +
28.432 +- (BOOL) animateMoveFrom: (CALayer<BitHolder>*)src to: (CALayer<BitHolder>*)dst
28.433 {
28.434 if( src==nil || dst==nil || dst==src )
28.435 return NO;
28.436 @@ -241,7 +361,35 @@
28.437
28.438 [src draggedBit: bit to: dst];
28.439 [self bit: bit movedFrom: src to: dst];
28.440 - src = dst;
28.441 + return YES;
28.442 +}
28.443 +
28.444 +
28.445 +- (BOOL) animatePlacementIn: (CALayer<BitHolder>*)dst
28.446 +{
28.447 + if( dst == nil )
28.448 + return NO;
28.449 + Bit *bit = [self bitToPlaceInHolder: dst];
28.450 + if( ! bit )
28.451 + return NO;
28.452 +
28.453 + CALayer<BitHolder>* oldHolder = (CALayer<BitHolder>*) bit.holder;
28.454 + if( oldHolder ) {
28.455 + if( oldHolder != dst )
28.456 + return [self animateMoveFrom: oldHolder to: dst];
28.457 + } else
28.458 + bit.position = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: _board.superlayer];
28.459 + ChangeSuperlayer(bit, _board.superlayer, -1);
28.460 + bit.pickedUp = YES;
28.461 + dst.highlighted = YES;
28.462 +
28.463 + DelayFor(0.2);
28.464 +
28.465 + dst.bit = bit;
28.466 + dst.highlighted = NO;
28.467 + bit.pickedUp = NO;
28.468 +
28.469 + [self bit: bit movedFrom: nil to: dst];
28.470 return YES;
28.471 }
28.472
28.473 @@ -250,12 +398,37 @@
28.474 #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN:
28.475
28.476
28.477 ++ (NSString*) identifier
28.478 +{
28.479 + NSString* name = [self description];
28.480 + if( [name hasSuffix: @"Game"] )
28.481 + name = [name substringToIndex: name.length-4];
28.482 + return name;
28.483 +}
28.484 +
28.485 ++ (NSString*) displayName
28.486 +{
28.487 + return [self identifier];
28.488 +}
28.489 +
28.490 + (BOOL) landscapeOriented
28.491 {
28.492 return NO;
28.493 }
28.494
28.495
28.496 +- (NSString*) initialStateString
28.497 +{
28.498 + return @"";
28.499 +}
28.500 +
28.501 +
28.502 +- (CGImageRef) iconForPlayer: (int)playerIndex
28.503 +{
28.504 + return nil;
28.505 +}
28.506 +
28.507 +
28.508 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src
28.509 {
28.510 return YES;
28.511 @@ -287,83 +460,20 @@
28.512 return nil;
28.513 }
28.514
28.515 -
28.516 +/* These are abstract
28.517 +
28.518 - (NSString*) stateString {return @"";}
28.519 - (void) setStateString: (NSString*)s { }
28.520
28.521 - (BOOL) applyMoveString: (NSString*)move {return NO;}
28.522 +*/
28.523
28.524 @end
28.525
28.526
28.527
28.528
28.529 -@implementation Player
28.530 -
28.531 -
28.532 -- (id) initWithGame: (Game*)game
28.533 -{
28.534 - self = [super init];
28.535 - if (self != nil) {
28.536 - _game = game;
28.537 - }
28.538 - return self;
28.539 -}
28.540 -
28.541 -- (id) initWithCoder: (NSCoder*)decoder
28.542 -{
28.543 - self = [self init];
28.544 - if( self ) {
28.545 - _game = [decoder decodeObjectForKey: @"game"];
28.546 - _name = [[decoder decodeObjectForKey: @"name"] copy];
28.547 - }
28.548 - return self;
28.549 -}
28.550 -
28.551 -- (void) encodeWithCoder: (NSCoder*)coder
28.552 -{
28.553 - [coder encodeObject: _game forKey: @"game"];
28.554 - [coder encodeObject: _name forKey: @"name"];
28.555 -}
28.556 -
28.557 -- (void) dealloc
28.558 -{
28.559 - [_name release];
28.560 - [super dealloc];
28.561 -}
28.562 -
28.563 -
28.564 -@synthesize game=_game, name=_name;
28.565 -
28.566 -- (BOOL) isCurrent {return self == _game.currentPlayer;}
28.567 -- (BOOL) isFriendly {return self == _game.currentPlayer;} // could be overridden for games with partners
28.568 -- (BOOL) isUnfriendly {return ! self.friendly;}
28.569 -
28.570 -- (int) index
28.571 -{
28.572 - return [_game.players indexOfObjectIdenticalTo: self];
28.573 -}
28.574 -
28.575 -- (Player*) nextPlayer
28.576 -{
28.577 - return [_game.players objectAtIndex: (self.index+1) % _game.players.count];
28.578 -}
28.579 -
28.580 -- (Player*) previousPlayer
28.581 -{
28.582 - return [_game.players objectAtIndex: (self.index-1) % _game.players.count];
28.583 -}
28.584 -
28.585 -- (NSString*) description
28.586 -{
28.587 - return [NSString stringWithFormat: @"%@[%@]", self.class,self.name];
28.588 -}
28.589 -
28.590 -@end
28.591 -
28.592 -
28.593 -
28.594 -
28.595 +#pragma mark -
28.596 @implementation CALayer (Game)
28.597
28.598 - (Game*) game
29.1 --- a/Source/GoGame.h Thu May 29 15:04:06 2008 -0700
29.2 +++ b/Source/GoGame.h Thu Jul 03 17:44:30 2008 -0700
29.3 @@ -20,7 +20,7 @@
29.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29.6 */
29.7 -#import "Game.h"
29.8 +#import "Game+Protected.h"
29.9 @class RectGrid, Stack;
29.10
29.11
29.12 @@ -33,3 +33,13 @@
29.13 }
29.14
29.15 @end
29.16 +
29.17 +
29.18 +/** 9x9 Go */
29.19 +@interface Go9Game : GoGame
29.20 +@end
29.21 +
29.22 +
29.23 +/** 13x13 Go */
29.24 +@interface Go13Game : GoGame
29.25 +@end
30.1 --- a/Source/GoGame.m Thu May 29 15:04:06 2008 -0700
30.2 +++ b/Source/GoGame.m Thu Jul 03 17:44:30 2008 -0700
30.3 @@ -32,76 +32,93 @@
30.4 @implementation GoGame
30.5
30.6
30.7 -- (id) initWithBoard: (GGBLayer*)board
30.8 ++ (int) dimensions {return 19;}
30.9 +
30.10 +- (id) init
30.11 {
30.12 - self = [super initWithBoard: board];
30.13 + self = [super init];
30.14 if (self != nil) {
30.15 [self setNumberOfPlayers: 2];
30.16 [(Player*)[_players objectAtIndex: 0] setName: @"Red"];
30.17 [(Player*)[_players objectAtIndex: 1] setName: @"White"];
30.18 -
30.19 - CGSize size = board.bounds.size;
30.20 - CGFloat boardSide = MIN(size.width,size.height);
30.21 - RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9
30.22 - frame: CGRectMake(floor((size.width-boardSide)/2),
30.23 - floor((size.height-boardSide)/2),
30.24 - boardSide,boardSide)];
30.25 - _grid = grid;
30.26 - grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
30.27 - grid.borderColor = kTranslucentLightGrayColor;
30.28 - grid.borderWidth = 2;
30.29 - grid.lineColor = kTranslucentGrayColor;
30.30 - grid.cellClass = [GoSquare class];
30.31 - [grid addAllCells];
30.32 - ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES;
30.33 - ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES;
30.34 - ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES;
30.35 - ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES;
30.36 - grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO;
30.37 - [board addSublayer: grid];
30.38 - [grid release];
30.39 -
30.40 - CGRect gridFrame = grid.frame;
30.41 - CGFloat pieceSize = (int)grid.spacing.width & ~1; // make sure it's even
30.42 - CGFloat captureHeight = gridFrame.size.height-4*pieceSize;
30.43 - _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0)
30.44 - spacing: CGSizeMake(0,pieceSize)
30.45 - wrapInterval: floor(captureHeight/pieceSize)
30.46 - wrapSpacing: CGSizeMake(-pieceSize,0)];
30.47 - _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize,
30.48 - CGRectGetMinY(gridFrame)+3*pieceSize,
30.49 - 2*pieceSize, captureHeight);
30.50 - _captured[0].zPosition = kPieceZ+1;
30.51 - [board addSublayer: _captured[0]];
30.52 - [_captured[0] release];
30.53 -
30.54 - _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight)
30.55 - spacing: CGSizeMake(0,-pieceSize)
30.56 - wrapInterval: floor(captureHeight/pieceSize)
30.57 - wrapSpacing: CGSizeMake(pieceSize,0)];
30.58 - _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize,
30.59 - CGRectGetMinY(gridFrame)+pieceSize,
30.60 - 2*pieceSize, captureHeight);
30.61 - _captured[1].zPosition = kPieceZ+1;
30.62 - [board addSublayer: _captured[1]];
30.63 - [_captured[1] release];
30.64 -
30.65 - [self nextPlayer];
30.66 - PreloadSound(@"Pop");
30.67 -}
30.68 + }
30.69 return self;
30.70 }
30.71 +
30.72 +- (void) setUpBoard
30.73 +{
30.74 + int dimensions = [[self class] dimensions];
30.75 + CGSize size = _board.bounds.size;
30.76 + CGFloat boardSide = MIN(size.width,size.height);
30.77 + RectGrid *grid = [[RectGrid alloc] initWithRows: dimensions columns: dimensions
30.78 + frame: CGRectMake(floor((size.width-boardSide)/2),
30.79 + floor((size.height-boardSide)/2),
30.80 + boardSide,boardSide)];
30.81 + _grid = grid;
30.82 + /*
30.83 + grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
30.84 + grid.borderColor = kTranslucentLightGrayColor;
30.85 + grid.borderWidth = 2;
30.86 + */
30.87 + grid.lineColor = kTranslucentGrayColor;
30.88 + grid.cellClass = [GoSquare class];
30.89 + [grid addAllCells];
30.90 + ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES;
30.91 + ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES;
30.92 + ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES;
30.93 + ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES;
30.94 + grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO;
30.95 + [_board addSublayer: grid];
30.96 + [grid release];
30.97 +
30.98 + CGRect gridFrame = grid.frame;
30.99 + CGFloat pieceSize = (int)grid.spacing.width & ~1; // make sure it's even
30.100 + CGFloat captureHeight = gridFrame.size.height-4*pieceSize;
30.101 + _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0)
30.102 + spacing: CGSizeMake(0,pieceSize)
30.103 + wrapInterval: floor(captureHeight/pieceSize)
30.104 + wrapSpacing: CGSizeMake(-pieceSize,0)];
30.105 + _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize,
30.106 + CGRectGetMinY(gridFrame)+3*pieceSize,
30.107 + 2*pieceSize, captureHeight);
30.108 + _captured[0].zPosition = kPieceZ+1;
30.109 + [_board addSublayer: _captured[0]];
30.110 + [_captured[0] release];
30.111 +
30.112 + _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight)
30.113 + spacing: CGSizeMake(0,-pieceSize)
30.114 + wrapInterval: floor(captureHeight/pieceSize)
30.115 + wrapSpacing: CGSizeMake(pieceSize,0)];
30.116 + _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize,
30.117 + CGRectGetMinY(gridFrame)+pieceSize,
30.118 + 2*pieceSize, captureHeight);
30.119 + _captured[1].zPosition = kPieceZ+1;
30.120 + [_board addSublayer: _captured[1]];
30.121 + [_captured[1] release];
30.122
30.123 + PreloadSound(@"Pop");
30.124 +}
30.125 +
30.126 +- (CGImageRef) iconForPlayer: (int)playerNum
30.127 +{
30.128 + return GetCGImageNamed( playerNum ?@"bot086.png" :@"bot089.png" );
30.129 +}
30.130 +
30.131 +- (Piece*) pieceForPlayer: (int)index
30.132 +{
30.133 + NSString *imageName = index ?@"bot086.png" :@"bot089.png";
30.134 + CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1; // make sure it's even
30.135 + Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
30.136 + stone.owner = [self.players objectAtIndex: index];
30.137 + return [stone autorelease];
30.138 +}
30.139
30.140 - (Bit*) bitToPlaceInHolder: (id<BitHolder>)holder
30.141 {
30.142 if( holder.bit != nil || ! [holder isKindOfClass: [GoSquare class]] )
30.143 return nil;
30.144 - NSString *imageName = self.currentPlayer.index ?@"White Ball.png" :@"Red Ball.png";
30.145 - CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1; // make sure it's even
30.146 - Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
30.147 - stone.owner = self.currentPlayer;
30.148 - return [stone autorelease];
30.149 + else
30.150 + return [self pieceForPlayer: self.currentPlayer.index];
30.151 }
30.152
30.153
30.154 @@ -143,7 +160,7 @@
30.155 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
30.156 {
30.157 Square *dst=(Square*)dstHolder;
30.158 - int curIndex = _currentPlayer.index;
30.159 + int curIndex = self.currentPlayer.index;
30.160 // Check for captured enemy groups:
30.161 BOOL captured = NO;
30.162 for( GridCell *c in dst.neighbors )
30.163 @@ -158,8 +175,9 @@
30.164 }
30.165 if( captured )
30.166 PlaySound(@"Pop");
30.167 -
30.168 - [self nextPlayer];
30.169 +
30.170 + [self.currentTurn addToMove: dst.name];
30.171 + [self endTurn];
30.172 }
30.173
30.174
30.175 @@ -167,4 +185,62 @@
30.176 // both of which are rather complex to decide in Go.
30.177
30.178
30.179 +#pragma mark -
30.180 +#pragma mark STATE:
30.181 +
30.182 +
30.183 +- (NSString*) stateString
30.184 +{
30.185 + int n = _grid.rows;
30.186 + unichar state[n*n];
30.187 + for( int y=0; y<n; y++ )
30.188 + for( int x=0; x<n; x++ ) {
30.189 + Bit *bit = [_grid cellAtRow: y column: x].bit;
30.190 + unichar ch;
30.191 + if( bit==nil )
30.192 + ch = '-';
30.193 + else
30.194 + ch = '1' + bit.owner.index;
30.195 + state[y*n+x] = ch;
30.196 + }
30.197 + return [NSString stringWithCharacters: state length: n*n];
30.198 +}
30.199 +
30.200 +- (void) setStateString: (NSString*)state
30.201 +{
30.202 + NSLog(@"Go: setStateString: '%@'",state);
30.203 + int n = _grid.rows;
30.204 + for( int y=0; y<n; y++ )
30.205 + for( int x=0; x<n; x++ ) {
30.206 + int i = y*n+x;
30.207 + Piece *piece = nil;
30.208 + if( i < state.length ) {
30.209 + int index = [state characterAtIndex: i] - '1';
30.210 + if( index==0 || index==1 )
30.211 + piece = [self pieceForPlayer: index];
30.212 + }
30.213 + [_grid cellAtRow: y column: x].bit = piece;
30.214 + }
30.215 +}
30.216 +
30.217 +
30.218 +- (BOOL) applyMoveString: (NSString*)move
30.219 +{
30.220 + NSLog(@"Go: applyMoveString: '%@'",move);
30.221 + return [self animatePlacementIn: [_grid cellWithName: move]];
30.222 +}
30.223 +
30.224 +
30.225 @end
30.226 +
30.227 +
30.228 +@implementation Go9Game
30.229 ++ (NSString*) displayName {return @"Go (9x9)";}
30.230 ++ (int) dimensions {return 9;}
30.231 +@end
30.232 +
30.233 +
30.234 +@implementation Go13Game
30.235 ++ (NSString*) displayName {return @"Go (13x13)";}
30.236 ++ (int) dimensions {return 13;}
30.237 +@end
31.1 --- a/Source/Grid.m Thu May 29 15:04:06 2008 -0700
31.2 +++ b/Source/Grid.m Thu Jul 03 17:44:30 2008 -0700
31.3 @@ -23,6 +23,7 @@
31.4 #import "Grid.h"
31.5 #import "Bit.h"
31.6 #import "Game.h"
31.7 +#import "Player.h"
31.8 #import "QuartzUtils.h"
31.9
31.10
31.11 @@ -127,7 +128,7 @@
31.12 GridCell *cell = [[_cellClass alloc] initWithGrid: self
31.13 row: row column: col
31.14 frame: frame];
31.15 - cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),col];
31.16 + cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
31.17 return [cell autorelease];
31.18 }
31.19
32.1 --- a/Source/HexGrid.m Thu May 29 15:04:06 2008 -0700
32.2 +++ b/Source/HexGrid.m Thu Jul 03 17:44:30 2008 -0700
32.3 @@ -57,9 +57,17 @@
32.4 // Compute the horizontal spacing:
32.5 CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
32.6 (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
32.7 - return [self initWithRows: nRows columns: nColumns
32.8 + self = [self initWithRows: nRows columns: nColumns
32.9 spacing: CGSizeMake(s,s)
32.10 position: frame.origin];
32.11 + if( self ) {
32.12 + // Center in frame:
32.13 + CGRect curFrame = self.frame;
32.14 + curFrame.origin.x = round( curFrame.origin.x + (frame.size.width - curFrame.size.width )/2.0f );
32.15 + curFrame.origin.y = round( curFrame.origin.y + (frame.size.height- curFrame.size.height)/2.0f );
32.16 + self.frame = curFrame;
32.17 + }
32.18 + return self;
32.19 }
32.20
32.21
33.1 --- a/Source/HexchequerGame.m Thu May 29 15:04:06 2008 -0700
33.2 +++ b/Source/HexchequerGame.m Thu Jul 03 17:44:30 2008 -0700
33.3 @@ -30,19 +30,20 @@
33.4 @implementation HexchequerGame
33.5
33.6
33.7 -- (Grid*) x_makeGrid
33.8 +- (void) setUpBoard
33.9 {
33.10 HexGrid *grid = [[HexGrid alloc] initWithRows: 9 columns: 9 frame: _board.bounds];
33.11 _grid = grid;
33.12 + [_board addSublayer: _grid];
33.13 CGPoint pos = grid.position;
33.14 - pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
33.15 + pos.x += grid.spacing.width / 4; // The right edge of the grid is blank because of the unused cells outside the hexagon
33.16 grid.position = pos;
33.17 grid.allowsMoves = YES;
33.18 grid.allowsCaptures = NO; // no land-on captures, that is
33.19 grid.cellColor = CreateGray(1.0, 0.25);
33.20 grid.lineColor = kTranslucentLightGrayColor;
33.21 -
33.22 [grid addCellsInHexagon];
33.23 + [_cells removeAllObjects];
33.24 for( int y=0; y<9; y++ ) {
33.25 for( int x=0; x<9; x++ ) {
33.26 GridCell *cell = [_grid cellAtRow: y column: x];
33.27 @@ -50,14 +51,12 @@
33.28 [_cells addObject: cell];
33.29 }
33.30 }
33.31 - self.stateString = @"111111111111111111-------------------------222222222222222222";
33.32 -
33.33 - [self performSelector: @selector(applyMoveString:) withObject: @"C4D4" afterDelay: 2.0];
33.34 - [self performSelector: @selector(applyMoveString:) withObject: @"G3F3" afterDelay: 5.0];
33.35 - [self performSelector: @selector(applyMoveString:) withObject: @"D4E4" afterDelay: 8.0];
33.36 - [self performSelector: @selector(applyMoveString:) withObject: @"F3D4" afterDelay: 11.0];
33.37 -
33.38 - return grid;
33.39 +}
33.40 +
33.41 +
33.42 +- (NSString*) initialStateString
33.43 +{
33.44 + return @"111111111111111111-------------------------222222222222222222";
33.45 }
33.46
33.47
33.48 @@ -78,9 +77,11 @@
33.49 Hex *src=(Hex*)srcHolder, *dst=(Hex*)dstHolder;
33.50 int playerIndex = self.currentPlayer.index;
33.51
33.52 - if( self.currentMove.length==0 )
33.53 - [self.currentMove appendString: src.name];
33.54 - [self.currentMove appendString: dst.name];
33.55 + Turn *turn = self.currentTurn;
33.56 + if( turn.move.length==0 )
33.57 + [turn addToMove: src.name];
33.58 + [turn addToMove: @"-"];
33.59 + [turn addToMove: dst.name];
33.60
33.61 BOOL isKing = ([bit valueForKey: @"King"] != nil);
33.62 PlaySound(isKing ?@"Funk" :@"Tink");
33.63 @@ -111,9 +112,9 @@
33.64
33.65 if( capture ) {
33.66 PlaySound(@"Pop");
33.67 - Bit *bit = capture.bit;
33.68 - _numPieces[bit.owner.index]--;
33.69 - [bit destroy];
33.70 + [turn addToMove: @"!"];
33.71 + _numPieces[capture.bit.owner.index]--;
33.72 + [capture destroyBit];
33.73
33.74 // Now check if another capture is possible. If so, don't end the turn:
33.75 if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
34.1 --- a/Source/KlondikeGame.h Thu May 29 15:04:06 2008 -0700
34.2 +++ b/Source/KlondikeGame.h Thu Jul 03 17:44:30 2008 -0700
34.3 @@ -20,7 +20,7 @@
34.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
34.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34.6 */
34.7 -#import "Game.h"
34.8 +#import "Game+Protected.h"
34.9 @class Deck, Stack;
34.10
34.11
35.1 --- a/Source/KlondikeGame.m Thu May 29 15:04:06 2008 -0700
35.2 +++ b/Source/KlondikeGame.m Thu Jul 03 17:44:30 2008 -0700
35.3 @@ -39,64 +39,68 @@
35.4 }
35.5
35.6
35.7 -- (id) initWithBoard: (GGBLayer*)board
35.8 +- (id) init
35.9 {
35.10 - self = [super initWithBoard: board];
35.11 - if (self != nil) {
35.12 + self = [super init];
35.13 + if (self != nil)
35.14 [self setNumberOfPlayers: 1];
35.15 + return self;
35.16 +}
35.17 +
35.18
35.19 - CGSize boardSize = board.bounds.size;
35.20 - CGFloat xSpacing = floor(boardSize.width/7);
35.21 - CGSize kCardSize;
35.22 - kCardSize.width = round(xSpacing * 0.9); // 1/7th of width, with 10% gap
35.23 - kCardSize.height = round(kCardSize.width * 1.5);
35.24 - CGFloat gap = xSpacing-kCardSize.width;
35.25 - [Card setCardSize: kCardSize];
35.26 +- (void) setUpBoard
35.27 +{
35.28 + CGSize boardSize = _board.bounds.size;
35.29 + CGFloat xSpacing = floor(boardSize.width/7);
35.30 + CGSize kCardSize;
35.31 + kCardSize.width = round(xSpacing * 0.9); // 1/7th of width, with 10% gap
35.32 + kCardSize.height = round(kCardSize.width * 1.5);
35.33 + CGFloat gap = xSpacing-kCardSize.width;
35.34 + [Card setCardSize: kCardSize];
35.35 +
35.36 + CGPoint pos = {floor(gap/2)+kCardSize.width/2, floor(boardSize.height-kCardSize.height/2)};
35.37 + [_deck release];
35.38 + _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]];
35.39 + [_deck shuffle];
35.40 + _deck.position = pos;
35.41 + [_board addSublayer: _deck];
35.42 +
35.43 + pos.x += xSpacing;
35.44 + [_sink release];
35.45 + _sink = [[Deck alloc] init];
35.46 + _sink.position = pos;
35.47 + [_board addSublayer: _sink];
35.48 +
35.49 + pos.x += xSpacing;
35.50 + for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) {
35.51 + pos.x += xSpacing;
35.52 + Deck *aces = [[Deck alloc] init];
35.53 + aces.position = pos;
35.54 + [_board addSublayer: aces];
35.55 + [_aces[suit] release];
35.56 + _aces[suit] = aces;
35.57 + }
35.58 +
35.59 + CGRect stackFrame = {{floor(gap/2), gap},
35.60 + {kCardSize.width, boardSize.height-kCardSize.height-2*gap}};
35.61 + CGPoint startPos = CGPointMake(kCardSize.width/2,kCardSize.height/2);
35.62 + CGSize spacing = {0, floor((stackFrame.size.height-kCardSize.height)/11.0)};
35.63 + for( int s=0; s<7; s++ ) {
35.64 + Stack *stack = [[Stack alloc] initWithStartPos: startPos spacing: spacing];
35.65 + stack.frame = stackFrame;
35.66 + stackFrame.origin.x += xSpacing;
35.67 + stack.backgroundColor = nil; //kAlmostInvisibleWhiteColor;
35.68 + stack.dragAsStacks = YES;
35.69 + [_board addSublayer: stack];
35.70
35.71 - CGPoint pos = {floor(gap/2)+kCardSize.width/2, floor(boardSize.height-kCardSize.height/2)};
35.72 - _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]];
35.73 - [_deck shuffle];
35.74 - _deck.position = pos;
35.75 - [board addSublayer: _deck];
35.76 -
35.77 - pos.x += xSpacing;
35.78 - _sink = [[Deck alloc] init];
35.79 - _sink.position = pos;
35.80 - [board addSublayer: _sink];
35.81 -
35.82 - pos.x += xSpacing;
35.83 - for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) {
35.84 - pos.x += xSpacing;
35.85 - Deck *aces = [[Deck alloc] init];
35.86 - aces.position = pos;
35.87 - [board addSublayer: aces];
35.88 - _aces[suit] = aces;
35.89 - }
35.90 -
35.91 - CGRect stackFrame = {{floor(gap/2), gap},
35.92 - {kCardSize.width, boardSize.height-kCardSize.height-2*gap}};
35.93 - CGPoint startPos = CGPointMake(kCardSize.width/2,kCardSize.height/2);
35.94 - CGSize spacing = {0, floor((stackFrame.size.height-kCardSize.height)/11.0)};
35.95 - for( int s=0; s<7; s++ ) {
35.96 - Stack *stack = [[Stack alloc] initWithStartPos: startPos spacing: spacing];
35.97 - stack.frame = stackFrame;
35.98 - stackFrame.origin.x += xSpacing;
35.99 - stack.backgroundColor = nil; //kAlmostInvisibleWhiteColor;
35.100 - stack.dragAsStacks = YES;
35.101 - [board addSublayer: stack];
35.102 -
35.103 - // According to the rules, one card should be added to each stack in turn, instead
35.104 - // of populating entire stacks one at a time. However, if one trusts the Deck's
35.105 - // -shuffle method (which uses the random() function, seeded with a high-entropy
35.106 - // cryptographically-strong value), it shouldn't make any difference :-)
35.107 - for( int c=0; c<=s; c++ )
35.108 - [stack addBit: [_deck removeTopCard]];
35.109 - ((Card*)stack.bits.lastObject).faceUp = YES;
35.110 - }
35.111 -
35.112 - [self nextPlayer];
35.113 + // According to the rules, one card should be added to each stack in turn, instead
35.114 + // of populating entire stacks one at a time. However, if one trusts the Deck's
35.115 + // -shuffle method (which uses the random() function, seeded with a high-entropy
35.116 + // cryptographically-strong value), it shouldn't make any difference :-)
35.117 + for( int c=0; c<=s; c++ )
35.118 + [stack addBit: [_deck removeTopCard]];
35.119 + ((Card*)stack.bits.lastObject).faceUp = YES;
35.120 }
35.121 - return self;
35.122 }
35.123
35.124
35.125 @@ -185,7 +189,7 @@
35.126 for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ )
35.127 if( _aces[suit].cards.count < 13 )
35.128 return nil;
35.129 - return _currentPlayer;
35.130 + return self.currentPlayer;
35.131 }
35.132
35.133
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
36.2 +++ b/Source/Player.h Thu Jul 03 17:44:30 2008 -0700
36.3 @@ -0,0 +1,48 @@
36.4 +//
36.5 +// Player.h
36.6 +// YourMove
36.7 +//
36.8 +// Created by Jens Alfke on 7/3/08.
36.9 +// Copyright 2008 Jens Alfke. All rights reserved.
36.10 +//
36.11 +
36.12 +#import <Foundation/Foundation.h>
36.13 +@class Game;
36.14 +
36.15 +
36.16 +/** A mostly-passive object used to represent a player. */
36.17 +@interface Player : NSObject <NSCoding>
36.18 +{
36.19 + Game *_game;
36.20 + NSString *_name, *_uuid, *_address, *_addressType;
36.21 + BOOL _local;
36.22 +}
36.23 +
36.24 +- (id) initWithGame: (Game*)game;
36.25 +- (id) initWithName: (NSString*)name;
36.26 +
36.27 +- (id) initWithCoder: (NSCoder*)decoder;
36.28 +- (void) encodeWithCoder: (NSCoder*)coder;
36.29 +
36.30 +@property (readonly) Game *game;
36.31 +@property (copy) NSString *name, // Display name
36.32 + *UUID, // Address Book UUID
36.33 + *address, // Contact address
36.34 + *addressType; // Contact address type (an AB property type)
36.35 +@property (readonly) int index; // Player's index in the Game's -players array
36.36 +@property (readwrite,getter=isLocal) BOOL local; // Is the player on this computer? (Defaults to YES)
36.37 +@property (readonly, getter=isCurrent) BOOL current; // Is it this player's turn?
36.38 +@property (readonly, getter=isFriendly) BOOL friendly; // Is this player the current player or an ally?
36.39 +@property (readonly, getter=isUnfriendly) BOOL unfriendly; // Is this player an opponent of the current player?
36.40 +@property (readonly) Player *nextPlayer, *previousPlayer; // The next/previous player in sequence
36.41 +@property (readonly) CGImageRef icon;
36.42 +@end
36.43 +
36.44 +
36.45 +
36.46 +@interface CALayer (Game)
36.47 +
36.48 +/** Called on any CALayer in the game's layer tree, will return the current Game object. */
36.49 +@property (readonly) Game *game;
36.50 +
36.51 +@end
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
37.2 +++ b/Source/Player.m Thu Jul 03 17:44:30 2008 -0700
37.3 @@ -0,0 +1,105 @@
37.4 +//
37.5 +// Player.m
37.6 +// YourMove
37.7 +//
37.8 +// Created by Jens Alfke on 7/3/08.
37.9 +// Copyright 2008 Jens Alfke. All rights reserved.
37.10 +//
37.11 +
37.12 +#import "Player.h"
37.13 +#import "Game.h"
37.14 +
37.15 +
37.16 +#pragma mark -
37.17 +@implementation Player
37.18 +
37.19 +
37.20 +- (id) initWithGame: (Game*)game
37.21 +{
37.22 + self = [super init];
37.23 + if (self != nil) {
37.24 + _game = game;
37.25 + _local = YES;
37.26 + }
37.27 + return self;
37.28 +}
37.29 +
37.30 +- (id) initWithName: (NSString*)name
37.31 +{
37.32 + self = [super init];
37.33 + if (self != nil) {
37.34 + self.name = name;
37.35 + }
37.36 + return self;
37.37 +}
37.38 +
37.39 +
37.40 +- (id) initWithCoder: (NSCoder*)decoder
37.41 +{
37.42 + self = [self init];
37.43 + if( self ) {
37.44 + _game = [decoder decodeObjectForKey: @"game"];
37.45 + _name = [[decoder decodeObjectForKey: @"name"] copy];
37.46 + _uuid = [[decoder decodeObjectForKey: @"UUID"] copy];
37.47 + _address = [[decoder decodeObjectForKey: @"address"] copy];
37.48 + _addressType = [[decoder decodeObjectForKey: @"addressType"] copy];
37.49 + _local= [decoder decodeBoolForKey: @"local"];
37.50 + }
37.51 + return self;
37.52 +}
37.53 +
37.54 +- (void) encodeWithCoder: (NSCoder*)coder
37.55 +{
37.56 + [coder encodeObject: _game forKey: @"game"];
37.57 + [coder encodeObject: _name forKey: @"name"];
37.58 + [coder encodeObject: _uuid forKey: @"UUID"];
37.59 + [coder encodeObject: _address forKey: @"address"];
37.60 + [coder encodeObject: _addressType forKey: @"addressType"];
37.61 + [coder encodeBool: _local forKey: @"local"];
37.62 +}
37.63 +
37.64 +- (void) dealloc
37.65 +{
37.66 + [_name release];
37.67 + [_uuid release];
37.68 + [_address release];
37.69 + [_addressType release];
37.70 + [super dealloc];
37.71 +}
37.72 +
37.73 +
37.74 +@synthesize game=_game, name=_name, UUID=_uuid, address=_address, addressType=_addressType, local=_local;
37.75 +
37.76 +- (BOOL) isCurrent {return self == _game.currentPlayer;}
37.77 +- (BOOL) isFriendly {return self == _game.currentPlayer;} // could be overridden for games with partners
37.78 +- (BOOL) isUnfriendly {return ! self.friendly;}
37.79 +
37.80 ++ (NSArray*) keyPathsForValuesAffectingCurrent {return [NSArray arrayWithObject: @"game.currentPlayer"];}
37.81 +
37.82 +
37.83 +- (int) index
37.84 +{
37.85 + return [_game.players indexOfObjectIdenticalTo: self];
37.86 +}
37.87 +
37.88 +- (Player*) nextPlayer
37.89 +{
37.90 + return [_game.players objectAtIndex: (self.index+1) % _game.players.count];
37.91 +}
37.92 +
37.93 +- (Player*) previousPlayer
37.94 +{
37.95 + return [_game.players objectAtIndex: (self.index-1) % _game.players.count];
37.96 +}
37.97 +
37.98 +- (CGImageRef) icon
37.99 +{
37.100 + return [_game iconForPlayer: self.index];
37.101 +}
37.102 +
37.103 +- (NSString*) description
37.104 +{
37.105 + return [NSString stringWithFormat: @"%@[%@]", self.class,self.name];
37.106 +}
37.107 +
37.108 +@end
38.1 --- a/Source/QuartzUtils.h Thu May 29 15:04:06 2008 -0700
38.2 +++ b/Source/QuartzUtils.h Thu Jul 03 17:44:30 2008 -0700
38.3 @@ -26,7 +26,7 @@
38.4 /** Constants for various commonly used colors. */
38.5 extern CGColorRef kBlackColor, kWhiteColor,
38.6 kTranslucentGrayColor, kTranslucentLightGrayColor,
38.7 - kAlmostInvisibleWhiteColor,
38.8 + kTranslucentWhiteColor, kAlmostInvisibleWhiteColor,
38.9 kHighlightColor;
38.10
38.11 #if TARGET_OS_IPHONE
39.1 --- a/Source/QuartzUtils.m Thu May 29 15:04:06 2008 -0700
39.2 +++ b/Source/QuartzUtils.m Thu Jul 03 17:44:30 2008 -0700
39.3 @@ -27,7 +27,7 @@
39.4
39.5 CGColorRef kBlackColor, kWhiteColor,
39.6 kTranslucentGrayColor, kTranslucentLightGrayColor,
39.7 - kAlmostInvisibleWhiteColor,
39.8 + kTranslucentWhiteColor, kAlmostInvisibleWhiteColor,
39.9 kHighlightColor;
39.10
39.11
39.12 @@ -38,6 +38,7 @@
39.13 kWhiteColor = CreateGray(1.0, 1.0);
39.14 kTranslucentGrayColor = CreateGray(0.0, 0.5);
39.15 kTranslucentLightGrayColor = CreateGray(0.0, 0.25);
39.16 + kTranslucentWhiteColor = CreateGray(1, 0.25);
39.17 kAlmostInvisibleWhiteColor = CreateGray(1, 0.05);
39.18 kHighlightColor = CreateRGB(1, 1, 0, 0.5);
39.19 }
40.1 --- a/Source/TicTacToeGame.h Thu May 29 15:04:06 2008 -0700
40.2 +++ b/Source/TicTacToeGame.h Thu Jul 03 17:44:30 2008 -0700
40.3 @@ -20,7 +20,7 @@
40.4 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
40.5 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40.6 */
40.7 -#import "Game.h"
40.8 +#import "Game+Protected.h"
40.9 @class RectGrid, Dispenser;
40.10
40.11
41.1 --- a/Source/TicTacToeGame.m Thu May 29 15:04:06 2008 -0700
41.2 +++ b/Source/TicTacToeGame.m Thu Jul 03 17:44:30 2008 -0700
41.3 @@ -38,42 +38,44 @@
41.4 return [p autorelease];
41.5 }
41.6
41.7 -- (id) initWithBoard: (GGBLayer*)board
41.8 +- (id) init
41.9 {
41.10 - self = [super initWithBoard: board];
41.11 + self = [super init];
41.12 if (self != nil) {
41.13 [self setNumberOfPlayers: 2];
41.14 -
41.15 - // Create a 3x3 grid:
41.16 - CGFloat center = floor(CGRectGetMidX(board.bounds));
41.17 - _grid = [[RectGrid alloc] initWithRows: 3 columns: 3 frame: CGRectMake(center-150,0, 300,300)];
41.18 - [_grid addAllCells];
41.19 - _grid.allowsMoves = _grid.allowsCaptures = NO;
41.20 - _grid.cellColor = CreateGray(1.0, 0.25);
41.21 - _grid.lineColor = kTranslucentLightGrayColor;
41.22 - [board addSublayer: _grid];
41.23 -
41.24 - // Create piece dispensers for the two players:
41.25 - for( int playerNumber=0; playerNumber<=1; playerNumber++ ) {
41.26 - Piece *p = [self pieceForPlayer: playerNumber];
41.27 - CGFloat x = floor(CGRectGetMidX(_board.bounds));
41.28 -#if TARGET_OS_IPHONE
41.29 - x = x - 80 + 160*playerNumber;
41.30 - CGFloat y = 360;
41.31 -#else
41.32 - x += (playerNumber==0 ?-230 :230);
41.33 - CGFloat y = 175;
41.34 -#endif
41.35 - _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0
41.36 - frame: CGRectMake(x-45,y-45, 90,90)];
41.37 - [_board addSublayer: _dispenser[playerNumber]];
41.38 - }
41.39 -
41.40 - // And they're off!
41.41 - [self nextPlayer];
41.42 }
41.43 return self;
41.44 }
41.45 +
41.46 +- (void) setUpBoard
41.47 +{
41.48 + // Create a 3x3 grid:
41.49 + CGFloat center = floor(CGRectGetMidX(_board.bounds));
41.50 + [_grid release];
41.51 + _grid = [[RectGrid alloc] initWithRows: 3 columns: 3 frame: CGRectMake(center-150,0, 300,300)];
41.52 + [_grid addAllCells];
41.53 + _grid.allowsMoves = _grid.allowsCaptures = NO;
41.54 + _grid.cellColor = CreateGray(1.0, 0.25);
41.55 + _grid.lineColor = kTranslucentLightGrayColor;
41.56 + [_board addSublayer: _grid];
41.57 +
41.58 + // Create piece dispensers for the two players:
41.59 + for( int playerNumber=0; playerNumber<=1; playerNumber++ ) {
41.60 + Piece *p = [self pieceForPlayer: playerNumber];
41.61 + CGFloat x = floor(CGRectGetMidX(_board.bounds));
41.62 +#if TARGET_OS_IPHONE
41.63 + x = x - 80 + 160*playerNumber;
41.64 + CGFloat y = 360;
41.65 +#else
41.66 + x += (playerNumber==0 ?-230 :230);
41.67 + CGFloat y = 175;
41.68 +#endif
41.69 + [_dispenser[playerNumber] release];
41.70 + _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0
41.71 + frame: CGRectMake(x-45,y-45, 90,90)];
41.72 + [_board addSublayer: _dispenser[playerNumber]];
41.73 + }
41.74 +}
41.75
41.76
41.77 - (NSString*) stateString
41.78 @@ -92,12 +94,13 @@
41.79 - (void) setStateString: (NSString*)stateString
41.80 {
41.81 for( int i=0; i<9; i++ ) {
41.82 - Piece *piece;
41.83 - switch( [stateString characterAtIndex: i] ) {
41.84 - case 'X': case 'x': piece = [self pieceForPlayer: 0]; break;
41.85 - case 'O': case 'o': piece = [self pieceForPlayer: 1]; break;
41.86 - default: piece = nil; break;
41.87 - }
41.88 + Piece *piece = nil;
41.89 + if( i < stateString.length )
41.90 + switch( [stateString characterAtIndex: i] ) {
41.91 + case 'X': case 'x': piece = [self pieceForPlayer: 0]; break;
41.92 + case 'O': case 'o': piece = [self pieceForPlayer: 1]; break;
41.93 + default: break;
41.94 + }
41.95 [_grid cellAtRow: i/3 column: i%3].bit = piece;
41.96 }
41.97 }
41.98 @@ -116,16 +119,18 @@
41.99 {
41.100 Square *square = (Square*)dst;
41.101 int squareIndex = 3*square.row + square.column;
41.102 - [self.currentMove appendFormat: @"%@%i", bit.name, squareIndex];
41.103 + [self.currentTurn addToMove: [NSString stringWithFormat: @"%@%i", bit.name, squareIndex]];
41.104 [super bit: bit movedFrom: src to: dst];
41.105 }
41.106
41.107 +/* FIX: Need to restore this somehow, now that -nextPlayer is gone
41.108 - (void) nextPlayer
41.109 {
41.110 [super nextPlayer];
41.111 // Give the next player another piece to put down:
41.112 _dispenser[self.currentPlayer.index].quantity = 1;
41.113 }
41.114 + */
41.115
41.116 static Player* ownerAt( Grid *grid, int index )
41.117 {
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
42.2 +++ b/Source/Turn.h Thu Jul 03 17:44:30 2008 -0700
42.3 @@ -0,0 +1,61 @@
42.4 +//
42.5 +// Turn.h
42.6 +// YourMove
42.7 +//
42.8 +// Created by Jens Alfke on 7/3/08.
42.9 +// Copyright 2008 Jens Alfke. All rights reserved.
42.10 +//
42.11 +
42.12 +#import <Foundation/Foundation.h>
42.13 +@class Game, Player;
42.14 +
42.15 +
42.16 +typedef enum {
42.17 + kTurnEmpty, // No action yet
42.18 + kTurnPartial, // Action taken, but more needs to be done
42.19 + kTurnComplete, // Action complete, but player needs to confirm
42.20 + kTurnFinished // Turn is confirmed and finished
42.21 +} TurnStatus;
42.22 +
42.23 +
42.24 +extern NSString* const kTurnCompleteNotification;
42.25 +
42.26 +
42.27 +/** A record of a particular turn in a Game, including the player's move and the resulting state. */
42.28 +@interface Turn : NSObject <NSCoding>
42.29 +{
42.30 + Game *_game;
42.31 + Player *_player;
42.32 + TurnStatus _status;
42.33 + NSString *_move;
42.34 + NSString *_boardState;
42.35 + NSDate *_date;
42.36 + NSString *_comment;
42.37 + BOOL _replaying;
42.38 +}
42.39 +
42.40 +- (id) initWithPlayer: (Player*)player;
42.41 +- (id) initStartOfGame: (Game*)game;
42.42 +
42.43 +@property (readonly) Game *game;
42.44 +@property (readonly) Player *player, *nextPlayer;
42.45 +@property (readonly) Turn *previousTurn;
42.46 +@property (readonly) unsigned turnNumber;
42.47 +@property (readonly) BOOL isLatestTurn;
42.48 +
42.49 +@property TurnStatus status;
42.50 +
42.51 +@property (readonly,copy) NSString *move;
42.52 +@property (readonly,copy) NSString *boardState;
42.53 +@property (readonly,retain)NSDate *date;
42.54 +@property (copy) NSString *comment;
42.55 +
42.56 +/** Appends to the move string. Only allowed if the status is Empty or Partial. */
42.57 +- (void) addToMove: (NSString*)move;
42.58 +
42.59 +/** Copies the current state of the Game's board to my boardState */
42.60 +- (void) captureBoardState;
42.61 +
42.62 +@property BOOL replaying;
42.63 +
42.64 +@end
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
43.2 +++ b/Source/Turn.m Thu Jul 03 17:44:30 2008 -0700
43.3 @@ -0,0 +1,161 @@
43.4 +//
43.5 +// Turn.m
43.6 +// YourMove
43.7 +//
43.8 +// Created by Jens Alfke on 7/3/08.
43.9 +// Copyright 2008 Jens Alfke. All rights reserved.
43.10 +//
43.11 +
43.12 +#import "Turn.h"
43.13 +#import "Game+Protected.h"
43.14 +#import "Player.h"
43.15 +
43.16 +
43.17 +NSString* const kTurnCompleteNotification = @"TurnComplete";
43.18 +
43.19 +
43.20 +@interface Turn ()
43.21 +@property (copy) NSString *move, *boardState;
43.22 +@property (retain) NSDate *date;
43.23 +@end
43.24 +
43.25 +
43.26 +
43.27 +@implementation Turn
43.28 +
43.29 +
43.30 +- (id) initWithPlayer: (Player*)player
43.31 +{
43.32 + NSParameterAssert(player!=nil);
43.33 + self = [super init];
43.34 + if (self != nil) {
43.35 + _game = player.game;
43.36 + _player = player;
43.37 + _status = kTurnEmpty;
43.38 + self.boardState = _game.latestTurn.boardState;
43.39 + }
43.40 + return self;
43.41 +}
43.42 +
43.43 +- (id) initStartOfGame: (Game*)game
43.44 +{
43.45 + NSParameterAssert(game!=nil);
43.46 + self = [super init];
43.47 + if (self != nil) {
43.48 + _game = game;
43.49 + _status = kTurnFinished;
43.50 + self.boardState = game.initialStateString;
43.51 + self.date = [NSDate date];
43.52 + }
43.53 + return self;
43.54 +}
43.55 +
43.56 +
43.57 +- (id) initWithCoder: (NSCoder*)decoder
43.58 +{
43.59 + self = [self init];
43.60 + if( self ) {
43.61 + _game = [decoder decodeObjectForKey: @"game"];
43.62 + _player = [decoder decodeObjectForKey: @"player"];
43.63 + _status = [decoder decodeIntForKey: @"status"];
43.64 + _move = [[decoder decodeObjectForKey: @"move"] copy];
43.65 + _boardState = [[decoder decodeObjectForKey: @"boardState"] copy];
43.66 + _date = [[decoder decodeObjectForKey: @"date"] copy];
43.67 + _comment = [[decoder decodeObjectForKey: @"comment"] copy];
43.68 + }
43.69 + return self;
43.70 +}
43.71 +
43.72 +- (void) encodeWithCoder: (NSCoder*)coder
43.73 +{
43.74 + [coder encodeObject: _game forKey: @"game"];
43.75 + [coder encodeObject: _player forKey: @"player"];
43.76 + [coder encodeInt: _status forKey: @"status"];
43.77 + [coder encodeObject: _move forKey: @"move"];
43.78 + [coder encodeObject: _boardState forKey: @"boardState"];
43.79 + [coder encodeObject: _date forKey: @"date"];
43.80 + [coder encodeObject: _comment forKey: @"comment"];
43.81 +}
43.82 +
43.83 +- (void) dealloc
43.84 +{
43.85 + [_move release];
43.86 + [_boardState release];
43.87 + [_date release];
43.88 + [_comment release];
43.89 + [super dealloc];
43.90 +}
43.91 +
43.92 +
43.93 +- (NSString*) description
43.94 +{
43.95 + return [NSString stringWithFormat: @"%@[%@, #%i, %@]", self.class, _game.class, self.turnNumber, _move];
43.96 +}
43.97 +
43.98 +
43.99 +@synthesize game=_game, player=_player, move=_move, boardState=_boardState, date=_date, comment=_comment,
43.100 + replaying=_replaying;
43.101 +
43.102 +
43.103 +- (unsigned) turnNumber {return [_game.turns indexOfObjectIdenticalTo: self];}
43.104 +- (BOOL) isLatestTurn {return _game.turns.lastObject == self;}
43.105 +- (Turn*) previousTurn {return [_game.turns objectAtIndex: self.turnNumber-1];}
43.106 +- (Player*) nextPlayer {return _player ?_player.nextPlayer :[_game.players objectAtIndex: 0];}
43.107 +
43.108 +- (TurnStatus) status {return _status;}
43.109 +
43.110 +- (void) setStatus: (TurnStatus)status
43.111 +{
43.112 + BOOL ok = NO;
43.113 + switch( _status ) {
43.114 + case kTurnEmpty:
43.115 + ok = (status==kTurnPartial) || (status==kTurnComplete);
43.116 + break;
43.117 + case kTurnPartial:
43.118 + ok = (status==kTurnEmpty) || (status==kTurnComplete) || (status==kTurnFinished);
43.119 + break;
43.120 + case kTurnComplete:
43.121 + ok = (status==kTurnEmpty) || (status==kTurnPartial) || (status==kTurnFinished);
43.122 + break;
43.123 + case kTurnFinished:
43.124 + break;
43.125 + }
43.126 + NSAssert2(ok,@"Illegal Turn status transition %i -> %i", _status,status);
43.127 +
43.128 + [self captureBoardState];
43.129 + _status = status;
43.130 + if( _status==kTurnEmpty ) {
43.131 + self.move = nil;
43.132 + self.date = nil;
43.133 + } else
43.134 + self.date = [NSDate date];
43.135 +}
43.136 +
43.137 +
43.138 +- (void) addToMove: (NSString*)move
43.139 +{
43.140 + if( ! _replaying ) {
43.141 + NSParameterAssert(move.length);
43.142 + NSAssert(_status<kTurnComplete,@"Complete Turn can't be modified");
43.143 + if( _move )
43.144 + move = [_move stringByAppendingString: move];
43.145 + self.move = move;
43.146 + [self captureBoardState];
43.147 + self.date = [NSDate date];
43.148 + if( _status==kTurnEmpty )
43.149 + self.status = kTurnPartial;
43.150 + }
43.151 +}
43.152 +
43.153 +
43.154 +- (void) captureBoardState
43.155 +{
43.156 + if( ! _replaying ) {
43.157 + NSAssert(_status<kTurnFinished,@"Finished Turn can't be modified");
43.158 + if( _game.board )
43.159 + self.boardState = _game.stateString;
43.160 + }
43.161 +}
43.162 +
43.163 +
43.164 +@end