Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
authorJens Alfke <jens@mooseyard.com>
Thu Jul 03 17:44:30 2008 -0700 (2008-07-03)
changeset 106c78cc6bd7a6
parent 9 a59acc683080
child 11 436cbdf56810
Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
English.lproj/MainMenu.nib/designable.nib
English.lproj/MainMenu.nib/keyedobjects.nib
GeekGameBoard.xcodeproj/project.pbxproj
Resources/Gingko/Blue.png
Resources/Gingko/Gold.png
Resources/Gingko/Green.png
Resources/Gingko/Red.png
Resources/Gingko/Violet.png
Source/Bit.m
Source/BitHolder.h
Source/BitHolder.m
Source/BoardUIView.m
Source/BoardView.h
Source/BoardView.m
Source/CheckersGame.h
Source/CheckersGame.m
Source/DemoBoardView.h
Source/DemoBoardView.m
Source/GGBLayer.h
Source/GGBLayer.m
Source/GGBTextLayer.m
Source/GGBUtils.h
Source/GGBUtils.m
Source/Game+Protected.h
Source/Game-Persistence.h
Source/Game-Persistence.m
Source/Game.h
Source/Game.m
Source/GoGame.h
Source/GoGame.m
Source/Grid.m
Source/HexGrid.m
Source/HexchequerGame.m
Source/KlondikeGame.h
Source/KlondikeGame.m
Source/Player.h
Source/Player.m
Source/QuartzUtils.h
Source/QuartzUtils.m
Source/TicTacToeGame.h
Source/TicTacToeGame.m
Source/Turn.h
Source/Turn.m
     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