# HG changeset patch # User Jens Alfke # Date 1204918982 28800 # Node ID e9f7ba4718e1441c578052e8f4d11aa16d0cddb0 Initial check-in into Mercurial. Branched from 1.0 release of Apple's sample code. No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game. diff -r 000000000000 -r e9f7ba4718e1 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,9 @@ +syntax: glob +.DS_Store +build +.svn +*.pbxuser +*.perspectivev3 +*.mpkg +*.framework +(*) \ No newline at end of file diff -r 000000000000 -r e9f7ba4718e1 English.lproj/InfoPlist.strings Binary file English.lproj/InfoPlist.strings has changed diff -r 000000000000 -r e9f7ba4718e1 English.lproj/MainMenu.nib/designable.nib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/English.lproj/MainMenu.nib/designable.nib Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,2126 @@ + + + + 0 + 9B18 + 629 + 949 + 343.00 + + YES + + + + + YES + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + NSApplication + + + FirstResponder + + + + + + AMainMenu + + YES + + + GeekGameBoard + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + + NSMenuMixedState + + submenuAction: + + + + YES + + + About GeekGameBoard + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide GeekGameBoard + h + 1048576 + 2147483647 + + + + + + Hide Others + + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit GeekGameBoard + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + New + n + 1048576 + 2147483647 + + + + + + T3BlbuKApg + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + U2F2ZSBBc+KApg + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + UHJpbnTigKY + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Game + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Klondike (Solitaire) + 1 + 1048840 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Checkers + 2 + 1048840 + 2147483647 + + + 1 + + + + Go + 3 + 1048840 + 2147483647 + + + 4 + + + + Hexchequer + 4 + 1048840 + 2147483647 + + + 2 + + + + Tic-Tac-Toe + 5 + 1048840 + 2147483647 + + + 3 + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Full Screen + f + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Q3VzdG9taXplIFRvb2xiYXLigKY + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + + + _NSMainMenu + + + 7 + 2 + {{57, 175}, {1084, 587}} + 1946157056 + + NSWindow + + + + 256 + + YES + + + 274 + {1084, 587} + + + YES + DemoBoardView + + + {1084, 587} + + + + {{0, 0}, {1440, 878}} + + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + enterFullScreen: + + + + 378 + + + + startGameFromMenu: + + + + 388 + + + + + + + + 389 + + + + + + + + 390 + + + + + + + + 391 + + + + + + + + 394 + + + + initialFirstResponder + + + + 399 + + + + delegate + + + + 400 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + MainMenu + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 103 + + + YES + + + + + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + + + + 80 + + + 8 + + + 78 + + + 6 + + + 72 + + + + + 82 + + + 9 + + + 124 + + + YES + + + + + + 77 + + + + + + 73 + + + + + + 79 + + + 7 + + + 112 + + + 10 + + + 74 + + + + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 106 + + + YES + + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + 1111 + + + 144 + + + + + 129 + + + 121 + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + + + 297 + + + + + 298 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 371 + + + YES + + + + + + 372 + + + YES + + + + + + 375 + + + + + 376 + + + + + 377 + + + + + 379 + + + YES + + + + + + 380 + + + YES + + + + + + + + + + + 381 + + + + + 382 + + + + + 383 + + + + + 384 + + + + + 387 + + + + + 393 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 371.IBPluginDependency + 371.IBWindowTemplateEditedContentRect + 371.NSWindowTemplate.visibleAtLaunch + 371.editorWindowContentRectSynchronizationRect + 372.IBPluginDependency + 375.IBPluginDependency + 376.IBPluginDependency + 377.IBPluginDependency + 379.IBPluginDependency + 380.IBPluginDependency + 380.editorWindowContentRectSynchronizationRect + 381.IBPluginDependency + 382.IBPluginDependency + 383.IBPluginDependency + 384.IBPluginDependency + 387.IBPluginDependency + 393.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + + + + + + + + {{497, 828}, {64, 6}} + + + + + + + {{522, 812}, {146, 23}} + + + + + + + {{436, 809}, {64, 6}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{271, 653}, {154, 183}} + + + + + + + + + {{514, 573}, {167, 43}} + + + + + + + + + + + + + + + {{525, 802}, {197, 73}} + + + {74, 862} + {{85, 834}, {473, 20}} + + + {{319, 763}, {234, 73}} + + + + {{117, 199}, {1084, 587}} + + {{117, 199}, {1084, 587}} + + + + + + + {{319, 721}, {205, 113}} + + + + + + + + + + + + + {{97, 651}, {250, 183}} + + + + + + + + + + + + + + + + + + + + + {{233, 633}, {199, 203}} + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 400 + + + + YES + + BoardView + NSView + + enterFullScreen: + id + + + YES + + YES + + + YES + + + + IBProjectSource + Source/BoardView.h + + + + DemoBoardView + BoardView + + startGameFromMenu: + + + + YES + + YES + + + YES + + + + + Source/DemoBoardView.h + + + + + 0 + ../../GeekGameBoard.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEByQALAAwAMQA1ADYAPAA9 +AEIAWABZAFoAWwALAGgAbAB2AHcAeAB9AHYACwCBAIMAhACFAIgAjACzALsAzADRANIA0wDYANkA2gDd +AOEA4gDlAOYA6gDuAPYA/AD9AP4BAgEJAQ0BDgEPARMBGgEeAR8BIAEkASwBMAExATIBMwE4ATkBPAFA +AUcBSAFJAUoBTgFVAVkBWgFbAV8BZgFnAWgBbAF0AXkBegF7AXwBgAGHAYgBiQGKAY4BlQGWAZcBmAGd +AaUBpgGnAawBtAG1AbYBugHBAcIBwwHIAc8B0AHRAdUB3AHhAeIB4wHnAe4B7wHwAfEB9QH8Af0B/gH/ +AgMCCgILAgwCDQIRAhoCGwIcAh0CIQIoAikCKgIuAjUCNgI3AjgCPAJDAkQCRQJGAkoCUQJSAlMCWAJZ +Al0CZAJlAmYCZwJsAnMCdAJ1AnoCgQKCAoMChAKJApECkgKTApcCngKfAqACoQKlAqwCrQKuAq8CswK6 +ArsCvAK9AsECyALJAsoCywLPAtYC1wLYAtwC4wLkAuUC5gMwAzEDOANDA0QDRgNRA1YDVwNlA24DdQN2 +A3cDgAOJA1YDigOPA5IDkwOcA6UDpgOqA6sDrgO3A7gDxQPOA9cD4ANWA+ED5gPvA1YD+ANWBAEECgQT +A1YEFAQdBB4EKAQxA1YEMgRABEkDVgRKBE4ETwRYA1YEWQRgBGkDVgRyBHMEdgR4BMIFDQVYBVkFWgVb +BVwFXQVeBV8FYAVhBWIFYwVkBWUFZgVnBWgFaQVqBWsFbAVtBW4FbwVwBXEFcgVzBXQFdQV2BXcFeAV5 +BXoFewV8BX0FfgV/BYAFgQWCBYMFhAWFBYYFhwWIBYkFigWLBYwFjQWOBY8FkAWRBZIFkwWUBZUFlgWX +BZgFmQWaBZsFnAWfBaIGEQaABoEGggaDBoQGhQaGBocGiAaJBooGiwaMBo0GjgaPBpAGkQaSBpMGlAaV +BpYGlwaYBpkGmgabBpwGnQaeBp8GoAahBqIGowakBqUGpganBqgGqQaqBqsGrAatBq4GrwawBrEGsgaz +BrQGtQa2BrcGuAa5BroGuwa8Br0Gvga/BsAGwQbCBsMGxAbFBsYGxwbIBskGygbLBswGzQbOBs8G0AbR +BtIG0wbUBtUG1gbXBtgG2QbaBtsG3AbdBt4G3wbgBuEG4gbjBuQG5QbmBucG6AbpBuoG6wbsBu8G8gb1 +VSRudWxs3xASAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUA +JgAnACgAKQAqACsALAAtAC4ALwAwVk5TUm9vdFYkY2xhc3NdTlNPYmplY3RzS2V5c18QD05TQ2xhc3Nl +c1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eU9pZHNWYWx1ZXNdTlNDb25uZWN0aW9uc1tOU05hbWVzS2V5 +c1tOU0ZyYW1ld29ya11OU0NsYXNzZXNLZXlzWk5TT2lkc0tleXNdTlNOYW1lc1ZhbHVlc18QGU5TQWNj +ZXNzaWJpbGl0eUNvbm5lY3RvcnNdTlNGb250TWFuYWdlcl8QEE5TVmlzaWJsZVdpbmRvd3NfEA9OU09i +amVjdHNWYWx1ZXNfEBdOU0FjY2Vzc2liaWxpdHlPaWRzS2V5c1lOU05leHRPaWRcTlNPaWRzVmFsdWVz +gAKBAciAy4EBVoEBx4AagQEPgAWBAVWBAVeBARCBAcWAAIAGgQEOgQHGEQGSgQFY0gAOADIAMwA0W05T +Q2xhc3NOYW1lgASAA11OU0FwcGxpY2F0aW9u0gA3ADgAOQA6WCRjbGFzc2VzWiRjbGFzc25hbWWiADoA +O15OU0N1c3RvbU9iamVjdFhOU09iamVjdF8QEElCQ29jb2FGcmFtZXdvcmvSAA4APgA/AEBaTlMub2Jq +ZWN0c4AZoQBBgAfbAEMADgBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWACtcTlNX +aW5kb3dWaWV3XE5TU2NyZWVuUmVjdF8QE05TRnJhbWVBdXRvc2F2ZU5hbWVdTlNXaW5kb3dUaXRsZVlO +U1dURmxhZ3NdTlNXaW5kb3dDbGFzc1xOU1dpbmRvd1JlY3RfEA9OU1dpbmRvd0JhY2tpbmdfEBFOU1dp +bmRvd1N0eWxlTWFza1tOU1ZpZXdDbGFzc4ALgBiAFoAXgAkSdAAAAIAKgAgQAhAHgABfEBh7ezU3LCAx +NzV9LCB7MTA4NCwgNTg3fX1dR2Vla0dhbWVCb2FyZFhOU1dpbmRvd9cAXAAOAF0AXgBfAFoAYABhAGIA +YwBkAGUAYQBnXxAPTlNOZXh0UmVzcG9uZGVyWk5TU3Vidmlld3NYTlN2RmxhZ3NbTlNGcmFtZVNpemVb +TlNTdXBlcnZpZXeADIAVgA0RAQCAE4AMgBTSAA4APgBpAGqAEqEAa4AO2ABcAA4AXgBfAFoAMgBgAG0A +TQBvAHAAcQBhAHMATQB1XxAVTlNWaWV3SXNMYXllclRyZWVIb3N0gAuAEREBEoAPgAyAEIALCVt7MTA4 +NCwgNTg3fV1EZW1vQm9hcmRWaWV30gA3ADgAeQB6pAB6AHsAfAA7XE5TQ3VzdG9tVmlld1ZOU1ZpZXdb +TlNSZXNwb25kZXLSADcAOAB+AH+jAH8AgAA7Xk5TTXV0YWJsZUFycmF5V05TQXJyYXnSADcAOACCAHuj +AHsAfAA7XxAVe3swLCAwfSwgezE0NDAsIDg3OH19UNIANwA4AIYAh6IAhwA7XxAQTlNXaW5kb3dUZW1w +bGF0ZdIANwA4AIkAiqMAigCLADtcTlNNdXRhYmxlU2V0VU5TU2V00gAOAD4AaQCOgBKvECQAjwCQAJEA +kgCTAJQAlQCWAJcAmACZAJoAmwCcAJ0AngCfAKAAoQCiAKMApAClAKYApwCoAKkAqgCrAKwArQCuAK8A +sACxALKAG4ApgC6AM4A4gD6AQYBGgEuAT4BVgFqAX4BjgGeAa4BvgHSAeYB+gIOAiICMgJGAloCagJyA +oYClgKqAroCzgLiAvYDCgMbUAA4AtAC1ALYAtwBrALkAul1OU0Rlc3RpbmF0aW9uWE5TU291cmNlV05T +TGFiZWyAKIAOgByAJ9kADgC8AL0AvgC/AMAAwQDCAMMAxADFAMYAxwDIAMkAygDLAFVXTlNUaXRsZV8Q +EU5TS2V5RXF1aXZNb2RNYXNrWk5TS2V5RXF1aXZdTlNNbmVtb25pY0xvY1lOU09uSW1hZ2VcTlNNaXhl +ZEltYWdlVk5TTWVudVVOU1RhZ4AmgB4SABABCIAfEn////+AIIAkgB3TAA4AvADNAM4AzwDQW05TTWVu +dUl0ZW1zgNCA9oD4WkhleGNoZXF1ZXJRNNMADgAyANQA1QDWANdeTlNSZXNvdXJjZU5hbWWAI4AhgCJX +TlNJbWFnZV8QD05TTWVudUNoZWNrbWFya9IANwA4ANsA3KIA3AA7XxAQTlNDdXN0b21SZXNvdXJjZdMA +DgAyANQA1QDWAOCAI4AhgCVfEBBOU01lbnVNaXhlZFN0YXRl0gA3ADgA4wDkogDkADtaTlNNZW51SXRl +bV8QEnN0YXJ0R2FtZUZyb21NZW51OtIANwA4AOcA6KMA6ADpADtfEBVOU05pYkNvbnRyb2xDb25uZWN0 +b3JeTlNOaWJDb25uZWN0b3LTAA4AtQC2ALcA7ADtgCiAKoAt2AAOALwAvQC+AL8AwADBAMIAxADwAPEA +UADIAMkAygD1gCaALBIAEAAAgBeAIIAkgCvUAA4AvAD3AM0AzgD5APoA+1ZOU05hbWWA0IDMgM+AzV8Q +EkJyaW5nIEFsbCB0byBGcm9udF8QD2FycmFuZ2VJbkZyb250OtMADgC1ALYAtwEAAQGAKIAvgDLYAA4A +vAC9AL4AvwDAAMEAwgDEAQQA8QBQAMgAyQDKAQiAJoAxgBeAIIAkgDDTAA4AvADNAM4BCwEMgNCA7IDu +XlN0YXJ0IFNwZWFraW5nXnN0YXJ0U3BlYWtpbmc60wAOALUAtgC3AREBEoAogDSAN9cADgC8AL4AvwDA +AMEAwgDEARUAUADIAMkAygEZgCaANoAXgCCAJIA10wAOALwAzQDOARwBHYDQgPyA/l8QD1JldmVydCB0 +byBTYXZlZF8QFnJldmVydERvY3VtZW50VG9TYXZlZDrTAA4AtQC2ALcBIgEjgCiAOYA92AAOALwAvQC+ +AL8AwADBAMIAxAEmAScBKADIAMkAygErgCaAOxIAEgAAgDyAIIAkgDrTAA4AvADNAM4BLgEvgNCA54Do +VFJlZG9RWlVyZWRvOtQADgC0ALUAtgE0AGsAHwE3gECADoACgD9YZGVsZWdhdGXSADcAOAE6ATujATsA +6QA7XxAUTlNOaWJPdXRsZXRDb25uZWN0b3LTAA4AtQC2ALcBPgE/gCiAQoBF2AAOALwAvQC+AL8AwADB +AMIAxAFCAPEBQwDIAMkAygEZgCaAQ4BEgCCAJIA1U05ld1FuXG5ld0RvY3VtZW50OtMADgC1ALYAtwFM +AU2AKIBHgErYAA4AvAC9AL4AvwDAAMEAwgDEAVAA8QBQAMgAyQDKAVSAJoBJgBeAIIAkgEjTAA4AvADN +AM4BVwFYgNCBAQWBAQdvEBIAQwB1AHMAdABvAG0AaQB6AGUAIABUAG8AbwBsAGIAYQByICZfEB9ydW5U +b29sYmFyQ3VzdG9taXphdGlvblBhbGV0dGU60wAOALUAtgC3AV0BXoAogEyATtgADgC8AL0AvgC/AMAA +wQDCAMQBYQDxAFAAyADJAMoBK4AmgE2AF4AggCSAOlZEZWxldGVXZGVsZXRlOtMADgC1ALYAtwFqAWuA +KIBQgFTYAA4AvAC9AL4AvwDAAMEAwgDEAW4BbwFwAMgAyQDKAXOAJoBSEgAYAACAU4AggCSAUdQADgC8 +APcAzQDOAFEBdwF4gNCACYDigNRbSGlkZSBPdGhlcnNRaF8QFmhpZGVPdGhlckFwcGxpY2F0aW9uczrT +AA4AtQC2ALcBfgF/gCiAVoBZ2AAOALwAvQC+AL8AwADBAMIAxAGCAPEBgwDIAMkAygEZgCaAV4BYgCCA +JIA1ZgBQAHIAaQBuAHQgJlFwVnByaW50OtMADgC1ALYAtwGMAY2AKIBbgF7YAA4AvAC9AL4AvwDAAMEA +wgDEAZAA8QGRAMgAyQDKARmAJoBcgF2AIIAkgDVlAE8AcABlAG4gJlFvXW9wZW5Eb2N1bWVudDrUAA4A +tAC1ALYAtwBrAZsAuoAogA6AYIAn2QAOALwAvQC+AL8AwADBAMIAwwDEAZ8AxgGgAMgAyQDKAMsBpIAm +gGGAYoAggCSAHRAEUkdvUTPUAA4AtAC1ALYAtwBrAaoAuoAogA6AZIAn2QAOALwAvQC+AL8AwADBAMIA +wwDEAa4AxgGvAMgAyQDKAMsBs4AmgGWAZoAggCSAHRADW1RpYy1UYWMtVG9lUTXTAA4AtQC2ALcBuAG5 +gCiAaIBq2AAOALwAvQC+AL8AwADBAMIAxAG8APEAUADIAMkAygD1gCaAaYAXgCCAJIArVFpvb21ccGVy +Zm9ybVpvb2061AAOALQAtQC2ALcAawHGALqAKIAOgGyAJ9gADgC8AL0AvgC/AMAAwQDCAMQBygDGAcsA +yADJAMoAy4AmgG2AboAggCSAHV8QFEtsb25kaWtlIChTb2xpdGFpcmUpUTHTAA4AtQC2ALcB0wHUgCiA +cIBz2AAOALwAvQC+AL8AwADBAMIAxAHXAPEAUADIAMkAygHbgCaAcoAXgCCAJIBx1AAOALwA9wDNAM4B +3gHfAeCA0IEBAIEBA4EBAlpDbGVhciBNZW51XxAVY2xlYXJSZWNlbnREb2N1bWVudHM60wAOALUAtgC3 +AeUB5oAogHWAeNgADgC8AL0AvgC/AMAAwQDCAMQB6QDxAeoAyADJAMoA9YAmgHaAd4AggCSAK1hNaW5p +bWl6ZVFtXxATcGVyZm9ybU1pbmlhdHVyaXplOtMADgC1ALYAtwHzAfSAKIB6gH3YAA4AvAC9AL4AvwDA +AMEAwgDEAfcA8QH4AMgAyQDKARmAJoB7gHyAIIAkgDVUU2F2ZVFzXXNhdmVEb2N1bWVudDrTAA4AtQC2 +ALcCAQICgCiAf4CC2AAOALwAvQC+AL8AwADBAMIAxAIFAPECBgDIAMkAygErgCaAgICBgCCAJIA6VVBh +c3RlUXZWcGFzdGU60wAOALUAtgC3Ag8CEIAogISAh9kADgISALwAvQC+AL8AwADBAMIAxABQAhUBJwIW +AMgAyQDKARlZTlNUb29sVGlwgCaAF4CFgIaAIIAkgDVdUGFnZSBTZXR1cC4uLlFQXnJ1blBhZ2VMYXlv +dXQ60wAOALUAtgC3Ah8CIIAogImAi9gADgC8AL0AvgC/AMAAwQDCAMQCIwDxAFAAyADJAMoBCIAmgIqA +F4AggCSAMF1TdG9wIFNwZWFraW5nXXN0b3BTcGVha2luZzrTAA4AtQC2ALcCLAItgCiAjYCQ2AAOALwA +vQC+AL8AwADBAMIAxAIwAPECMQDIAMkAygErgCaAjoCPgCCAJIA6WlNlbGVjdCBBbGxRYVpzZWxlY3RB +bGw60wAOALUAtgC3AjoCO4AogJKAldgADgC8AL0AvgC/AMAAwQDCAMQCPgDxAj8AyADJAMoBK4AmgJOA +lIAggCSAOlRDb3B5UWNVY29weTrTAA4AtQC2ALcCSAJJgCiAl4CZ2AAOALwAvQC+AL8AwADBAMIAxAJM +APEBcADIAMkAygFzgCaAmIBTgCCAJIBRXxASSGlkZSBHZWVrR2FtZUJvYXJkVWhpZGU61AAOALQAtQC2 +ATQAawBBAleAQIAOgAeAm18QFWluaXRpYWxGaXJzdFJlc3BvbmRlctMADgC1ALYAtwJbAlyAKICdgKDY +AA4AvAC9AL4AvwDAAMEAwgDEAl8A8QJgAMgAyQDKARmAJoCegJ+AIIAkgDVVQ2xvc2VRd11wZXJmb3Jt +Q2xvc2U61AAOALQAtQC2ALcAHwJqAmuAKIACgKKApNcADgC8AL4AvwDAAMEAwgDEAm4AUADIAMkAygFz +gCaAo4AXgCCAJIBRXxATQWJvdXQgR2Vla0dhbWVCb2FyZF8QHW9yZGVyRnJvbnRTdGFuZGFyZEFib3V0 +UGFuZWw61AAOALQAtQC2ALcAawJ4AnmAKIAOgKaAqdgADgC8AL0AvgC/AMAAwQDCAMQCfADxAn0AyADJ +AMoBVIAmgKeAqIAggCSASFtGdWxsIFNjcmVlblFmXxAQZW50ZXJGdWxsU2NyZWVuOtQADgC0ALUAtgC3 +AGsChwC6gCiADoCrgCfZAA4AvAC9AL4AvwDAAMEAwgDDAMQCiwDGAowAyADJAMoAywKQgCaArICtgCCA +JIAdEAFYQ2hlY2tlcnNRMtMADgC1ALYAtwKVApaAKICvgLLYAA4AvAC9AL4AvwDAAMEAwgDEApkA8QKa +AMgAyQDKASuAJoCwgLGAIIAkgDpUVW5kb1F6VXVuZG860wAOALUAtgC3AqMCpIAogLSAt9gADgC8AL0A +vgC/AMAAwQDCAMQCpwDxAqgAyADJAMoBc4AmgLWAtoAggCSAUV8QElF1aXQgR2Vla0dhbWVCb2FyZFFx +WnRlcm1pbmF0ZTrTAA4AtQC2ALcCsQKygCiAuYC82AAOALwAvQC+AL8AwADBAMIAxAK1APECtgDIAMkA +ygErgCaAuoC7gCCAJIA6U0N1dFF4VGN1dDrTAA4AtQC2ALcCvwLAgCiAvoDB2AAOALwAvQC+AL8AwADB +AMIAxALDAW8CxADIAMkAygFUgCaAv4DAgCCAJIBIXFNob3cgVG9vbGJhclF0XxATdG9nZ2xlVG9vbGJh +clNob3duOtMADgC1ALYAtwLNAs6AKIDDgMXYAA4AvAC9AL4AvwDAAMEAwgDEAtEA8QBQAMgAyQDKAXOA +JoDEgBeAIIAkgFFYU2hvdyBBbGxfEBZ1bmhpZGVBbGxBcHBsaWNhdGlvbnM60wAOALUAtgC3AtoC24Ao +gMeAytgADgC8AL0AvgC/AMAAwQDCAMQC3gEnAt8AyADJAMoBGYAmgMiAyYAggCSANWgAUwBhAHYAZQAg +AEEAcyAmUVNfEA9zYXZlRG9jdW1lbnRBczrSAA4APgLnAuiBAQ2vEEcA9QE+AusC7AHlAu4CLAGMAvEC +8gKHASsBagHGAvcBcwKVAfMBIgEAAUwCowL/AdMBqgLaAg8COgK/AwYBfgMIAwkCeAMLAwwBXQMOAw8B +GQMRAxIDEwMUAxUAuQKxAs0ATQERAEEDHAJqAh8DHwJIAGsB2wEIAbgCAQMmAMsBmwMpAVQDKwDsAy0C +WwMvgCuAQoDOgNGAdYDjgI2AW4DmgNWAq4A6gFCAbIDvgFGAr4B6gDmAL4BHgLSA8IBwgGSAx4CEgJKA +voDrgFaA8oDdgKaA9ID1gEyA0oDZgDWA4YEBCYEBDIDWgPuAHIC5gMOAC4A0gAeA6YCigImBAQSAl4AO +gHGAMIBogH+A2oAdgGCA/4BIgQEIgCqA4ICdgOpWV2luZG930gAOAD4AaQMzgBKkAeUBuALrAOyAdYBo +gM6AKtoADgC8AL0DOQC+AzoAvwDAAMEAwgDEAFAA8QB1AFAAdQDIAMkAygD1XU5TSXNTZXBhcmF0b3Jc +TlNJc0Rpc2FibGVkgCaAFwmAFwmAIIAkgCteX05TV2luZG93c01lbnXSADcAOANFAMKiAMIAO9oADgNH +ALwAvQC+AL8AwADBAMIDSADEAXMAUQDxAFAAyADJAMoDDgNQWU5TU3VibWVudVhOU0FjdGlvboAmgFGA +CYAXgCCAJIDSgNPUAA4AvAD3AM0AzgNTA1QDVYDQgPmBAQuA+l5zdWJtZW51QWN0aW9uOtIADgA+AGkD +WYASqwJqAvIDFAMPAyYDLQJIAWoCzQMRAqOAooDVgNaA2YDagOCAl4BQgMOA4YC02gAOALwAvQM5AL4D +OgC/AMAAwQDCAMQAUADxAHUAUAB1AMgAyQDKAXOAJoAXCYAXCYAggCSAUdgADgC8AL0AvgC/AMAAwQDC +AMQDcADxA3EAyADJAMoBc4AmgNeA2IAggCSAUWwAUAByAGUAZgBlAHIAZQBuAGMAZQBzICZRLNoADgC8 +AL0DOQC+AzoAvwDAAMEAwgDEAFAA8QB1AFAAdQDIAMkAygFzgCaAFwmAFwmAIIAkgFHaAA4DRwC8AL0A +vgC/AMAAwQDCA0gAxAMJA4MA8QBQAMgAyQDKAXMDiIAmgN2A24AXgCCAJIBRgNxYU2VydmljZXPUAA4A +vAD3AM0AzgODA40DjoDQgNuA34De0gAOAD4AaQORgBKgXxAPX05TU2VydmljZXNNZW512gAOALwAvQM5 +AL4DOgC/AMAAwQDCAMQAUADxAHUAUAB1AMgAyQDKAXOAJoAXCYAXCYAggCSAUdoADgC8AL0DOQC+AzoA +vwDAAMEAwgDEAFAA8QB1AFAAdQDIAMkAygFzgCaAFwmAFwmAIIAkgFFcX05TQXBwbGVNZW510wAOALwA +zQDOA6gDqYDQgOSA5VRIZWxw0gAOAD4AaQOtgBKg2gAOALwAvQM5AL4DOgC/AMAAwQDCAMQAUADxAHUA +UAB1AMgAyQDKARmAJoAXCYAXCYAggCSANVRFZGl00gAOAD4AaQO6gBKqApUBIgMcArECOgIBAV0CLAMv +AwaAr4A5gOmAuYCSgH+ATICNgOqA69oADgC8AL0DOQC+AzoAvwDAAMEAwgDEAFAA8QB1AFAAdQDIAMkA +ygErgCaAFwmAFwmAIIAkgDraAA4AvAC9AzkAvgM6AL8AwADBAMIAxABQAPEAdQBQAHUAyADJAMoBK4Am +gBcJgBcJgCCAJIA62gAOA0cAvAC9AL4AvwDAAMEAwgNIAMQBCAELAPEAUADIAMkAygErA9+AJoAwgOyA +F4AggCSAOoDtVlNwZWVjaNIADgA+AGkD44ASogEAAh+AL4CJ2gAOALwAvQM5AL4DOgC/AMAAwQDCAMQA +UADxAHUAUAB1AMgAyQDKAMuAJoAXCYAXCYAggCSAHdoADgNHALwAvQC+AL8AwADBAMIDSADEAu4DqADx +AFAAyADJAMoDDgP3gCaA44DkgBeAIIAkgNKA8doADgNHALwAvQC+AL8AwADBAMIDSADEASsBLgDxAFAA +yADJAMoDDgQAgCaAOoDngBeAIIAkgNKA89oADgC8AL0DOQC+AzoAvwDAAMEAwgDEAFAA8QB1AFAAdQDI +AMkAygEZgCaAFwmAFwmAIIAkgDXaAA4DRwC8AL0AvgC/AMAAwQDCA0gAxADLAM8A8QBQAMgAyQDKAw4E +EoAmgB2A9oAXgCCAJIDSgPdUR2FtZdIADgA+AGkEFoASpgHGAvcChwGbALkBqoBsgO+Aq4BggByAZFlB +TWFpbk1lbnXSAA4APgBpBCCAEqcC7AMVAwgDDAMfAxIC/4DRgPuA8oD1gQEEgQEJgPDaAA4DRwC8AL0A +vgC/AMAAwQDCA0gAxAEZARwA8QBQAMgAyQDKAw4EMIAmgDWA/IAXgCCAJIDSgP1URmlsZdIADgA+AGkE +NIASqwE+AYwDKQLxAlsB8wLaAREDCwIPAX6AQoBbgP+A5oCdgHqAx4A0gPSAhIBW2gAOA0cAvAC9AL4A +vwDAAMEAwgNIAMQB2wHeAPEAUADIAMkAygEZBEiAJoBxgQEAgBeAIIAkgDWBAQFbT3BlbiBSZWNlbnTS +AA4APgBpBEyAEqEB04BwXxAWX05TUmVjZW50RG9jdW1lbnRzTWVuddoADgNHALwAvQC+AL8AwADBAMID +SADEAVQBVwDxAFAAyADJAMoDDgRXgCaASIEBBYAXgCCAJIDSgQEGVFZpZXfSAA4APgBpBFuAEqQCeAMr +Ar8BTICmgQEIgL6AR9oADgC8AL0DOQC+AzoAvwDAAMEAwgDEAFAA8QB1AFAAdQDIAMkAygFUgCaAFwmA +FwmAIIAkgEjaAA4DRwC8AL0AvgC/AMAAwQDCA0gAxAD1APkA8QBQAMgAyQDKAw4EcYAmgCuAzIAXgCCA +JIDSgQEKW19OU01haW5NZW510gAOADIAMwA0gASAA9IANwA4BHcAgKIAgAA70gAOAD4C5wR6gQENrxBH +AxIBGQD1Aw4A9QL/ASsBGQEZAXMAywMIAXMAywDLAuwBKwEZASsBCAFUAXMDDgHbAMsBGQEZASsBVAEr +ARkDDgMmAVQBGQMOASsAHwFzAxUBcwMOAB8BcwMOAMsBKwFzAEEBGQAfASsBcwEIAw4BcwBNAykDBgD1 +ASsBcwMMAMsBGQMfAVQA9QFzARkBK4EBCYA1gCuA0oArgPCAOoA1gDWAUYAdgPKAUYAdgB2A0YA6gDWA +OoAwgEiAUYDSgHGAHYA1gDWAOoBIgDqANYDSgNqASIA1gNKAOoACgFGA+4BRgNKAAoBRgNKAHYA6gFGA +B4A1gAKAOoBRgDCA0oBRgAuA/4DrgCuAOoBRgPWAHYA1gQEEgEiAK4BRgDWAOtIADgA+AucExIEBDa8Q +SAD1AT4C7ALrAeUC7gIsAYwC8QLyAocBKwFqAcYC9wFzApUB8wEiAaoBTAL/AqMBAAHTAtoCDwI6Ar8D +BgF+AwgDCQJ4AB8DCwMMAV0DDgEZAw8DEQMSAxMDFQMUALkCsQLNAE0BEQBBAGsDHwJqAxwCSAIfAdsB +CAG4AMsDJgIBAZsDKQFUAysDLQDsAlsDL4ArgEKA0YDOgHWA44CNgFuA5oDVgKuAOoBQgGyA74BRgK+A +eoA5gGSAR4DwgLSAL4BwgMeAhICSgL6A64BWgPKA3YCmgAKA9ID1gEyA0oA1gNmA4YEBCYEBDID7gNaA +HIC5gMOAC4A0gAeADoEBBICigOmAl4CJgHGAMIBogB2A2oB/gGCA/4BIgQEIgOCAKoCdgOrSAA4APgLn +BQ+BAQ2vEEgFEAURBRIFEwUUAowFFgUXBRgFGQUaBRsFHAUdBR4FHwUgAaAFIgUjBSQBywUmBScFKAUp +Aa8FKwUsBS0FLgUvBTAFMQUyBTMFNAU1BTYFNwU4BTkFOgU7BTwFPQU+BT8FQAVBBUIFQwVEBUUFRgVH +BUgFSQVKBUsFTAVNBU4FTwVQBVEFUgVTBVQFVQVWBVeBARGBARKBAROBARSBARWArYEBFoEBF4EBGIEB +GYEBGoEBG4EBHIEBHYEBHoEBH4EBIIBigQEhgQEigQEjgG6BASSBASWBASaBASeAZoEBKIEBKYEBKoEB +K4EBLIEBLYEBLoEBL4EBMIEBMYEBMoEBM4EBNIEBNYEBNoEBN4EBOIEBOYEBOoEBO4EBPIEBPYEBPoEB +P4EBQIEBQYEBQoEBQ4EBRIEBRYEBRoEBR4EBSIEBSYEBSoEBS4EBTIEBTYEBToEBT4EBUIEBUYEBUoEB +U4EBVF1NZW51IChXaW5kb3cpUTlfEBlNZW51IEl0ZW0gKEdlZWtHYW1lQm9hcmQpW1NlcGFyYXRvci03 +XxAUTWVudSBJdGVtIChNaW5pbWl6ZSlfEBZNZW51IEl0ZW0gKFNlbGVjdCBBbGwpbxARAE0AZQBuAHUA +IABJAHQAZQBtACAAKABPAHAAZQBuICYAKVE3WVNlcGFyYXRvcl8QFE1lbnUgSXRlbSAoQ2hlY2tlcnMp +W01lbnUgKEVkaXQpXxAXTWVudSBJdGVtIChIaWRlIE90aGVycylfECBNZW51IEl0ZW0gKEtsb25kaWtl +IChTb2xpdGFpcmUpKVtTZXBhcmF0b3ItNl8QFE1lbnUgKEdlZWtHYW1lQm9hcmQpXxAQTWVudSBJdGVt +IChVbmRvKV8QEE1lbnUgSXRlbSAoUmVkbylfEBdNZW51IEl0ZW0gKFRpYy1UYWMtVG9lKW8QHgBNAGUA +bgB1ACAASQB0AGUAbQAgACgAQwB1AHMAdABvAG0AaQB6AGUAIABUAG8AbwBsAGIAYQByICYAKVQxMTEx +XxAaTWVudSBJdGVtIChTdGFydCBTcGVha2luZylfEBZNZW51IEl0ZW0gKENsZWFyIE1lbnUpUThfEBBN +ZW51IEl0ZW0gKENvcHkpXxAYTWVudSBJdGVtIChTaG93IFRvb2xiYXIpXxASTWVudSBJdGVtIChTcGVl +Y2gpUTZfEBBNZW51IEl0ZW0gKEVkaXQpXxAPTWVudSAoU2VydmljZXMpXxAXTWVudSBJdGVtIChGdWxs +IFNjcmVlbilcRmlsZSdzIE93bmVyUzItMV8QEE1lbnUgSXRlbSAoR2FtZSlfEBJNZW51IEl0ZW0gKERl +bGV0ZSlYTWFpbk1lbnVbTWVudSAoRmlsZSlbU2VwYXJhdG9yLTFbU2VwYXJhdG9yLTJfEBJNZW51IEl0 +ZW0gKFdpbmRvdylbQXBwbGljYXRpb25fEBBNZW51IEl0ZW0gKEZpbGUpUzEyMV8QFk1lbnUgSXRlbSAo +SGV4Y2hlcXVlcilfEA9NZW51IEl0ZW0gKEN1dClfEBRNZW51IEl0ZW0gKFNob3cgQWxsKVxDb250ZW50 +IFZpZXdSMTBfEBZXaW5kb3cgKEdlZWtHYW1lQm9hcmQpXxAPRGVtbyBCb2FyZCBWaWV3XxAQTWVudSBJ +dGVtIChWaWV3KV8QH01lbnUgSXRlbSAoQWJvdXQgR2Vla0dhbWVCb2FyZClbU2VwYXJhdG9yLTRfEB5N +ZW51IEl0ZW0gKEhpZGUgR2Vla0dhbWVCb2FyZClfEBlNZW51IEl0ZW0gKFN0b3AgU3BlYWtpbmcpXxAS +TWVudSAoT3BlbiBSZWNlbnQpXU1lbnUgKFNwZWVjaClfEBBNZW51IEl0ZW0gKFpvb20pW01lbnUgKEdh +bWUpXxAUTWVudSBJdGVtIChTZXJ2aWNlcylfEBFNZW51IEl0ZW0gKFBhc3RlKV5NZW51IEl0ZW0gKEdv +KV8QF01lbnUgSXRlbSAoT3BlbiBSZWNlbnQpW01lbnUgKFZpZXcpW1NlcGFyYXRvci04W1NlcGFyYXRv +ci0zXxAeTWVudSBJdGVtIChCcmluZyBBbGwgdG8gRnJvbnQpUzEtMVtTZXBhcmF0b3ItNdIADgA+AucF +noEBDaDSAA4APgLnBaGBAQ2g0gAOAD4C5wWkgQENrxBsAT4AlwLuAiwBjAHGAvcAsAKVAfMBAAChAUwC +owL/AKUC2gI6Ar8ApACsAX4AqAMJAJUArwMLAwwAsQFdAw4BGQCcAxIApgMTAJ0DFACqAJMAngCuAs0B +EQBBAxwDHwCaAkgArQIBAMsBmwMrAOwDLQMvAPUC6wLsAeUC8QLyAocBKwFqAJ8BcwEiAaoAogHTAg8D +BgCrAKkDCACQALIAowJ4AB8ApwCPAw8DEQMVALkAkgKxAJEATQCWAGsCHwJqAJkAoAHbAQgBuAMmAJgD +KQFUAJQAmwJbgEKAS4DjgI2AW4BsgO+AvYCvgHqAL4B5gEeAtIDwgIyAx4CSgL6AiICqgFaAmoDdgEGA +uID0gPWAwoBMgNKANYBjgQEJgJGBAQyAZ4DWgKGAOIBrgLOAw4A0gAeA6YEBBIBagJeAroB/gB2AYIEB +CIAqgOCA6oArgM6A0YB1gOaA1YCrgDqAUIBvgFGAOYBkgH6AcICEgOuApYCcgPKAKYDGgIOApoACgJaA +G4DZgOGA+4AcgDOAuYAugAuARoAOgImAooBVgHSAcYAwgGiA2oBPgP+ASIA+gF+AndIADgA+AucGE4EB +Da8QbAYUBhUGFgYXBhgGGQYaBhsGHAYdBh4GHwYgBiEGIgYjBiQGJQYmBicGKAYpBioGKwYsBi0GLgYv +BjAGMQYyBjMGNAY1BjYGNwY4BjkGOgY7BjwGPQY+Bj8GQAZBBkIGQwZEBkUGRgZHBkgGSQZKBksGTAZN +Bk4GTwZQBlEGUgZTBlQGVQZWBlcGWAZZBloGWwZcBl0GXgZfBmAGYQZiBmMGZAZlBmYGZwZoBmkGagZr +BmwGbQZuBm8GcAZxBnIGcwZ0BnUGdgZ3BngGeQZ6BnsGfAZ9Bn4Gf4EBWYEBWoEBW4EBXIEBXYEBXoEB +X4EBYIEBYYEBYoEBY4EBZIEBZYEBZoEBZ4EBaIEBaYEBaoEBa4EBbIEBbYEBboEBb4EBcIEBcYEBcoEB +c4EBdIEBdYEBdoEBd4EBeIEBeYEBeoEBe4EBfIEBfYEBfoEBf4EBgIEBgYEBgoEBg4EBhIEBhYEBhoEB +h4EBiIEBiYEBioEBi4EBjIEBjYEBjoEBj4EBkIEBkYEBkoEBk4EBlIEBlYEBloEBl4EBmIEBmYEBmoEB +m4EBnIEBnYEBnoEBn4EBoIEBoYEBooEBo4EBpIEBpYEBpoEBp4EBqIEBqYEBqoEBq4EBrIEBrYEBroEB +r4EBsIEBsYEBsoEBs4EBtIEBtYEBtoEBt4EBuIEBuYEBuoEBu4EBvIEBvYEBvoEBv4EBwIEBwYEBwoEB +w4EBxBBSEOsQahDGEEgRAX0RAYMRAW4QzxBLEMQRAWoRASoQiBBnEOgQUBDFEQEpEOMRAYUQThEBjxCC +EQF1EOQQShEBexEBchDKEB0QUREBhxATEOAT//////////0Q8BCBEI4Q5xEBhBEBcRCWEHARAXMQzhEB +JxEBdhCGEN8QyxEBfBEBiREBeRAFEJAQ1hAYEFwQOBAXEE8Q7BEBfhDNEJEQfxA5ENcRAYAQ4hB+EE0Q +0xEBehDBENkQJxEBaxBXEQF4EQGREQFvEQGGEI8QlRBTEQF/EQFsEMcQ6REBdBEBbREBdxDDEDoQVhAl +EH0Q1BDvEIMRAXAQfBEBKBEBkBEBihBJ0gAOAD4AaQbugBKg0gAOAD4C5wbxgQENoNIADgA+AucG9IEB +DaDSADcAOAb2BveiBvcAO15OU0lCT2JqZWN0RGF0YQAIABkAIgAnADEAOgA/AEQAUgBUAGYD/AQCBE0E +VARbBGkEewSXBKUEsQS9BMsE1gTkBQAFDgUhBTMFTQVXBWQFZgVpBWsFbgVxBXMFdgV4BXsFfgWBBYQF +hgWIBYsFjgWRBZQFnQWpBasFrQW7BcQFzQXYBd0F7AX1BggGEQYcBh4GIQYjBlAGXQZqBoAGjgaYBqYG +swbFBtkG5QbnBukG6wbtBu8G9Ab2BvgG+gb8Bv4HGQcnBzAHTQdfB2oHcwd/B4sHjQePB5EHlAeWB5gH +mgejB6UHqAeqB8sH4wflB+cH6gfsB+4H8AfyB/MH/wgNCBYIHwgsCDMIPwhICE8IXghmCG8IdgiOCI8I +mAidCLAIuQjACM0I0wjcCN4JKQkrCS0JLwkxCTMJNQk3CTkJOwk9CT8JQQlDCUUJRwlJCUsJTQlPCVEJ +UwlVCVcJWQlbCV0JXwlhCWMJZQlnCWkJawltCW8JcQmCCZAJmQmhCaMJpQmnCakJzgnWCeoJ9QoDCg0K +GgohCicKKQorCjAKMgo3CjkKOwo9CkoKVgpYCloKXApnCmkKdgqFCocKiQqLCpMKpQquCrMKxgrTCtUK +1wrZCuwK9Qr6CwULGgsjCyoLQgtRC14LYAtiC2QLhQuHC4kLjguQC5ILlAuWC6cLrguwC7ILtAu2C8sL +3QvqC+wL7gvwDBEMEwwVDBcMGQwbDB0MKgwsDC4MMAw/DE4MWwxdDF8MYQx+DIAMggyEDIYMiAyKDJcM +mQybDJ0MrwzIDNUM1wzZDNsM/Az+DQANBQ0HDQkNCw0NDRoNHA0eDSANJQ0nDS0NPg1ADUINRA1GDU8N +WA1fDXYNgw2FDYcNiQ2qDawNrg2wDbINtA22DboNvA3JDdYN2A3aDdwN/Q3/DgEOAw4FDgcOCQ4WDhgO +Gw4eDkUOZw50DnYOeA56DpsOnQ6fDqEOow6lDqcOrg62DsMOxQ7HDskO6g7sDu4O8w71DvcO+Q77DwwP +Dg8QDxIPFA8gDyIPOw9ID0oPTA9OD28PcQ9zD3UPdw95D3sPiA+KD5EPng+gD6IPpA/FD8cPyQ/LD80P +zw/RD9wP3g/sD/0P/xABEAMQBRAqECwQLhAwEDIQNBA2EDgQOxA9EE4QUBBSEFQQVhB7EH0QfxCBEIMQ +hRCHEIkQlRCXEKQQphCoEKoQyxDNEM8Q0RDTENUQ1xDcEOkQ+hD8EP4RABECESMRJREnESkRKxEtES8R +RhFIEVURVxFZEVsRfBF+EYARghGEEYYRiBGZEZsRnhGhEaQRrxHHEdQR1hHYEdoR+xH9Ef8SARIDEgUS +BxIQEhISKBI1EjcSORI7ElwSXhJgEmISZBJmEmgSbRJvEn0SihKMEo4SkBKxErMStRK3ErkSuxK9EsMS +xRLMEtkS2xLdEt8TBBMOExATEhMUExYTGBMaExwTKhMsEzsTSBNKE0wTThNvE3ETcxN1E3cTeRN7E4kT +lxOkE6YTqBOqE8sTzRPPE9ET0xPVE9cT4hPkE+8T/BP+FAAUAhQjFCUUJxQpFCsULRQvFDQUNhQ8FEkU +SxRNFE8UcBRyFHQUdhR4FHoUfBSRFJcUqBSqFKwUrhSwFMgU1RTXFNkU2xT8FP4VABUCFQQVBhUIFQ4V +EBUeFS8VMRUzFTUVNxVUFVYVWBVaFVwVXhVgFXYVlhWnFakVqxWtFa8V0BXSFdQV1hXYFdoV3BXoFeoV +/RYOFhAWEhYUFhYWOxY9Fj8WQRZDFkUWRxZJFlIWVBZhFmMWZRZnFogWihaMFo4WkBaSFpQWmRabFqEW +rhawFrIWtBbVFtcW2RbbFt0W3xbhFvYW+BcDFxAXEhcUFxYXNxc5FzsXPRc/F0EXQxdHF0kXThdbF10X +XxdhF4IXhBeGF4gXiheMF44XmxedF7MXwBfCF8QXxhfnF+kX6xftF+8X8RfzF/wYFRgiGCQYJhgoGEkY +SxhNGE8YURhTGFUYZhhoGHoYgxiGGRcZGRkbGR0ZHxkhGSMZJRknGSkZKxktGS8ZMRkzGTUZNxk5GTsZ +PRk/GUEZQxlFGUcZSRlLGU0ZTxlRGVMZVRlXGVkZWxldGV8ZYRljGWUZZxlpGWwZbxlxGXMZdRl3GXkZ +exl9GX8ZgRmDGYUZiBmKGYwZjhmQGZIZlBmWGZgZmhmcGZ4ZoRmjGaUZpxmpGbAZuRm7GcQZxhnIGcoZ +zBn1GgMaEBoSGhQaFRoXGhgaGhocGh4aLRo2GjsaZBpuGncaeRp7Gn0afxqBGoMahRqHGpgamhqcGp8a +oRqwGrkauxrSGtQa1hrYGtoa3BreGuAa4hrkGuYa6BsRGxMbFRsWGxgbGRsbGx0bHxtAG0IbRBtGG0gb +ShtMG2UbZxuQG5IblBuVG5cbmBuaG5wbnhvHG8kbyxvNG88b0RvTG9Ub1xvgG/Eb8xv1G/cb+RwCHAQc +BRwXHEAcQhxEHEUcRxxIHEocTBxOHHcceRx7HHwcfhx/HIEcgxyFHJIcnxyhHKMcpRyqHLMctRy2HN8c +4RzjHOQc5hznHOkc6xztHPIc+xz9HRIdFB0WHRgdGh0cHR4dIB0iHSQdJh1PHVEdUx1UHVYdVx1ZHVsd +XR2GHYgdih2LHY0djh2QHZIdlB29Hb8dwR3DHcUdxx3JHcsdzR3UHd0d3x3kHeYd6B4RHhMeFR4WHhge +GR4bHh0eHx5IHkoeTB5OHlAeUh5UHlYeWB6BHoMehR6HHokeix6NHo8ekR66Hrwevh6/HsEewh7EHsYe +yB7xHvMe9R73Hvke+x79Hv8fAR8GHw8fER8eHyAfIh8kHyYfKB8qHzQfPR8/H04fUB9SH1QfVh9ZH1wf +Xh+HH4kfix+NH48fkR+TH5Uflx+cH6Ufpx++H8Afwh/EH8YfyB/KH8wfzh/QH9If1B/9H/8gASAEIAYg +CCAKIAwgDyAbICQgJiApICsgRCBtIG8gcSB0IHYgeCB6IHwgfyCEII0gjyCYIJognSCfIKEgyiDMIM4g +zyDRINIg1CDWINghASEDIQUhByEJIQshDSEPIRIhHiEnISkhKyE0ITkhQiFFIdYh2SHbId0h3yHhIeMh +5SHnIekh6yHtIe8h8SHzIfUh9yH5Ifsh/SH/IgEiAyIFIgciCSILIg0iDyIRIhMiFSIXIhkiGyIdIh8i +ISIjIiUiJyIpIisiLSIvIjEiMyI1IjciOSI7Ij0iPyJBIkMiRSJHIkkiSyJNIk8iUSJTIlUiVyJZIlwi +XiJgImIiZCJmIm8iciMFIwcjCSMLIw0jDyMRIxMjFSMXIxkjGyMdIx8jISMjIyUjJyMpIysjLSMvIzEj +MyM1IzcjOSM7Iz0jPyNBI0MjRSNHI0kjSyNNI08jUSNTI1UjVyNZI1wjXyNhI2MjZSNnI2kjayNtI28j +cSN0I3YjeCN6I3wjfiOAI4IjhCOGI4gjiiOMI44jkSOTI5UjlyOZI6IjpSQ4JDskPiRBJEQkRyRJJEwk +TyRSJFUkWCRbJF4kYSRkJGckaiRsJG8kciR1JHckeiR9JIAkgySFJIgkiySOJJEklCSXJJoknSSgJKMk +piSpJKwkrySyJLUkuCS7JL4kwSTEJMckyiTNJNAk0yTWJNkk3CTfJOIk5SToJOsk7iTxJPQk9yT6JP0l +ACUDJQYlCSUMJRolHCU4JUQlWyV0JZklmyWlJbwlyCXiJgUmESYoJjsmTiZoJqcmrCbJJuIm5Cb3JxIn +JycpJzwnTidoJ3UneSeMJ6Enqie2J8InzifjJ+8oAigGKB8oMShIKFUoWChxKIMolii4KMQo5SkBKRYp +JCk3KUMpWiluKX0plymjKa8puyncKeAp7Cn1Kfgp+SoCKgUqBioPKhIq7SrvKvEq8yr1Kvcq+Sr7Kv0q +/ysBKwMrBSsHKwkrCysNKw8rESsTKxUrFysZKxsrHSsfKyErIyslKycrKSsrKy0rLysyKzQrNys5Kzsr +PSs/K0ErQytFK0crSStLK04rUCtSK1QrVitYK1orXStfK2ErYytlK2craStrK20rbytxK3MrdSt3K3kr +eyt9K38rgSuDK4UrhyuJK4srjSuPK5ErkyuVK5crmSubK50rnyuhK6MrpSunK6krqyutK68rsSuzK7Ur +tyu5K7srvSu/K8ErwyvFK8crySvSK9UssCyzLLYsuSy8LL8swizFLMgsyyzOLNEs1CzXLNos3SzgLOMs +5izpLOws7yzyLPUs+Cz7LP4tAS0ELQctCi0NLRAtEy0WLRktHC0fLSItJS0oLSstLi0xLTQtNy06LT0t +QC1DLUYtSS1MLU8tUi1VLVgtWy1eLWEtZC1nLWotbS1wLXMtdi15LXwtfy2CLYUtiC2LLY4tkS2ULZct +mi2dLaAtoy2mLaktrC2vLbIttS24Lbstvi3BLcQtxy3KLc0t0C3TLdYt2S3cLd8t4i3lLegt6y3uLfEt +9C32Lfgt+i38Lf4uAS4ELgcuCS4LLg0uEC4TLhUuFy4ZLhsuHS4gLiIuJS4nLiouLC4vLjEuMy42Ljku +Oy49Lj8uQi5ELkYuTy5RLlMuVS5XLlouXS5fLmEuZC5mLmkubC5uLnAuci51Lnguey59Ln8ugS6DLoUu +hy6JLosujS6QLpIulC6WLpgumi6dLp8uoS6jLqUuqC6qLqwuri6xLrMuti65Lrwuvy7BLsMuxS7ILssu +zS7PLtIu1S7YLtou3C7eLuAu4i7kLuYu6C7rLu0u8C7zLvYu+C8BLwMvBC8NLxAvES8aLx0vHi8nLywA +AAAAAAACAgAAAAAAAAb4AAAAAAAAAAAAAAAAAAAvOw + + + diff -r 000000000000 -r e9f7ba4718e1 English.lproj/MainMenu.nib/keyedobjects.nib Binary file English.lproj/MainMenu.nib/keyedobjects.nib has changed diff -r 000000000000 -r e9f7ba4718e1 GeekGameBoard.xcodeproj/project.pbxproj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeekGameBoard.xcodeproj/project.pbxproj Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,460 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXBuildFile section */ + 27224FA00CC29B1A00814095 /* Grid.m in Sources */ = {isa = PBXBuildFile; fileRef = 27224F9F0CC29B1A00814095 /* Grid.m */; }; + 2722526F0CC2E86600814095 /* QuartzUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2722526E0CC2E86600814095 /* QuartzUtils.m */; }; + 2727556A0CC3234F009C4C6C /* Dispenser.m in Sources */ = {isa = PBXBuildFile; fileRef = 272755690CC3234F009C4C6C /* Dispenser.m */; }; + 27275A550CC6713E009C4C6C /* HexGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 27275A540CC6713E009C4C6C /* HexGrid.m */; }; + 27275C4B0CC700F2009C4C6C /* Game.m in Sources */ = {isa = PBXBuildFile; fileRef = 27275C4A0CC700F2009C4C6C /* Game.m */; }; + 27275C920CC7C578009C4C6C /* TicTacToeGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27275C910CC7C578009C4C6C /* TicTacToeGame.m */; }; + 272C90AB0CCACBA4009EE530 /* DiscPiece.m in Sources */ = {isa = PBXBuildFile; fileRef = 272C90AA0CCACBA4009EE530 /* DiscPiece.m */; }; + 2731E0280CD430B800E6E4C8 /* Deck.m in Sources */ = {isa = PBXBuildFile; fileRef = 2731E0270CD430B800E6E4C8 /* Deck.m */; }; + 2731E2A80CD5630600E6E4C8 /* BitHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 2731E2A70CD5630600E6E4C8 /* BitHolder.m */; }; + 2734B2620CC7F25A0070C008 /* HexchequerGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 2734B2610CC7F25A0070C008 /* HexchequerGame.m */; }; + 2734B4F00CCA5BDB0070C008 /* CheckersGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 2734B4EF0CCA5BDB0070C008 /* CheckersGame.m */; }; + 274124060CFCCF9D00842A9B /* DemoBoardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 274124050CFCCF9D00842A9B /* DemoBoardView.m */; }; + 276DFC410D00867000D329AE /* Green Ball.png in Resources */ = {isa = PBXBuildFile; fileRef = 276DFC400D00867000D329AE /* Green Ball.png */; }; + 276DFC560D00890C00D329AE /* Red Ball.png in Resources */ = {isa = PBXBuildFile; fileRef = 276DFC530D00890C00D329AE /* Red Ball.png */; }; + 276DFC570D00890C00D329AE /* Yellow Ball.png in Resources */ = {isa = PBXBuildFile; fileRef = 276DFC540D00890C00D329AE /* Yellow Ball.png */; }; + 276DFC580D00890C00D329AE /* White Ball.png in Resources */ = {isa = PBXBuildFile; fileRef = 276DFC550D00890C00D329AE /* White Ball.png */; }; + 2795C2B80CC278C800D7B2BD /* Piece.m in Sources */ = {isa = PBXBuildFile; fileRef = 2795C2B70CC278C800D7B2BD /* Piece.m */; }; + 27B7543E0D08884F000516B9 /* Wood.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 27B7543D0D08884F000516B9 /* Wood.jpg */; }; + 27C999C30D81185E005AFD4F /* GGBUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C999C20D81185E005AFD4F /* GGBUtils.m */; }; + 27CCA8050CB8A3F9001CFE24 /* BoardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCA8040CB8A3F9001CFE24 /* BoardView.m */; }; + 27CCA95F0CB8A74D001CFE24 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27CCA95E0CB8A74D001CFE24 /* Quartz.framework */; }; + 27CCAABD0CB92A9F001CFE24 /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAABC0CB92A9F001CFE24 /* Card.m */; }; + 27CCABBF0CB9496B001CFE24 /* Bit.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCABBE0CB9496B001CFE24 /* Bit.m */; }; + 27CCAC750CB95C2B001CFE24 /* PlayingCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */; }; + 27D4F1260CCF011200923605 /* Stack.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D4F1250CCF011200923605 /* Stack.m */; }; + 27DFC4410CCD01B7005E34CE /* GoGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27DFC4400CCD01B7005E34CE /* GoGame.m */; }; + 27F230B90CD1A61B006939C1 /* KlondikeGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F230B80CD1A61B006939C1 /* KlondikeGame.m */; }; + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 27224F9E0CC29B1A00814095 /* Grid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Grid.h; sourceTree = ""; }; + 27224F9F0CC29B1A00814095 /* Grid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Grid.m; sourceTree = ""; }; + 2722526D0CC2E86600814095 /* QuartzUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuartzUtils.h; sourceTree = ""; }; + 2722526E0CC2E86600814095 /* QuartzUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QuartzUtils.m; sourceTree = ""; }; + 272755680CC3234F009C4C6C /* Dispenser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dispenser.h; sourceTree = ""; }; + 272755690CC3234F009C4C6C /* Dispenser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Dispenser.m; sourceTree = ""; }; + 27275A530CC6713E009C4C6C /* HexGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexGrid.h; sourceTree = ""; }; + 27275A540CC6713E009C4C6C /* HexGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HexGrid.m; sourceTree = ""; }; + 27275C490CC700F2009C4C6C /* Game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Game.h; sourceTree = ""; }; + 27275C4A0CC700F2009C4C6C /* Game.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Game.m; sourceTree = ""; }; + 27275C900CC7C578009C4C6C /* TicTacToeGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TicTacToeGame.h; sourceTree = ""; }; + 27275C910CC7C578009C4C6C /* TicTacToeGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TicTacToeGame.m; sourceTree = ""; }; + 272C90A90CCACBA4009EE530 /* DiscPiece.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiscPiece.h; sourceTree = ""; }; + 272C90AA0CCACBA4009EE530 /* DiscPiece.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiscPiece.m; sourceTree = ""; }; + 2731E0260CD430B800E6E4C8 /* Deck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Deck.h; sourceTree = ""; }; + 2731E0270CD430B800E6E4C8 /* Deck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Deck.m; sourceTree = ""; }; + 2731E2A60CD5630600E6E4C8 /* BitHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitHolder.h; sourceTree = ""; }; + 2731E2A70CD5630600E6E4C8 /* BitHolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BitHolder.m; sourceTree = ""; }; + 2734B2600CC7F25A0070C008 /* HexchequerGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexchequerGame.h; sourceTree = ""; }; + 2734B2610CC7F25A0070C008 /* HexchequerGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HexchequerGame.m; sourceTree = ""; }; + 2734B4EE0CCA5BDB0070C008 /* CheckersGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CheckersGame.h; sourceTree = ""; }; + 2734B4EF0CCA5BDB0070C008 /* CheckersGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CheckersGame.m; sourceTree = ""; }; + 274124040CFCCF9D00842A9B /* DemoBoardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoBoardView.h; sourceTree = ""; }; + 274124050CFCCF9D00842A9B /* DemoBoardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoBoardView.m; sourceTree = ""; }; + 276DFC400D00867000D329AE /* Green Ball.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Green Ball.png"; path = "Resources/Green Ball.png"; sourceTree = ""; }; + 276DFC530D00890C00D329AE /* Red Ball.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Red Ball.png"; path = "Resources/Red Ball.png"; sourceTree = ""; }; + 276DFC540D00890C00D329AE /* Yellow Ball.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Yellow Ball.png"; path = "Resources/Yellow Ball.png"; sourceTree = ""; }; + 276DFC550D00890C00D329AE /* White Ball.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "White Ball.png"; path = "Resources/White Ball.png"; sourceTree = ""; }; + 2795C2B60CC278C800D7B2BD /* Piece.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Piece.h; sourceTree = ""; }; + 2795C2B70CC278C800D7B2BD /* Piece.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Piece.m; sourceTree = ""; }; + 27B7543D0D08884F000516B9 /* Wood.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = Wood.jpg; path = Resources/Wood.jpg; sourceTree = ""; }; + 27C999C10D81185E005AFD4F /* GGBUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GGBUtils.h; sourceTree = ""; }; + 27C999C20D81185E005AFD4F /* GGBUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GGBUtils.m; sourceTree = ""; }; + 27CCA8030CB8A3F9001CFE24 /* BoardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BoardView.h; sourceTree = ""; }; + 27CCA8040CB8A3F9001CFE24 /* BoardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BoardView.m; sourceTree = ""; }; + 27CCA95E0CB8A74D001CFE24 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = /System/Library/Frameworks/Quartz.framework; sourceTree = ""; }; + 27CCAABB0CB92A9F001CFE24 /* Card.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Card.h; sourceTree = ""; }; + 27CCAABC0CB92A9F001CFE24 /* Card.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Card.m; sourceTree = ""; }; + 27CCABBD0CB9496B001CFE24 /* Bit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bit.h; sourceTree = ""; }; + 27CCABBE0CB9496B001CFE24 /* Bit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Bit.m; sourceTree = ""; }; + 27CCAC730CB95C2B001CFE24 /* PlayingCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayingCard.h; sourceTree = ""; }; + 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayingCard.m; sourceTree = ""; }; + 27D4F1240CCF011200923605 /* Stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Stack.h; sourceTree = ""; }; + 27D4F1250CCF011200923605 /* Stack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Stack.m; sourceTree = ""; }; + 27DFC43F0CCD01B7005E34CE /* GoGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GoGame.h; sourceTree = ""; }; + 27DFC4400CCD01B7005E34CE /* GoGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoGame.m; sourceTree = ""; }; + 27F230B70CD1A61B006939C1 /* KlondikeGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KlondikeGame.h; sourceTree = ""; }; + 27F230B80CD1A61B006939C1 /* KlondikeGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KlondikeGame.m; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* GeekGameBoard_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeekGameBoard_Prefix.pch; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Resources/Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* GeekGameBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeekGameBoard.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 27CCA95F0CB8A74D001CFE24 /* Quartz.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Source */ = { + isa = PBXGroup; + children = ( + 27CCA8030CB8A3F9001CFE24 /* BoardView.h */, + 27CCA8040CB8A3F9001CFE24 /* BoardView.m */, + 27CCABBD0CB9496B001CFE24 /* Bit.h */, + 27CCABBE0CB9496B001CFE24 /* Bit.m */, + 2731E2A60CD5630600E6E4C8 /* BitHolder.h */, + 2731E2A70CD5630600E6E4C8 /* BitHolder.m */, + 27D4F1240CCF011200923605 /* Stack.h */, + 27D4F1250CCF011200923605 /* Stack.m */, + 27275C440CC70095009C4C6C /* Cards */, + 27275C450CC700C4009C4C6C /* Boards and Pieces */, + 27275C480CC700D8009C4C6C /* Games */, + 2722526D0CC2E86600814095 /* QuartzUtils.h */, + 2722526E0CC2E86600814095 /* QuartzUtils.m */, + 27C999C10D81185E005AFD4F /* GGBUtils.h */, + 27C999C20D81185E005AFD4F /* GGBUtils.m */, + ); + path = Source; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 27CCA95E0CB8A74D001CFE24 /* Quartz.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* GeekGameBoard.app */, + ); + name = Products; + sourceTree = ""; + }; + 27275C440CC70095009C4C6C /* Cards */ = { + isa = PBXGroup; + children = ( + 27CCAABB0CB92A9F001CFE24 /* Card.h */, + 27CCAABC0CB92A9F001CFE24 /* Card.m */, + 2731E0260CD430B800E6E4C8 /* Deck.h */, + 2731E0270CD430B800E6E4C8 /* Deck.m */, + 27CCAC730CB95C2B001CFE24 /* PlayingCard.h */, + 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */, + ); + name = Cards; + sourceTree = ""; + }; + 27275C450CC700C4009C4C6C /* Boards and Pieces */ = { + isa = PBXGroup; + children = ( + 2795C2B60CC278C800D7B2BD /* Piece.h */, + 2795C2B70CC278C800D7B2BD /* Piece.m */, + 272C90A90CCACBA4009EE530 /* DiscPiece.h */, + 272C90AA0CCACBA4009EE530 /* DiscPiece.m */, + 27224F9E0CC29B1A00814095 /* Grid.h */, + 27224F9F0CC29B1A00814095 /* Grid.m */, + 27275A530CC6713E009C4C6C /* HexGrid.h */, + 27275A540CC6713E009C4C6C /* HexGrid.m */, + 272755680CC3234F009C4C6C /* Dispenser.h */, + 272755690CC3234F009C4C6C /* Dispenser.m */, + ); + name = "Boards and Pieces"; + sourceTree = ""; + }; + 27275C480CC700D8009C4C6C /* Games */ = { + isa = PBXGroup; + children = ( + 27275C490CC700F2009C4C6C /* Game.h */, + 27275C4A0CC700F2009C4C6C /* Game.m */, + 27275C900CC7C578009C4C6C /* TicTacToeGame.h */, + 27275C910CC7C578009C4C6C /* TicTacToeGame.m */, + 2734B4EE0CCA5BDB0070C008 /* CheckersGame.h */, + 2734B4EF0CCA5BDB0070C008 /* CheckersGame.m */, + 2734B2600CC7F25A0070C008 /* HexchequerGame.h */, + 2734B2610CC7F25A0070C008 /* HexchequerGame.m */, + 27DFC43F0CCD01B7005E34CE /* GoGame.h */, + 27DFC4400CCD01B7005E34CE /* GoGame.m */, + 27F230B70CD1A61B006939C1 /* KlondikeGame.h */, + 27F230B80CD1A61B006939C1 /* KlondikeGame.m */, + ); + name = Games; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* BoardGame */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Source */, + 29B97315FDCFA39411CA2CEA /* Demo Application */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = BoardGame; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Demo Application */ = { + isa = PBXGroup; + children = ( + 274124040CFCCF9D00842A9B /* DemoBoardView.h */, + 274124050CFCCF9D00842A9B /* DemoBoardView.m */, + 32CA4F630368D1EE00C91783 /* GeekGameBoard_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Demo Application"; + path = Source; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, + 8D1107310486CEB800E47090 /* Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + 276DFC400D00867000D329AE /* Green Ball.png */, + 276DFC530D00890C00D329AE /* Red Ball.png */, + 276DFC540D00890C00D329AE /* Yellow Ball.png */, + 276DFC550D00890C00D329AE /* White Ball.png */, + 27B7543D0D08884F000516B9 /* Wood.jpg */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* BoardGame */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "BoardGame" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BoardGame; + productInstallPath = "$(HOME)/Applications"; + productName = BoardGame; + productReference = 8D1107320486CEB800E47090 /* GeekGameBoard.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "GeekGameBoard" */; + compatibilityVersion = "Xcode 3.0"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* BoardGame */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* BoardGame */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + 276DFC410D00867000D329AE /* Green Ball.png in Resources */, + 276DFC560D00890C00D329AE /* Red Ball.png in Resources */, + 276DFC570D00890C00D329AE /* Yellow Ball.png in Resources */, + 276DFC580D00890C00D329AE /* White Ball.png in Resources */, + 27B7543E0D08884F000516B9 /* Wood.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + 27CCA8050CB8A3F9001CFE24 /* BoardView.m in Sources */, + 27CCAABD0CB92A9F001CFE24 /* Card.m in Sources */, + 27CCABBF0CB9496B001CFE24 /* Bit.m in Sources */, + 27CCAC750CB95C2B001CFE24 /* PlayingCard.m in Sources */, + 2795C2B80CC278C800D7B2BD /* Piece.m in Sources */, + 27224FA00CC29B1A00814095 /* Grid.m in Sources */, + 2722526F0CC2E86600814095 /* QuartzUtils.m in Sources */, + 2727556A0CC3234F009C4C6C /* Dispenser.m in Sources */, + 27275A550CC6713E009C4C6C /* HexGrid.m in Sources */, + 27275C4B0CC700F2009C4C6C /* Game.m in Sources */, + 27275C920CC7C578009C4C6C /* TicTacToeGame.m in Sources */, + 2734B2620CC7F25A0070C008 /* HexchequerGame.m in Sources */, + 2734B4F00CCA5BDB0070C008 /* CheckersGame.m in Sources */, + 272C90AB0CCACBA4009EE530 /* DiscPiece.m in Sources */, + 27DFC4410CCD01B7005E34CE /* GoGame.m in Sources */, + 27D4F1260CCF011200923605 /* Stack.m in Sources */, + 27F230B90CD1A61B006939C1 /* KlondikeGame.m in Sources */, + 2731E0280CD430B800E6E4C8 /* Deck.m in Sources */, + 2731E2A80CD5630600E6E4C8 /* BitHolder.m in Sources */, + 274124060CFCCF9D00842A9B /* DemoBoardView.m in Sources */, + 27C999C30D81185E005AFD4F /* GGBUtils.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { + isa = PBXVariantGroup; + children = ( + 29B97319FDCFA39411CA2CEA /* English */, + ); + name = MainMenu.nib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Source/GeekGameBoard_Prefix.pch; + INFOPLIST_FILE = Resources/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = GeekGameBoard; + WRAPPER_EXTENSION = app; + ZERO_LINK = YES; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + i386, + ppc, + ppc64, + x86_64, + ); + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Source/GeekGameBoard_Prefix.pch; + INFOPLIST_FILE = Resources/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = GeekGameBoard; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_GC = unsupported; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_GC = unsupported; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "BoardGame" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "GeekGameBoard" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff -r 000000000000 -r e9f7ba4718e1 Read Me.rtf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Read Me.rtf Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,139 @@ +{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf270 +{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\margl1440\margr1440\vieww13900\viewh17120\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural + +\f0\b\fs38 \cf0 GeekGameBoard +\b0\fs22 \ +\ + +\b by Jens Alfke +\b0 \ +\ +GeekGameBoard is an example of using Core Animation to present the user interface of a board or card game. It implements a small framework for implementing such games, with domain-specific classes like "Grid" and "Piece", and examples of several game definitions built on top of the framework.\ +\ +Some of the generally-useful Core Animation techniques illustrated are:\ +\'95 Hit testing\ +\'95 Dragging CALayers with the mouse\ +\'95 Loading images from files and setting them as layer contents\ +\'95 3D "card-flip" animations\ +\ + +\b\fs28 History +\b0 \ + +\fs22 \ +I wrote GeekGameBoard in 2007 while at Apple, which published it as sample code. Apple's sample code license allows unrestricted use of the source code, requiring only that you keep their copyright notice in an +\i unmodified +\i0 distribution.\ +\ +I am no longer employed by Apple. To encourage further development of this framework, in March 2008 I created this open source project (under a BSD license) based on a slightly-evolved version of the published sample code.\ +\ +1.0: Original Apple sample code release (12/19/2007)\ +2.0: First open source release. (Major version number bumped to avoid conflict with any future Apple revisions.) No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game. (3/7/2008)\ +\ + +\b\fs28 Project Description\ + +\b0\fs22 \ + +\b Important Classes +\b0 \ +\ +A +\b Bit +\b0 is a moveable object in a game, either a +\b Card +\b0 or a +\b Piece +\b0 .\ +A +\b BitHolder +\b0 is a container for Bits; they can be dragged into and/or out of it. Typical BitHolders are +\b GridCell +\b0 s (the squares/hexes in a board), +\b Deck +\b0 s or +\b Stack +\b0 s of cards, and +\b Dispensers +\b0 (supplies of new pieces.)\ +A +\b Grid +\b0 is a regular arrangement of +\b GridCell +\b0 s; rectangular and hex grids are implemented.\ + +\b BoardView +\b0 is the NSView whose layer hosts all of the above. It also tracks mouse events so the user can drag Bits.\ + +\b Game +\b0 represents the rules of a particular game. It sets up the pieces/cards/grids that make up the game, decides which moves are legal, keeps track of whose turn it is, and decides when someone's won.\ + +\b Player +\b0 is a passive object that represents one of the players in a Game.\ +\ + +\b Class Hierarchy +\b0 \ +\ +(Underlined classes are from external frameworks; italic classes are abstract.)\ +\ +\ul CALayer\ulnone \ + +\i Bit +\i0 \ + Card\ + PlayingCard\ + DraggedStack\ + Piece\ + DiscPiece\ + +\i BitHolder* +\i0 \ + +\i GridCell +\i0 \ + Hex\ + Square\ + GoSquare\ + Deck\ + Dispenser\ + Stack\ + +\i Grid +\i0 \ + HexGrid\ + RectGrid\ +\ +\ul NSView\ulnone \ + BoardView\ +\ + +\i \ul NSObject +\i0 \ulnone \ + +\i Game +\i0 \ + TicTacToeGame (etc...)\ + Player\ +\ + +\fs20 * BitHolder is actually both a protocol, and a class that implements the protocol. I've combined them here, for simplicity.\ + +\fs22 \ +\ + +\b\fs28 Related Information\ + +\b0\fs22 \ +\'95 {\field{\*\fldinst{HYPERLINK "http://developer.apple.com/samplecode/GeekGameBoard/"}}{\fldrslt Apple's original GeekGameBoard sample code}}\ +\'95 {\field{\*\fldinst{HYPERLINK "http://developer.apple.com/documentation/Cocoa/Conceptual/CoreAnimation_guide/index.html"}}{\fldrslt +\i Core Animation Programming Guide}}\ + +\i \'95 {\field{\*\fldinst{HYPERLINK "http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_images/chapter_12_section_4.html"}}{\fldrslt Quartz 2D Programming Guide: Creating Images}}\ +\'95 {\field{\*\fldinst{HYPERLINK "http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_patterns/chapter_7_section_1.html"}}{\fldrslt Quartz 2D Programming Guide: Patterns}}\ +\'95 {\field{\*\fldinst{HYPERLINK "http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_paths/chapter_4_section_1.html"}}{\fldrslt Quartz 2D Programming Guide: Paths}} +\i0 \ +} \ No newline at end of file diff -r 000000000000 -r e9f7ba4718e1 Resources/Green Ball.png Binary file Resources/Green Ball.png has changed diff -r 000000000000 -r e9f7ba4718e1 Resources/Info.plist --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Info.plist Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.apple.sample.GeekGameBoard + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff -r 000000000000 -r e9f7ba4718e1 Resources/Red Ball.png Binary file Resources/Red Ball.png has changed diff -r 000000000000 -r e9f7ba4718e1 Resources/White Ball.png Binary file Resources/White Ball.png has changed diff -r 000000000000 -r e9f7ba4718e1 Resources/Wood.jpg Binary file Resources/Wood.jpg has changed diff -r 000000000000 -r e9f7ba4718e1 Resources/Yellow Ball.png Binary file Resources/Yellow Ball.png has changed diff -r 000000000000 -r e9f7ba4718e1 Source/Bit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Bit.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,69 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import + + +@class Game, Player; +@protocol BitHolder; + + +/** Standard Z positions */ +enum { + kBoardZ = 1, + kCardZ = 2, + kPieceZ = 3, + + kPickedUpZ = 100 +}; + + +/** A moveable item in a card/board game. + Abstract superclass of Card and Piece. */ +@interface Bit : CALayer +{ + @private + int _restingZ; // Original z position, saved while pickedUp + Player *_owner; // Player that owns this Bit +} + +/** Conveniences for getting/setting the layer's scale and rotation */ +@property CGFloat scale; +@property int rotation; // in degrees! Positive = clockwise + +/** "Picking up" a Bit makes it larger, translucent, and in front of everything else */ +@property BOOL pickedUp; + +/** Current holder (or nil) */ +@property (readonly) id holder; + +/** Ownership of this Bit */ +@property (assign) Player *owner; + +/** Conveniences for owner.friendly, owner.unfriendly */ +@property (readonly, getter=isFriendly) BOOL friendly; +@property (readonly, getter=isUnfriendly) BOOL unfriendly; + +/** Removes this Bit while running a explosion/fade-out animation */ +- (void) destroy; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Bit.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Bit.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,172 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Bit.h" +#import "Game.h" +#import "QuartzUtils.h" + + +@implementation Bit + + +- (id) copyWithZone: (NSZone*)zone +{ + // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to + // a temporary data block, then unarchiving a new layer from that block. + + // One complication is that, due to a bug in Core Animation, CALayer can't archive + // a pattern-based CGColor. So as a workaround, clear the background before archiving, + // then restore it afterwards. + + // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 ) + // As a workaround, clear the contents before archiving, then restore. + + CGColorRef bg = CGColorRetain(self.backgroundColor); + self.backgroundColor = NULL; + id contents = [self.contents retain]; + self.contents = nil; + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self]; + + self.backgroundColor = bg; + self.contents = contents; + + Bit *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data]; + clone.backgroundColor = bg; + clone.contents = contents; + CGColorRelease(bg); + [contents release]; + + clone->_owner = _owner; // _owner is not archived + return [clone retain]; +} + + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y]; +} + + +@synthesize owner=_owner; + +- (BOOL) isFriendly {return _owner.friendly;} +- (BOOL) isUnfriendly {return _owner.unfriendly;} + + +- (CGFloat) scale +{ + NSNumber *scale = [self valueForKeyPath: @"transform.scale"]; + return scale.floatValue; +} + +- (void) setScale: (CGFloat)scale +{ + [self setValue: [NSNumber numberWithFloat: scale] + forKeyPath: @"transform.scale"]; +} + + +- (int) rotation +{ + NSNumber *rot = [self valueForKeyPath: @"transform.rotation"]; + return round( rot.doubleValue * 180.0 / M_PI ); +} + +- (void) setRotation: (int)rotation +{ + [self setValue: [NSNumber numberWithDouble: rotation*M_PI/180.0] + forKeyPath: @"transform.rotation"]; +} + + +- (BOOL) pickedUp +{ + return self.zPosition >= kPickedUpZ; +} + +- (void) setPickedUp: (BOOL)up +{ + if( up != self.pickedUp ) { + CGFloat shadow, offset, radius, opacity, z, scale; + if( up ) { + shadow = 0.8; + offset = 2; + radius = 8; + opacity = 0.9; + scale = 1.2; + z = kPickedUpZ; + _restingZ = self.zPosition; + } else { + shadow = offset = radius = 0.0; + opacity = 1.0; + scale = 1.0/1.2; + z = _restingZ; + } + + self.zPosition = z; + self.shadowOpacity = shadow; + self.shadowOffset = CGSizeMake(offset,-offset); + self.shadowRadius = radius; + self.opacity = opacity; + self.scale *= scale; + } +} + + +- (BOOL)containsPoint:(CGPoint)p +{ + // Make picked-up pieces invisible to hit-testing. + // Otherwise, while dragging a Bit, hit-testing the cursor position would always return + // that Bit, since it's directly under the cursor... + if( self.pickedUp ) + return NO; + else + return [super containsPoint: p]; +} + + +-(id) holder +{ + // Look for my nearest ancestor that's a BitHolder: + for( CALayer *layer=self.superlayer; layer; layer=layer.superlayer ) { + if( [layer conformsToProtocol: @protocol(BitHolder)] ) + return (id)layer; + else if( [layer isKindOfClass: [Bit class]] ) + return nil; + } + return nil; +} + + +- (void) destroy +{ + // "Pop" the Bit by expanding it 5x as it fades away: + self.scale = 5; + self.opacity = 0.0; + // Removing the view from its superlayer right now would cancel the animations. + // Instead, defer the removal until sometime shortly after the animations finish: + [self performSelector: @selector(removeFromSuperlayer) withObject: nil afterDelay: 1.0]; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/BitHolder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/BitHolder.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,73 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import +@class Bit; + + +/** Protocol for a layer that acts as a container for Bits. */ +@protocol BitHolder + +/** Current Bit, or nil if empty */ +@property (retain) Bit* bit; + +/** Conveniences for comparing self.bit with nil */ +@property (readonly, getter=isEmpty) BOOL empty; + +/** BitHolders will be highlighted while the target of a drag operation */ +@property BOOL highlighted; + + +/** Tests whether the bit is allowed to be dragged out of me. + Returns the input bit, or possibly a different Bit to drag instead, or nil if not allowed. + Either -cancelDragBit: or -draggedBit:to: must be called next. */ +- (Bit*) canDragBit: (Bit*)bit; + +/** Cancels a pending drag (begun by -canDragBit:). */ +- (void) cancelDragBit: (Bit*)bit; + +/** Called after a drag finishes. */ +- (void) draggedBit: (Bit*)bit to: (id)dst; + + +/** Tests whether the bit is allowed to be dropped into me. + Either -willNotDropBit: or -dropBit:atPoint: must be called next. */ +- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point; + +/** Cancels a pending drop (after -canDropBit:atPoint: was already called.) */ +- (void) willNotDropBit: (Bit*)bit; + +/** Finishes a drop. */ +- (BOOL) dropBit: (Bit*)bit atPoint: (CGPoint)point; + +@end + + +/** A basic implementation of the BitHolder protocol. */ +@interface BitHolder : CALayer +{ + @protected + Bit *_bit; + BOOL _highlighted; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/BitHolder.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/BitHolder.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,80 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BitHolder.h" +#import "Bit.h" +#import "QuartzUtils.h" +#import "GGBUtils.h" + + +@implementation BitHolder + + +- (void) dealloc +{ + [_bit release]; + [super dealloc]; +} + + +- (Bit*) bit +{ + if( _bit && _bit.superlayer != self && !_bit.pickedUp ) + setObj(&_bit,nil); + return _bit; +} + +- (void) setBit: (Bit*)bit +{ + if( bit != self.bit ) { + if( bit && _bit ) + [_bit destroy]; + setObj(&_bit,bit); + ChangeSuperlayer(bit,self,-1); + } +} + +- (BOOL) isEmpty {return self.bit==nil;} + +@synthesize highlighted=_highlighted; + +- (Bit*) canDragBit: (Bit*)bit +{ + if( bit.superlayer == self && ! bit.unfriendly ) + return bit; + else + return nil; +} + +- (void) cancelDragBit: (Bit*)bit { } +- (void) draggedBit: (Bit*)bit to: (id)dst {self.bit = nil;} + +- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point {return YES;} +- (void) willNotDropBit: (Bit*)bit { } + +- (BOOL) dropBit: (Bit*)bit atPoint: (CGPoint)point +{ + self.bit = bit; + return YES; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/BoardView.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/BoardView.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,60 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import +@class Bit, Card, Grid, Game; +@protocol BitHolder; + + +/** NSView that hosts a game. */ +@interface BoardView : NSView +{ + @private + Game *_game; // Current Game + CALayer *_gameboard; // Game's main layer + + // Used during mouse-down tracking: + NSPoint _dragStartPos; // Starting position of mouseDown + Bit *_dragBit; // Bit being dragged + id _oldHolder; // Bit's original holder + CALayer *_oldSuperlayer; // Bit's original superlayer + int _oldLayerIndex; // Bit's original index in _oldSuperlayer.layers + CGPoint _oldPos; // Bit's original x/y position + CGPoint _dragOffset; // Offset of mouse position from _dragBit's origin + BOOL _dragMoved; // Has the mouse moved more than 3 pixels since mouseDown? + id _dropTarget; // Current BitHolder the cursor is over + + // Used while handling incoming drags: + CALayer *_viewDropTarget; // Current drop target during an incoming drag-n-drop + NSDragOperation _viewDropOp; // Current drag operation +} + +- (void) startGameNamed: (NSString*)gameClassName; + +- (IBAction) enterFullScreen: (id)sender; + +@property (readonly) Game *game; +@property (readonly) CALayer *gameboard; + +- (CGRect) gameBoardFrame; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/BoardView.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/BoardView.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,329 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BoardView.h" +#import "Bit.h" +#import "BitHolder.h" +#import "Game.h" +#import "QuartzUtils.h" +#import "GGBUtils.h" + + +@implementation BoardView + + +@synthesize game=_game, gameboard=_gameboard; + + +- (void) dealloc +{ + [_game release]; + [super dealloc]; +} + + +- (void) startGameNamed: (NSString*)gameClassName +{ + if( _gameboard ) { + [_gameboard removeFromSuperlayer]; + _gameboard = nil; + } + _gameboard = [[CALayer alloc] init]; + _gameboard.frame = [self gameBoardFrame]; + _gameboard.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; + [self.layer addSublayer: _gameboard]; + [_gameboard release]; + + Class gameClass = NSClassFromString(gameClassName); + setObj(&_game, [[gameClass alloc] initWithBoard: _gameboard]); +} + + +- (CGRect) gameBoardFrame +{ + return self.layer.bounds; +} + + +- (void)resetCursorRects +{ + [super resetCursorRects]; + [self addCursorRect: self.bounds cursor: [NSCursor openHandCursor]]; +} + + +- (IBAction) enterFullScreen: (id)sender +{ + if( self.isInFullScreenMode ) { + [self exitFullScreenModeWithOptions: nil]; + } else { + [self enterFullScreenMode: self.window.screen + withOptions: nil]; + } +} + + +#pragma mark - +#pragma mark KEY EVENTS: + + +- (void) keyDown: (NSEvent*)ev +{ + if( self.isInFullScreenMode ) { + if( [ev.charactersIgnoringModifiers hasPrefix: @"\033"] ) // Esc key + [self enterFullScreen: self]; + } +} + + +#pragma mark - +#pragma mark HIT-TESTING: + + +// Hit-testing callbacks (to identify which layers caller is interested in): +typedef BOOL (*LayerMatchCallback)(CALayer*); + +static BOOL layerIsBit( CALayer* layer ) {return [layer isKindOfClass: [Bit class]];} +static BOOL layerIsBitHolder( CALayer* layer ) {return [layer conformsToProtocol: @protocol(BitHolder)];} +static BOOL layerIsDropTarget( CALayer* layer ) {return [layer respondsToSelector: @selector(draggingEntered:)];} + + +/** Locates the layer at a given point in window coords. + If the leaf layer doesn't pass the layer-match callback, the nearest ancestor that does is returned. + If outOffset is provided, the point's position relative to the layer is stored into it. */ +- (CALayer*) hitTestPoint: (NSPoint)locationInWindow + forLayerMatching: (LayerMatchCallback)match + offset: (CGPoint*)outOffset +{ + CGPoint where = NSPointToCGPoint([self convertPoint: locationInWindow fromView: nil]); + where = [_gameboard convertPoint: where fromLayer: self.layer]; + CALayer *layer = [_gameboard hitTest: where]; + while( layer ) { + if( match(layer) ) { + CGPoint bitPos = [self.layer convertPoint: layer.position + fromLayer: layer.superlayer]; + if( outOffset ) + *outOffset = CGPointMake( bitPos.x-where.x, bitPos.y-where.y); + return layer; + } else + layer = layer.superlayer; + } + return nil; +} + + +#pragma mark - +#pragma mark MOUSE CLICKS & DRAGS: + + +- (void) mouseDown: (NSEvent*)ev +{ + _dragStartPos = ev.locationInWindow; + _dragBit = (Bit*) [self hitTestPoint: _dragStartPos + forLayerMatching: layerIsBit + offset: &_dragOffset]; + if( _dragBit ) { + _dragMoved = NO; + _dropTarget = nil; + _oldHolder = _dragBit.holder; + // Ask holder's and game's permission before dragging: + if( _oldHolder ) + _dragBit = [_oldHolder canDragBit: _dragBit]; + if( _dragBit && ! [_game canBit: _dragBit moveFrom: _oldHolder] ) { + [_oldHolder cancelDragBit: _dragBit]; + _dragBit = nil; + } + if( ! _dragBit ) { + _oldHolder = nil; + NSBeep(); + return; + } + // Start dragging: + _oldSuperlayer = _dragBit.superlayer; + _oldLayerIndex = [_oldSuperlayer.sublayers indexOfObjectIdenticalTo: _dragBit]; + _oldPos = _dragBit.position; + ChangeSuperlayer(_dragBit, self.layer, self.layer.sublayers.count); + _dragBit.pickedUp = YES; + [[NSCursor closedHandCursor] push]; + } else + NSBeep(); +} + +- (void) mouseDragged: (NSEvent*)ev +{ + if( _dragBit ) { + // Get the mouse position, and see if we've moved 3 pixels since the mouseDown: + NSPoint pos = ev.locationInWindow; + if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 ) + _dragMoved = YES; + + // Move the _dragBit (without animation -- it's unnecessary and slows down responsiveness): + NSPoint where = [self convertPoint: pos fromView: nil]; + where.x += _dragOffset.x; + where.y += _dragOffset.y; + + CGPoint newPos = [_dragBit.superlayer convertPoint: NSPointToCGPoint(where) + fromLayer: self.layer]; + + [CATransaction flush]; + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue + forKey:kCATransactionDisableActions]; + _dragBit.position = newPos; + [CATransaction commit]; + + // Find what it's over: + id target = (id) [self hitTestPoint: where + forLayerMatching: layerIsBitHolder + offset: NULL]; + if( target == _oldHolder ) + target = nil; + if( target != _dropTarget ) { + [_dropTarget willNotDropBit: _dragBit]; + _dropTarget.highlighted = NO; + _dropTarget = nil; + } + if( target ) { + CGPoint targetPos = [(CALayer*)target convertPoint: _dragBit.position + fromLayer: _dragBit.superlayer]; + if( [target canDropBit: _dragBit atPoint: targetPos] + && [_game canBit: _dragBit moveFrom: _oldHolder to: target] ) { + _dropTarget = target; + _dropTarget.highlighted = YES; + } + } + } +} + +- (void) mouseUp: (NSEvent*)ev +{ + if( _dragBit ) { + if( _dragMoved ) { + // Update the drag tracking to the final mouse position: + [self mouseDragged: ev]; + _dropTarget.highlighted = NO; + _dragBit.pickedUp = NO; + + // Is the move legal? + if( _dropTarget && [_dropTarget dropBit: _dragBit + atPoint: [(CALayer*)_dropTarget convertPoint: _dragBit.position + fromLayer: _dragBit.superlayer]] ) { + // Yes, notify the interested parties: + [_oldHolder draggedBit: _dragBit to: _dropTarget]; + [_game bit: _dragBit movedFrom: _oldHolder to: _dropTarget]; + } else { + // Nope, cancel: + [_dropTarget willNotDropBit: _dragBit]; + ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex); + _dragBit.position = _oldPos; + [_oldHolder cancelDragBit: _dragBit]; + } + } else { + // Just a click, without a drag: + _dropTarget.highlighted = NO; + _dragBit.pickedUp = NO; + ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex); + [_oldHolder cancelDragBit: _dragBit]; + if( ! [_game clickedBit: _dragBit] ) + NSBeep(); + } + _dropTarget = nil; + _dragBit = nil; + [NSCursor pop]; + } +} + + +#pragma mark - +#pragma mark INCOMING DRAGS: + + +// subroutine to call the target +static int tell( id target, SEL selector, id arg, int defaultValue ) +{ + if( target && [target respondsToSelector: selector] ) + return (ssize_t) [target performSelector: selector withObject: arg]; + else + return defaultValue; +} + + +- (NSDragOperation)draggingEntered:(id )sender +{ + _viewDropTarget = [self hitTestPoint: [sender draggingLocation] + forLayerMatching: layerIsDropTarget + offset: NULL]; + _viewDropOp = _viewDropTarget ?[_viewDropTarget draggingEntered: sender] :NSDragOperationNone; + return _viewDropOp; +} + +- (NSDragOperation)draggingUpdated:(id )sender +{ + CALayer *target = [self hitTestPoint: [sender draggingLocation] + forLayerMatching: layerIsDropTarget + offset: NULL]; + if( target == _viewDropTarget ) { + if( _viewDropTarget ) + _viewDropOp = tell(_viewDropTarget,@selector(draggingUpdated:),sender,_viewDropOp); + } else { + tell(_viewDropTarget,@selector(draggingExited:),sender,0); + _viewDropTarget = target; + if( _viewDropTarget ) + _viewDropOp = [_viewDropTarget draggingEntered: sender]; + else + _viewDropOp = NSDragOperationNone; + } + return _viewDropOp; +} + +- (BOOL)wantsPeriodicDraggingUpdates +{ + return (_viewDropTarget!=nil); +} + +- (void)draggingExited:(id )sender +{ + tell(_viewDropTarget,@selector(draggingExited:),sender,0); + _viewDropTarget = nil; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + return tell(_viewDropTarget,@selector(prepareForDragOperation:),sender,YES); +} + +- (BOOL)performDragOperation:(id )sender +{ + return [_viewDropTarget performDragOperation: sender]; +} + +- (void)concludeDragOperation:(id )sender +{ + tell(_viewDropTarget,@selector(concludeDragOperation:),sender,0); +} + +- (void)draggingEnded:(id )sender +{ + tell(_viewDropTarget,@selector(draggingEnded:),sender,0); +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Card.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Card.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,66 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Bit.h" + + +/** Hardcoded dimensions of a Card */ +#define kCardWidth 100 +#define kCardHeight 150 + + +/* A card of some type (playing card, Community Chest, money, ...) + Has an identifying serial number (could be in the range 1..52 for playing cards). + Can be face-up or down. */ +@interface Card : Bit +{ + @private + int _serialNumber; + CALayer *_front, *_back; + BOOL _faceUp; +} + +/** The range of serialNumbers used for this type of card. Used when instantiating Decks. + Abstract; must be overridden. */ ++ (NSRange) serialNumberRange; + +- (id) initWithSerialNumber: (int)serial position: (CGPoint)pos; + +@property (readonly) int serialNumber; + +/** Cards can be face-up or face-down, of course. */ +@property BOOL faceUp; + + +//protected -- for subclasses only: + +/** Creates the sub-layer that displays the front side of the card. + Subclasses should probably call the superclass method, configure the layer it returns + (based on the card's serialNumber) and then return that layer. */ +- (CALayer*) createFront; + +/** Creates the sub-layer that displays the back side of the card. + Subclasses should probably call the superclass method, configure the layer it returns and + return that layer. */ +- (CALayer*) createBack; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Card.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Card.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,186 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Card.h" +#import "QuartzUtils.h" + + +@implementation Card + + +static CATransform3D kFaceUpTransform, kFaceDownTransform; + ++ (void) initialize +{ + if( self==[Card class] ) { + kFaceUpTransform = kFaceDownTransform = CATransform3DIdentity; + // Construct a 180-degree rotation matrix: + kFaceDownTransform.m11 = kFaceDownTransform.m33 = -1; + // The more obvious way to create kFaceDownTransform would be to call + // CATransform3DMakeRotation(pi,0,1,0), but due to round-off errors, that transform + // will have non-zero values in some other places, making it appear to CA as a true + // 3D transform; this will then cause unexpected clipping behaviors when used. + } +} + + ++ (NSRange) serialNumberRange; +{ + NSAssert1(NO,@"%@ forgot to override +serialNumberRange",self); + return NSMakeRange(0,0); +} + + +- (id) initWithSerialNumber: (int)serial position: (CGPoint)pos +{ + self = [super init]; + if (self != nil) { + _serialNumber = serial; + self.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); + self.position = pos; + self.edgeAntialiasingMask = 0; + _back = [self createBack]; + [self addSublayer: _back]; + _front = [self createFront]; + _front.transform = kFaceDownTransform; + [self addSublayer: _front]; + } + return self; +} + + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [super encodeWithCoder: aCoder]; + [aCoder encodeInt: _serialNumber forKey: @"serialNumber"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder: aDecoder]; + if( self ) { + _serialNumber = [aDecoder decodeIntForKey: @"serialNumber"]; + } + return self; +} + + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@[#%i]",self.class,_serialNumber]; +} + + +@synthesize serialNumber=_serialNumber; + + +- (BOOL) faceUp +{ + return _faceUp; +} + +- (void) setFaceUp: (BOOL)up +{ + if( up != _faceUp ) { + // The Card has separate sub-layers for its front and back. At any time, one of them + // is hidden, by having a 180 degree rotation about the Y axis. + // To flip the card, both front and back layers are flipped over. + CATransform3D xform; + xform = up ?kFaceUpTransform :kFaceDownTransform; + _front.transform = xform; + + xform = up ?kFaceDownTransform :kFaceUpTransform; + _back.transform = xform; + _faceUp = up; + } +} + + +- (CALayer*) createFront +{ + CALayer *front = [[CALayer alloc] init]; + front.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); + front.position = CGPointMake(kCardWidth/2,kCardHeight/2); + front.edgeAntialiasingMask = 0; + front.backgroundColor = kWhiteColor; + front.cornerRadius = 8; + front.borderWidth = 1; + front.borderColor = CGColorCreateGenericGray(0.7, 1.0); + front.doubleSided = NO; // this makes the layer invisible when it's flipped + return [front autorelease]; +} + + +- (CALayer*) createBack +{ + CGSize size = self.bounds.size; + CALayer *back = [[CALayer alloc] init]; + back.bounds = CGRectMake(0,0,size.width,size.height); + back.position = CGPointMake(kCardWidth/2,kCardHeight/2); + back.contents = (id) GetCGImageNamed(@"/Library/Desktop Pictures/Classic Aqua Blue.jpg"); + back.contentsGravity = kCAGravityResize; + back.masksToBounds = YES; + back.borderWidth = 4; + back.borderColor = kWhiteColor; + back.cornerRadius = 8; + back.edgeAntialiasingMask = 0; + back.doubleSided = NO; // this makes the layer invisible when it's flipped + + CATextLayer *label = AddTextLayer(back, @"\u2603", // Unicode snowman character + [NSFont systemFontOfSize: 0.9*size.width], + kCALayerWidthSizable|kCALayerHeightSizable); + label.foregroundColor = CGColorCreateGenericGray(1.0,0.5); + return [back autorelease]; +} + + +#pragma mark - +#pragma mark DRAG-AND-DROP: + + +// An image from another app can be dragged onto a Card to change its background. */ + + +- (NSDragOperation)draggingEntered:(id )sender +{ + NSPasteboard *pb = [sender draggingPasteboard]; + if( [NSImage canInitWithPasteboard: pb] ) + return NSDragOperationCopy; + else + return NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id )sender +{ + CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); + if( image ) { + CALayer *face = _faceUp ?_front :_back; + face.contents = (id) image; + face.contentsGravity = kCAGravityResizeAspectFill; + face.masksToBounds = YES; + return YES; + } else + return NO; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/CheckersGame.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/CheckersGame.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,41 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Game.h" +@class Grid; + + +/** US Checkers, known as Draughts in the UK. (Other countries use similar but not identical rules.) + See: http://en.wikipedia.org/wiki/Draughts */ +@interface CheckersGame : Game +{ + int _numPieces[2]; +} + +// For subclasses (See HexchequerGame): +- (void) addPieces: (NSString*)imageName + toGrid: (Grid*)grid + forPlayer: (int)playerNum + rows: (NSRange)rows + alternating: (BOOL)alternating; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/CheckersGame.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/CheckersGame.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,156 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "CheckersGame.h" +#import "Grid.h" +#import "Piece.h" +#import "QuartzUtils.h" + + +@implementation CheckersGame + + +- (void) addPieces: (NSString*)imageName + toGrid: (Grid*)grid + forPlayer: (int)playerNum + rows: (NSRange)rows + alternating: (BOOL)alternating +{ + Piece *prototype = [[Piece alloc] initWithImageNamed: imageName scale: floor(grid.spacing.width * 0.8)]; + prototype.owner = [self.players objectAtIndex: playerNum]; + unsigned cols=grid.columns; + for( unsigned row=rows.location; row)srcHolder to: (id)dstHolder +{ + Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder; + if( [bit valueForKey: @"King"] ) + if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r + || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) ) + return YES; + return dst==src.fl || dst==src.fr + || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr); +} + +- (void) bit: (Bit*)bit movedFrom: (id)srcHolder to: (id)dstHolder +{ + Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder; + int playerIndex = self.currentPlayer.index; + BOOL isKing = ([bit valueForKey: @"King"] != nil); + + [[NSSound soundNamed: (isKing ?@"Funk" :@"Tink")] play]; + + // "King" a piece that made it to the last row: + if( dst.row == (playerIndex ?0 :7) ) + if( ! isKing ) { + [[NSSound soundNamed: @"Blow"] play]; + bit.scale = 1.4; + [bit setValue: @"King" forKey: @"King"]; + // don't set isKing flag - piece can't jump again after being kinged. + } + + // Check for a capture: + Square *capture = nil; + if(dst==src.fl.fl) + capture = src.fl; + else if(dst==src.fr.fr) + capture = src.fr; + else if(dst==src.bl.bl) + capture = src.bl; + else if(dst==src.br.br) + capture = src.br; + + if( capture ) { + [[NSSound soundNamed: @"Pop"] play]; + Bit *bit = capture.bit; + _numPieces[bit.owner.index]--; + [bit destroy]; + + // Now check if another capture is possible. If so, don't end the turn: + if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) ) + return; + if( isKing ) + if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) ) + return; + } + + [self endTurn]; +} + +- (Player*) checkForWinner +{ + // Whoever runs out of pieces loses: + if( _numPieces[0]==0 ) + return [self.players objectAtIndex: 1]; + else if( _numPieces[1]==0 ) + return [self.players objectAtIndex: 0]; + else + return nil; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Deck.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Deck.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,57 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BitHolder.h" +@class Card; + + +/** A pile of Cards. Unlike a Stack, only the top card is visible or accessible; + so as an optimization, it's the only one that's added to the layer tree. */ +@interface Deck : BitHolder +{ + NSMutableArray *_cards; +} + +/** Creates an empty Deck. */ +- (id) init; + +/** Creates a Deck populated with a full set of cards (in order.) */ +- (id) initWithCardsOfClass: (Class)klass; + +@property (readonly) NSArray *cards; +@property (readonly) Card *topCard; // same as the -bit property + +/** Randomly shuffles all the cards in the Deck. */ +- (void) shuffle; + +/** Flips over the Deck: Reverses the order of cards, and inverts their -faceUp flags. */ +- (void) flip; + +- (void) addCard: (Card*)card; +- (void) addCardAtBottom: (Card*)card; +- (void) addCardAtRandom: (Card*)card; +- (void) addCards: (NSArray*)cards; + +- (Card*) removeTopCard; +- (NSArray*) removeAllCards; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Deck.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Deck.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,267 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Deck.h" +#import "Card.h" +#import "Stack.h" +#import "QuartzUtils.h" +#import "GGBUtils.h" + + +@interface Deck () +- (void) setCards: (NSMutableArray*)cards; +- (void) x_showTopCard; +@end + + +@implementation Deck + + +- (id) init +{ + self = [super init]; + if (self != nil) { + self.bounds = CGRectMake(0,0,kCardWidth,kCardHeight); + self.cornerRadius = 8; + self.backgroundColor = kAlmostInvisibleWhiteColor; + self.borderColor = kHighlightColor; + self.cards = [NSMutableArray array]; + } + return self; +} + +- (id) initWithCardsOfClass: (Class)klass +{ + self = [self init]; + if (self != nil) { + // Create a full deck of cards: + NSRange serials = [klass serialNumberRange]; + for( int i=serials.location; i 0; n-- ) { + int i = random() % n; + Card *card = [_cards objectAtIndex: i]; + [shuffled addObject: card]; + [_cards removeObjectAtIndex: i]; + } + self.cards = shuffled; + [self x_showTopCard]; +} + + +- (void) flip +{ + int n = _cards.count; + NSMutableArray *flipped = [NSMutableArray arrayWithCapacity: n]; + while( --n >= 0 ) { + Card *card = [_cards objectAtIndex: n]; + card.faceUp = ! card.faceUp; + [flipped addObject: card]; + } + self.cards = flipped; + [self x_showTopCard]; +} + + +- (void) addCard: (Card*)card +{ + [_cards addObject: card]; + [self x_showTopCard]; +} + +- (void) addCardAtBottom: (Card*)card +{ + [_cards insertObject: card atIndex: 0]; + if( _cards.count==1 ) + [self x_showTopCard]; +} + +- (void) addCardAtRandom: (Card*)card +{ + // Put the card at some random location, but _not_ on top (unless the deck is empty.) + int n = _cards.count; + if( n==0 ) + [self addCard: card]; + else + [_cards insertObject: card atIndex: (random() % (n-1))]; +} + + +- (void) addCards: (NSArray*)cards +{ + [_cards addObjectsFromArray: cards]; + [self x_showTopCard]; +} + + +- (BOOL) addBit: (Bit*)bit +{ + if( [bit isKindOfClass: [DraggedStack class]] ) { + // Convert a DraggedStack back to a group of Cards: + for( Bit *subBit in [(DraggedStack*)bit bits] ) + if( ! [self addBit: subBit] ) + return NO; + return YES; + } else if( [bit isKindOfClass: [Card class]] ) { + [self addCard: (Card*)bit]; + return YES; + } else + return NO; +} + + +- (Card*) removeTopCard +{ + Card *card = [_cards lastObject]; + if( card ) { + [[card retain] autorelease]; + [_cards removeLastObject]; + _bit = nil; // keep it from being removed from superlayer by _showTopCard + [self x_showTopCard]; + } + return card; +} + + +- (NSArray*) removeAllCards +{ + NSArray *removedCards = [[_cards retain] autorelease]; + self.cards = [NSMutableArray array]; + [removedCards makeObjectsPerformSelector: @selector(removeFromSuperlayer)]; + [self x_showTopCard]; + return removedCards; +} + + +#pragma mark - +#pragma mark BITHOLDER INTERFACE: + + +- (Bit*) canDragBit: (Bit*)bit +{ + if( bit == _bit ) { + [bit retain]; + [_cards removeObjectIdenticalTo: bit]; + _bit = nil; // prevent the card from being removed from my layer + [self x_showTopCard]; + return [bit autorelease]; + } else + return nil; +} + +- (void) cancelDragBit: (Bit*)bit +{ + [self addCard: (Card*)bit]; +} + +- (void) draggedBit: (Bit*)bit to: (id)dst {} + + +- (void) setHighlighted: (BOOL)h +{ + [super setHighlighted: h]; + self.borderWidth = h ?6 :0; +} + +- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point +{ + return [bit isKindOfClass: [Card class]] || [bit isKindOfClass: [DraggedStack class]]; +} + +- (BOOL) dropBit: (Bit*)bit atPoint: (CGPoint)point +{ + return [self addBit: bit]; +} + + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/DemoBoardView.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/DemoBoardView.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,35 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BoardView.h" + + +/** Simple subclass of BoardView that lets you pick a game from a menu, + and displays the game status in a headline. */ +@interface DemoBoardView : BoardView +{ + CATextLayer *_headline; +} + +- (IBAction) startGameFromMenu: (id)sender; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/DemoBoardView.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/DemoBoardView.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,137 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "DemoBoardView.h" +#import "Game.h" +#import "QuartzUtils.h" + + +/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! + ** This sample application uses Objective-C 2.0 garbage collection. + ** Therefore, the source code in this file does NOT perform manual object memory management. + ** If you reuse any of this code in a process that isn't garbage collected, you will need to + ** add all necessary retain/release/autorelease calls, and implement -dealloc methods, + ** otherwise unpleasant leakage will occur! + **/ + + +@implementation DemoBoardView + + +/** Class names of available games */ +static NSString* const kMenuGameNames[] = {@"KlondikeGame", @"CheckersGame", @"HexchequerGame", + @"TicTacToeGame", @"GoGame"}; + +/** Class name of the current game. */ +static NSString* sCurrentGameName = @"KlondikeGame"; + + +- (void) startGameNamed: (NSString*)gameClassName +{ + [super startGameNamed: gameClassName]; + + Game *game = self.game; + [game addObserver: self + forKeyPath: @"currentPlayer" + options: NSKeyValueObservingOptionInitial + context: NULL]; + [game addObserver: self + forKeyPath: @"winner" + options: 0 + context: NULL]; + + self.window.title = [(id)[game class] displayName]; +} + + +- (CGRect) gameBoardFrame +{ + CGRect bounds = [super gameBoardFrame]; + bounds.size.height -= 32; // Leave room for headline + return CGRectInset(bounds,4,4); +} + + +- (void) awakeFromNib +{ + srandomdev(); + + [self registerForDraggedTypes: [NSImage imagePasteboardTypes]]; + [self registerForDraggedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]]; + + CGRect bounds = self.layer.bounds; + self.layer.backgroundColor = GetCGPatternNamed(@"/Library/Desktop Pictures/Small Ripples graphite.png"); + + bounds.size.height -= 32; + _headline = AddTextLayer(self.layer, + nil, [NSFont boldSystemFontOfSize: 24], + kCALayerWidthSizable | kCALayerMinYMargin); + + [self startGameNamed: sCurrentGameName]; +} + + +- (void) startGameFromMenu: (id)sender +{ + sCurrentGameName = kMenuGameNames[ [sender tag] ]; + [self startGameNamed: sCurrentGameName]; +} + + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + Game *game = self.game; + if( object == game ) { + Player *p = game.winner; + NSString *msg; + if( p ) { + [[NSSound soundNamed: @"Sosumi"] play]; + msg = @"%@ wins! Congratulations!"; + } else { + p = game.currentPlayer; + msg = @"Your turn, %@"; + } + _headline.string = [NSString stringWithFormat: msg, p.name]; + } +} + + +- (IBAction) enterFullScreen: (id)sender +{ + [super enterFullScreen: sender]; + [self startGameNamed: sCurrentGameName]; // restart game so it'll use the new size +} + + +#pragma mark - +#pragma mark NSAPPLICATION DELEGATE: + + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender +{ + return YES; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/DiscPiece.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/DiscPiece.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,33 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Piece.h" + + +/** A Piece whose image is superimposed on a disc. + Set the backgroundColor property to change the color or texture of the disc. */ +@interface DiscPiece : Piece +{ + CALayer *_imageLayer; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/DiscPiece.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/DiscPiece.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,57 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "DiscPiece.h" +#import "QuartzUtils.h" + + +@implementation DiscPiece + + +- (void) setImage: (CGImageRef)image scale: (CGFloat)scale +{ + if( scale < 4.0 ) { + int size = MAX(CGImageGetWidth(image), CGImageGetHeight(image)); + if( scale > 0 ) + scale = ceil( size * scale); + else + scale = size; + scale *= 1.2; + } + self.bounds = CGRectMake(0,0,scale,scale); + + if( ! _imageLayer ) { + _imageLayer = [[CALayer alloc] init]; + _imageLayer.contentsGravity = @"resizeAspect"; + [self addSublayer: _imageLayer]; + [_imageLayer release]; + } + _imageLayer.frame = CGRectInset(self.bounds, scale*.1, scale*.1); + _imageLayer.contents = (id) image; + self.cornerRadius = scale/2; + self.borderWidth = 3; + self.borderColor = kTranslucentLightGrayColor; + self.imageName = nil; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Dispenser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Dispenser.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,43 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BitHolder.h" + + +/** A Dispenser provides a supply of identical copies of a particular Bit, one at a time. */ + +@interface Dispenser : BitHolder +{ + Bit *_prototype; + unsigned _quantity; +} + +- (id) initWithPrototype: (Bit*)prototype quantity: (unsigned)quantity frame: (CGRect)frame; + +/** The Bit that will be copied. */ +@property (retain) Bit *prototype; + +/** The number of Bits remaining; decremented whenever one is dragged out. + When it hits zero, no more Bits will appear. */ +@property unsigned quantity; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Dispenser.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Dispenser.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,194 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Dispenser.h" +#import "Piece.h" +#import "QuartzUtils.h" +#import "GGBUtils.h" + + +@implementation Dispenser + + +- (id) initWithPrototype: (Bit*)prototype quantity: (unsigned)quantity frame: (CGRect)frame +{ + self = [super init]; + if (self != nil) { + self.backgroundColor = kTranslucentLightGrayColor; + self.borderColor = kTranslucentGrayColor; + self.borderWidth = 3; + self.cornerRadius = 16; + self.zPosition = kBoardZ; + self.masksToBounds = YES; + self.frame = frame; + self.prototype = prototype; + self.quantity = quantity; + } + return self; +} + + +- (void) dealloc +{ + [_prototype release]; + [super dealloc]; +} + + +@synthesize bit=_bit; + + +- (Bit*) createBit +{ + if( _prototype ) { + Bit *bit = [_prototype copy]; + CGRect bounds = self.bounds; + bit.position = GetCGRectCenter(bounds); + return [bit autorelease]; + } else + return nil; +} + +- (void) x_regenerateCurrentBit +{ + NSAssert(_bit==nil,@"Already have a currentBit"); + + [CATransaction begin]; + [CATransaction setValue: (id)kCFBooleanTrue + forKey: kCATransactionDisableActions]; + self.bit = [self createBit]; + CGPoint pos = _bit.position; + _bit.position = CGPointMake(pos.x, pos.y+70); + [self addSublayer: _bit]; + [CATransaction commit]; + + _bit.position = pos; +} + + +- (Bit*) prototype +{ + return _prototype; +} + +- (void) setPrototype: (Bit*)prototype +{ + setObj(&_prototype, prototype); + if( _bit ) { + [_bit removeFromSuperlayer]; + self.bit = nil; + if( prototype ) + [self x_regenerateCurrentBit]; + } +} + + +- (unsigned) quantity +{ + return _quantity; +} + +- (void) setQuantity: (unsigned)quantity +{ + _quantity = quantity; + if( quantity > 0 && !_bit ) + [self x_regenerateCurrentBit]; + else if( quantity==0 && _bit ) { + [_bit removeFromSuperlayer]; + self.bit = nil; + } +} + + +#pragma mark - +#pragma mark DRAGGING BITS: + + +- (Bit*) canDragBit: (Bit*)bit +{ + bit = [super canDragBit: bit]; + if( bit==_bit ) { + [[bit retain] autorelease]; + self.bit = nil; + } + return bit; +} + +- (void) cancelDragBit: (Bit*)bit +{ + if( ! _bit ) + self.bit = bit; + else + [bit removeFromSuperlayer]; +} + +- (void) draggedBit: (Bit*)bit to: (id)dst +{ + if( --_quantity > 0 ) + [self performSelector: @selector(x_regenerateCurrentBit) withObject: nil afterDelay: 0.0]; +} + +- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point +{ + return [bit isEqual: _bit]; +} + +- (BOOL) dropBit: (Bit*)bit atPoint: (CGPoint)point +{ + [bit removeFromSuperlayer]; + return YES; +} + + +#pragma mark - +#pragma mark DRAG-AND-DROP: + + +// An image from another app can be dragged onto a Dispenser to change the Piece's appearance. + + +- (NSDragOperation)draggingEntered:(id )sender +{ + if( ! [_prototype isKindOfClass: [Piece class]] ) + return NSDragOperationNone; + NSPasteboard *pb = [sender draggingPasteboard]; + if( [NSImage canInitWithPasteboard: pb] ) + return NSDragOperationCopy; + else + return NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id )sender +{ + if( ! [_prototype isKindOfClass: [Piece class]] ) + return NO; + CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); + if( image ) { + [(Piece*)_prototype setImage: image]; + self.prototype = _prototype; // recreates _bit + return YES; + } else + return NO; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/GGBUtils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/GGBUtils.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,31 @@ +/* Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import + +/** Handy utility for assigning a new value to a retained instance variable. Use as: + setObj(&_instanceVar, newValue); + It releases the old value and retains the new one. */ + +void setObj( id *variable, id newValue ); + + +/** Just like setObj except that it _copies_ the new value. */ +void setObjCopy( id *variable, id newValue ); diff -r 000000000000 -r e9f7ba4718e1 Source/GGBUtils.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/GGBUtils.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,38 @@ +/* Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "GGBUtils.h" + + +void setObj( id *variable, id newValue ) +{ + if( *variable != newValue ) { + [*variable release]; + *variable = [newValue retain]; + } +} + +void setObjCopy( id *variable, id newValue ) +{ + if( *variable != newValue ) { + [*variable release]; + *variable = [(id)newValue copy]; + } +} diff -r 000000000000 -r e9f7ba4718e1 Source/Game.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Game.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,115 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import +@class Bit, Player; +@protocol BitHolder; + + +/** Abstract superclass. Keeps track of the rules and turns of a game. */ +@interface Game : NSObject +{ + CALayer *_board; + NSArray *_players; + Player *_currentPlayer, *_winner; +} + +/** Returns the human-readable name of this game. + (By default it just returns the class name with the "Game" suffix removed.) */ ++ (NSString*) displayName; + +@property (readonly, copy) NSArray *players; +@property (readonly) Player *currentPlayer, *winner; + + +// Methods for subclasses to implement: + +/** Designated initializer. After calling the superclass implementation, + it should add the necessary Grids, Pieces, Cards, Decks etc. to the board. */ +- (id) initWithBoard: (CALayer*)board; + +/** Should return YES if it is legal for the given bit to be moved from its current holder. + Default implementation always returns YES. */ +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src; + +/** Should return YES if it is legal for the given Bit to move from src to dst. + Default implementation always returns YES. */ +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src to: (id)dst; + +/** Should handle any side effects of a Bit's movement, such as captures or scoring. + Does not need to do the actual movement! That's already happened. + It should end by calling -endTurn, if the player's turn is over. + Default implementation just calls -endTurn. */ +- (void) bit: (Bit*)bit movedFrom: (id)src to: (id)dst; + +/** Called instead of the above if a Bit is simply clicked, not dragged. + Should return NO if the click is illegal (i.e. clicking an empty draw pile in a card game.) + Default implementation always returns YES. */ +- (BOOL) clickedBit: (Bit*)bit; + +/** Should return the winning player, if the current position is a win, else nil. + Default implementation returns nil. */ +- (Player*) checkForWinner; + + +// Protected methods for subclasses to call: + +/** Sets the number of players in the game. Subclass initializers should call this. */ +- (void) setNumberOfPlayers: (unsigned)n; + +/** Advance to the next player, when a turn is over. */ +- (void) nextPlayer; + +/** Checks for a winner and advances to the next player. */ +- (void) endTurn; + +@end + + + +/** A mostly-passive object used to represent a player. */ +@interface Player : NSObject +{ + Game *_game; + NSString *_name; +} + +- (id) initWithGame: (Game*)game; + +@property (readonly) Game *game; +@property (copy) NSString *name; +@property (readonly) int index; +@property (readonly, getter=isCurrent) BOOL current; +@property (readonly, getter=isFriendly) BOOL friendly; +@property (readonly, getter=isUnfriendly) BOOL unfriendly; +@property (readonly) Player *nextPlayer, *previousPlayer; + +@end + + + +@interface CALayer (Game) + +/** Called on any CALayer in the game's layer tree, will return the current Game object. */ +@property (readonly) Game *game; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Game.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Game.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,212 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Game.h" +#import "Bit.h" + + +@interface Game () +@property (copy) NSArray *players; +@property (assign) Player *currentPlayer, *winner; +@end + + +/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! + ** This sample application uses Objective-C 2.0 garbage collection. + ** Therefore, the source code in this file does NOT perform manual object memory management. + ** If you reuse any of this code in a process that isn't garbage collected, you will need to + ** add all necessary retain/release/autorelease calls, and implement -dealloc methods, + ** otherwise unpleasant leakage will occur! + **/ + + +@implementation Game + + ++ (NSString*) displayName +{ + NSString* name = [self description]; + if( [name hasSuffix: @"Game"] ) + name = [name substringToIndex: name.length-4]; + return name; +} + + +- (id) initWithBoard: (CALayer*)board +{ + self = [super init]; + if (self != nil) { + _board = [board retain]; + // Store a pointer to myself as the value of the "Game" property + // of my root layer. (CALayers can have arbitrary KV properties stored into them.) + // This is used by the -[CALayer game] category method defined below, to find the Game. + [board setValue: self forKey: @"Game"]; + } + return self; +} + + +- (void) dealloc +{ + [_board release]; + [_players release]; + [super dealloc]; +} + + +@synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner; + + +- (void) setNumberOfPlayers: (unsigned)n +{ + NSMutableArray *players = [NSMutableArray arrayWithCapacity: n]; + for( int i=1; i<=n; i++ ) { + Player *player = [[Player alloc] initWithGame: self]; + player.name = [NSString stringWithFormat: @"Player %i",i]; + [players addObject: player]; + [player release]; + } + self.winner = nil; + self.currentPlayer = nil; + self.players = players; +} + + +- (void) nextPlayer +{ + if( ! _currentPlayer ) { + NSLog(@"*** The %@ Begins! ***", self.class); + self.currentPlayer = [_players objectAtIndex: 0]; + } else { + self.currentPlayer = _currentPlayer.nextPlayer; + } + NSLog(@"Current player is %@",_currentPlayer); +} + + +- (void) endTurn +{ + NSLog(@"--- End of turn"); + Player *winner = [self checkForWinner]; + if( winner ) { + NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner); + self.winner = winner; + } else + [self nextPlayer]; +} + + +#pragma mark - +#pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN: + + +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src +{ + return YES; +} + +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src to: (id)dst +{ + return YES; +} + +- (void) bit: (Bit*)bit movedFrom: (id)src to: (id)dst +{ + [self endTurn]; +} + +- (BOOL) clickedBit: (Bit*)bit +{ + return YES; +} + +- (Player*) checkForWinner +{ + return nil; +} + + +@end + + + + +@implementation Player + + +- (id) initWithGame: (Game*)game +{ + self = [super init]; + if (self != nil) { + _game = game; + } + return self; +} + + +@synthesize game=_game, name=_name; + +- (BOOL) isCurrent {return self == _game.currentPlayer;} +- (BOOL) isFriendly {return self == _game.currentPlayer;} // could be overridden for games with partners +- (BOOL) isUnfriendly {return ! self.friendly;} + +- (int) index +{ + return [_game.players indexOfObjectIdenticalTo: self]; +} + +- (Player*) nextPlayer +{ + return [_game.players objectAtIndex: (self.index+1) % _game.players.count]; +} + +- (Player*) previousPlayer +{ + return [_game.players objectAtIndex: (self.index-1) % _game.players.count]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@[%@]", self.class,self.name]; +} + +@end + + + + +@implementation CALayer (Game) + +- (Game*) game +{ + // The Game object stores a pointer to itself as the value of the "Game" property + // of its root layer. (CALayers can have arbitrary KV properties stored into them.) + for( CALayer *layer = self; layer; layer=layer.superlayer ) { + Game *game = [layer valueForKey: @"Game"]; + if( game ) + return game; + } + NSAssert1(NO,@"Couldn't look up Game from %@",self); + return nil; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/GeekGameBoard_Prefix.pch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/GeekGameBoard_Prefix.pch Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,31 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// +// Prefix header for all source files of the 'GeekGameBoard' target in the 'GeekGameBoard' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff -r 000000000000 -r e9f7ba4718e1 Source/GoGame.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/GoGame.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,35 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Game.h" +@class RectGrid, Stack; + + +/** The Asian board game Go. + See: http://en.wikipedia.org/wiki/Go_%28board_game%29 */ +@interface GoGame : Game +{ + RectGrid *_grid; + Stack *_captured[2]; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/GoGame.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/GoGame.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,166 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "GoGame.h" +#import "Grid.h" +#import "Piece.h" +#import "Dispenser.h" +#import "Stack.h" +#import "QuartzUtils.h" + + +@implementation GoGame + + +- (void) x_createDispenser: (NSString*)imageName position: (CGPoint)position forPlayer: (unsigned)playerNo +{ + CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1; // make sure it's even + Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize]; + stone.owner = [self.players objectAtIndex: playerNo]; + CGRect frame = {position, {1.5*pieceSize,1.5*pieceSize}}; + Dispenser *disp = [[Dispenser alloc] initWithPrototype: stone quantity: INT_MAX frame:frame]; + [_board addSublayer: disp]; + [disp release]; + [stone release]; +} + + +- (id) initWithBoard: (CALayer*)board +{ + self = [super initWithBoard: board]; + if (self != nil) { + [self setNumberOfPlayers: 2]; + + CGSize size = board.bounds.size; + RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 + frame: CGRectMake((size.width-size.height+16)/2,8, + size.height-16,size.height-16)]; + _grid = grid; + grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg"); + grid.borderColor = kTranslucentLightGrayColor; + grid.borderWidth = 2; + grid.lineColor = kTranslucentGrayColor; + grid.cellClass = [GoSquare class]; + [grid addAllCells]; + ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES; + ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES; + ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES; + ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES; + grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO; + [board addSublayer: grid]; + [grid release]; + + CGRect gridFrame = grid.frame; + CGFloat pieceSize = (int)grid.spacing.width & ~1; // make sure it's even + [self x_createDispenser: @"Red Ball.png" + position: CGPointMake(CGRectGetMinX(gridFrame)-2*pieceSize, + CGRectGetMinY(gridFrame)) + forPlayer: 0]; + [self x_createDispenser: @"White Ball.png" + position: CGPointMake(CGRectGetMaxX(gridFrame)+0.5*pieceSize, + CGRectGetMaxY(gridFrame)-1.5*pieceSize) + forPlayer: 1]; + + CGFloat captureHeight = gridFrame.size.height-4*pieceSize; + _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0) + spacing: CGSizeMake(0,pieceSize) + wrapInterval: floor(captureHeight/pieceSize) + wrapSpacing: CGSizeMake(-pieceSize,0)]; + _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize, + CGRectGetMinY(gridFrame)+3*pieceSize, + 2*pieceSize, captureHeight); + _captured[0].zPosition = kPieceZ+1; + [board addSublayer: _captured[0]]; + [_captured[0] release]; + + _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight) + spacing: CGSizeMake(0,-pieceSize) + wrapInterval: floor(captureHeight/pieceSize) + wrapSpacing: CGSizeMake(pieceSize,0)]; + _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize, + CGRectGetMinY(gridFrame)+pieceSize, + 2*pieceSize, captureHeight); + _captured[1].zPosition = kPieceZ+1; + [board addSublayer: _captured[1]]; + [_captured[1] release]; + + [self nextPlayer]; +} + return self; +} + + +- (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder to: (id)dstHolder +{ + Square *dst=(Square*)dstHolder; + + // There should be a check here for a "ko" (repeated position) ... exercise for the reader! + + // Check for suicidal move. First an easy check for an empty adjacent space: + NSArray *neighbors = dst.neighbors; + for( GridCell *c in neighbors ) + if( c.empty ) + return YES; // there's an empty space + // If the piece is surrounded, check the neighboring groups' liberties: + for( GridCell *c in neighbors ) { + int nLiberties; + [c getGroup: &nLiberties]; + if( c.bit.unfriendly ) { + if( nLiberties <= 1 ) + return YES; // the move captures, so it's not suicidal + } else { + if( nLiberties > 1 ) + return YES; // the stone joins a group with other liberties + } + } + return NO; +} + + +- (void) bit: (Bit*)bit movedFrom: (id)srcHolder to: (id)dstHolder +{ + Square *dst=(Square*)dstHolder; + int curIndex = _currentPlayer.index; + // Check for captured enemy groups: + BOOL captured = NO; + for( GridCell *c in dst.neighbors ) + if( c.bit.unfriendly ) { + int nLiberties; + NSSet *group = [c getGroup: &nLiberties]; + if( nLiberties == 0 ) { + captured = YES; + for( GridCell *capture in group ) + [_captured[curIndex] addBit: capture.bit]; // Moves piece to POW camp! + } + } + if( captured ) + [[NSSound soundNamed: @"Pop"] play]; + + [self nextPlayer]; +} + + +// This sample code makes no attempt to detect the end of the game, or count score, +// both of which are rather complex to decide in Go. + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Grid.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Grid.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,139 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "BitHolder.h" +@class GridCell; + + +/** Abstract superclass of regular geometric grids of GridCells that Bits can be placed on. */ +@interface Grid : CALayer +{ + unsigned _nRows, _nColumns; + CGSize _spacing; + Class _cellClass; + CGColorRef _cellColor, _lineColor; + BOOL _usesDiagonals, _allowsMoves, _allowsCaptures; + NSMutableArray *_cells; // Really a 2D array, in row-major order. +} + +/** Initializes a new Grid with the given dimensions and cell size, and position in superview. + Note that a new Grid has no cells! Either call -addAllCells, or -addCellAtRow:column:. */ +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + spacing: (CGSize)spacing + position: (CGPoint)pos; + +/** Initializes a new Grid with the given dimensions and frame in superview. + The cell size will be computed by dividing frame size by dimensions. + Note that a new Grid has no cells! Either call -addAllCells, or -addCellAtRow:column:. */ +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + frame: (CGRect)frame; + +@property Class cellClass; // What kind of GridCells to create +@property (readonly) unsigned rows, columns; // Dimensions of the grid +@property (readonly) CGSize spacing; // x,y spacing of GridCells +@property CGColorRef cellColor, lineColor; // Cell background color, line color (or nil) +@property BOOL usesDiagonals; // Affects GridCell.neighbors, for rect grids +@property BOOL allowsMoves, allowsCaptures; // Can pieces be moved, and can they land on others? + +/** Returns the GridCell at the given coordinates, or nil if there is no cell there. + It's OK to call this with off-the-board coordinates; it will just return nil.*/ +- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col; + +/** Adds cells at all coordinates, creating a complete grid. */ +- (void) addAllCells; + +/** Adds a GridCell at the given coordinates. */ +- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col; + +/** Removes a particular cell, leaving a blank space. */ +- (void) removeCellAtRow: (unsigned)row column: (unsigned)col; + + +// protected: +- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col + suggestedFrame: (CGRect)frame; + +@end + + +/** Abstract superclass of a single cell in a grid. */ +@interface GridCell : BitHolder +{ + Grid *_grid; + unsigned _row, _column; +} + +- (id) initWithGrid: (Grid*)grid + row: (unsigned)row column: (unsigned)col + frame: (CGRect)frame; + +@property (readonly) Grid* grid; +@property (readonly) unsigned row, column; +@property (readonly) NSArray* neighbors; // Dependent on grid.usesDiagonals + +/** Returns YES if 'forward' is north (increasing row#) for the current player */ +@property (readonly) BOOL fwdIsN; + +/* Go-style group detection. Returns the set of contiguous GridCells that have pieces of the same + owner as this one, and optionally a count of the number of "liberties", or adjacent empty cells. */ +- (NSSet*) getGroup: (int*)outLiberties; + +// protected: +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill; +@end + + + +/** A rectangular grid of squares. */ +@interface RectGrid : Grid +{ + CGColorRef _altCellColor; +} + +/** If non-nil, alternate cells will be drawn with this background color, in a checkerboard pattern. + The precise rule is that cells whose row+column is odd use the altCellColor.*/ +@property CGColorRef altCellColor; + +@end + + + +/* A square in a RectGrid */ +@interface Square : GridCell + +@property (readonly) Square *nw, *n, *ne, *e, *se, *s, *sw, *w; // Absolute directions (n = increasing row#) +@property (readonly) Square *fl, *f, *fr, *r, *br, *b, *bl, *l; // Relative to player (upside-down for player 2) + +@end + + +/* Substitute this for Square in a RectGrid's cellClass to draw the lines through the centers + of the squares, so the pieces sit on the intersections, as in a Go board. */ +@interface GoSquare : Square +{ + BOOL _dotted; +} + +/** Set to YES to put a dot at the intersection, as in the handicap points of a Go board. */ +@property BOOL dotted; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Grid.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Grid.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,486 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Grid.h" +#import "Bit.h" +#import "Game.h" +#import "QuartzUtils.h" + + +@implementation Grid + + +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + spacing: (CGSize)spacing + position: (CGPoint)pos +{ + NSParameterAssert(nRows>0 && nColumns>0); + self = [super init]; + if( self ) { + _nRows = nRows; + _nColumns = nColumns; + _spacing = spacing; + _cellClass = [GridCell class]; + _lineColor = kBlackColor; + _allowsMoves = YES; + _usesDiagonals = YES; + + self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2); + self.position = pos; + self.anchorPoint = CGPointMake(0,0); + self.zPosition = kBoardZ; + self.needsDisplayOnBoundsChange = YES; + + unsigned n = nRows*nColumns; + _cells = [[NSMutableArray alloc] initWithCapacity: n]; + id null = [NSNull null]; + while( n-- > 0 ) + [_cells addObject: null]; + + [self setNeedsDisplay]; + } + return self; +} + + +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + frame: (CGRect)frame +{ + CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns, + (frame.size.height-2)/(CGFloat)nRows) ); + return [self initWithRows: nRows columns: nColumns + spacing: CGSizeMake(spacing,spacing) + position: frame.origin]; +} + + +- (void) dealloc +{ + CGColorRelease(_cellColor); + CGColorRelease(_lineColor); + [_cells release]; + [super dealloc]; +} + + +static void setcolor( CGColorRef *var, CGColorRef color ) +{ + if( color != *var ) { + CGColorRelease(*var); + *var = CGColorRetain(color); + } +} + +- (CGColorRef) cellColor {return _cellColor;} +- (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);} + +- (CGColorRef) lineColor {return _lineColor;} +- (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);} + +@synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, + usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures; + + +#pragma mark - +#pragma mark GEOMETRY: + + +- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col +{ + if( row < _nRows && col < _nColumns ) { + id cell = [_cells objectAtIndex: row*_nColumns+col]; + if( cell != [NSNull null] ) + return cell; + } + return nil; +} + + +/** Subclasses can override this, to change the cell's class or frame. */ +- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col + suggestedFrame: (CGRect)frame +{ + return [[[_cellClass alloc] initWithGrid: self + row: row column: col + frame: frame] + autorelease]; +} + + +- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col +{ + NSParameterAssert(row<_nRows); + NSParameterAssert(col<_nColumns); + unsigned index = row*_nColumns+col; + GridCell *cell = [_cells objectAtIndex: index]; + if( (id)cell == [NSNull null] ) { + CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height, + _spacing.width,_spacing.height); + cell = [self createCellAtRow: row column: col suggestedFrame: frame]; + if( cell ) { + [_cells replaceObjectAtIndex: index withObject: cell]; + [self addSublayer: cell]; + [self setNeedsDisplay]; + } + } + return cell; +} + + +- (void) addAllCells +{ + for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back' + for( int col=0; col<_nColumns; col++ ) + [self addCellAtRow: row column: col]; +} + + +- (void) removeCellAtRow: (unsigned)row column: (unsigned)col +{ + NSParameterAssert(row<_nRows); + NSParameterAssert(col<_nColumns); + unsigned index = row*_nColumns+col; + id cell = [_cells objectAtIndex: index]; + if( cell != [NSNull null] ) + [cell removeFromSuperlayer]; + [_cells replaceObjectAtIndex: index withObject: [NSNull null]]; + [self setNeedsDisplay]; +} + + +#pragma mark - +#pragma mark DRAWING: + + +- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill +{ + // Subroutine of -drawInContext:. Draws all the cells, with or without a fill. + for( unsigned row=0; row<_nRows; row++ ) + for( unsigned col=0; col<_nColumns; col++ ) { + GridCell *cell = [self cellAtRow: row column: col]; + if( cell ) + [cell drawInParentContext: ctx fill: fill]; + } +} + + +- (void)drawInContext:(CGContextRef)ctx +{ + // Custom CALayer drawing implementation. Delegates to the cells to draw themselves + // in me; this is more efficient than having each cell have its own drawing. + if( _cellColor ) { + CGContextSetFillColorWithColor(ctx, _cellColor); + [self drawCellsInContext: ctx fill: YES]; + } + if( _lineColor ) { + CGContextSetStrokeColorWithColor(ctx,_lineColor); + [self drawCellsInContext:ctx fill: NO]; + } +} + + +@end + + + +#pragma mark - + +@implementation GridCell + + +- (id) initWithGrid: (Grid*)grid + row: (unsigned)row column: (unsigned)col + frame: (CGRect)frame +{ + self = [super init]; + if (self != nil) { + _grid = grid; + _row = row; + _column = col; + self.position = frame.origin; + CGRect bounds = frame; + bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries + bounds.origin.y -= floor(bounds.origin.y); + self.bounds = bounds; + self.anchorPoint = CGPointMake(0,0); + self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:) + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row]; +} + +@synthesize grid=_grid, row=_row, column=_column; + + +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill +{ + // Default implementation just fills or outlines the cell. + CGRect frame = self.frame; + if( fill ) + CGContextFillRect(ctx,frame); + else + CGContextStrokeRect(ctx, frame); +} + + +- (void) setBit: (Bit*)bit +{ + if( bit != self.bit ) { + [super setBit: bit]; + if( bit ) { + // Center it: + CGSize size = self.bounds.size; + bit.position = CGPointMake(floor(size.width/2.0), + floor(size.height/2.0)); + } + } +} + +- (Bit*) canDragBit: (Bit*)bit +{ + if( _grid.allowsMoves && bit==self.bit ) + return [super canDragBit: bit]; + else + return nil; +} + +- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point +{ + return self.bit == nil || _grid.allowsCaptures; +} + + +- (BOOL) fwdIsN +{ + return self.game.currentPlayer.index == 0; +} + + +- (NSArray*) neighbors +{ + BOOL orthogonal = ! _grid.usesDiagonals; + NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8]; + for( int dy=-1; dy<=1; dy++ ) + for( int dx=-1; dx<=1; dx++ ) + if( (dx || dy) && !(orthogonal && dx && dy) ) { + GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx]; + if( cell ) + [neighbors addObject: cell]; + } + return neighbors; +} + + +// Recursive subroutine used by getGroup:. +- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner +{ + Bit *bit = self.bit; + if( bit == nil ) { + if( [liberties containsObject: self] ) + return; // already traversed + [liberties addObject: self]; + } else if( bit.owner==owner ) { + if( [group containsObject: self] ) + return; // already traversed + [group addObject: self]; + for( GridCell *c in self.neighbors ) + [c x_addToGroup: group liberties: liberties owner: owner]; + } +} + + +- (NSSet*) getGroup: (int*)outLiberties +{ + NSMutableSet *group=[NSMutableSet set], *liberties=nil; + if( outLiberties ) + liberties = [NSMutableSet set]; + [self x_addToGroup: group liberties: liberties owner: self.bit.owner]; + if( outLiberties ) + *outLiberties = liberties.count; + return group; +} + + +#pragma mark - +#pragma mark DRAG-AND-DROP: + + +// An image from another app can be dragged onto a Dispenser to change the Piece's appearance. + + +- (NSDragOperation)draggingEntered:(id )sender +{ + NSPasteboard *pb = [sender draggingPasteboard]; + if( [NSImage canInitWithPasteboard: pb] ) + return NSDragOperationCopy; + else + return NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id )sender +{ + CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); + if( image ) { + CGColorRef pattern = CreatePatternColor(image); + _grid.cellColor = pattern; + CGColorRelease(pattern); + [_grid setNeedsDisplay]; + return YES; + } else + return NO; +} + + +@end + + + + +#pragma mark - + +@implementation RectGrid + + +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + spacing: (CGSize)spacing + position: (CGPoint)pos +{ + self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos]; + if( self ) { + _cellClass = [Square class]; + } + return self; +} + + +- (CGColorRef) altCellColor {return _altCellColor;} +- (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);} + + +@end + + + +#pragma mark - + +@implementation Square + + +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill +{ + if( fill ) { + CGColorRef c = ((RectGrid*)_grid).altCellColor; + if( c ) { + if( ! ((_row+_column) & 1) ) + c = _grid.cellColor; + CGContextSetFillColorWithColor(ctx, c); + } + } + [super drawInParentContext: ctx fill: fill]; +} + + +- (void) setHighlighted: (BOOL)highlighted +{ + [super setHighlighted: highlighted]; + self.borderWidth = (highlighted ?6 :0); +} + + +- (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];} +- (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];} +- (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];} +- (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];} +- (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];} +- (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];} +- (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];} +- (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];} + +// Directions relative to the current player: +- (Square*) fl {return self.fwdIsN ?self.nw :self.se;} +- (Square*) f {return self.fwdIsN ?self.n :self.s;} +- (Square*) fr {return self.fwdIsN ?self.ne :self.sw;} +- (Square*) r {return self.fwdIsN ?self.e :self.w;} +- (Square*) br {return self.fwdIsN ?self.se :self.nw;} +- (Square*) b {return self.fwdIsN ?self.s :self.n;} +- (Square*) bl {return self.fwdIsN ?self.sw :self.ne;} +- (Square*) l {return self.fwdIsN ?self.w :self.e;} + + +- (BOOL)performDragOperation:(id )sender +{ + CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); + if( image ) { + CGColorRef color = CreatePatternColor(image); + RectGrid *rectGrid = (RectGrid*)_grid; + if( rectGrid.altCellColor && ((_row+_column) & 1) ) + rectGrid.altCellColor = color; + else + rectGrid.cellColor = color; + CGColorRelease(color); + [rectGrid setNeedsDisplay]; + return YES; + } else + return NO; +} + +@end + + + +#pragma mark - + +@implementation GoSquare + +@synthesize dotted=_dotted; + +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill +{ + if( fill ) + [super drawInParentContext: ctx fill: fill]; + else { + CGRect frame = self.frame; + const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, + midy=floor(CGRectGetMidY(frame))+0.5; + CGPoint p[4] = {{CGRectGetMinX(frame),midy}, + {CGRectGetMaxX(frame),midy}, + {midx,CGRectGetMinY(frame)}, + {midx,CGRectGetMaxY(frame)}}; + if( ! self.s ) p[2].y = midy; + if( ! self.n ) p[3].y = midy; + if( ! self.w ) p[0].x = midx; + if( ! self.e ) p[1].x = midx; + CGContextStrokeLineSegments(ctx, p, 4); + + if( _dotted ) { + CGContextSetFillColorWithColor(ctx,_grid.lineColor); + CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5); + CGContextFillEllipseInRect(ctx, dot); + } + } +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/HexGrid.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/HexGrid.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,47 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Grid.h" + + +/** Hexagonal grid. + The hexes are oriented with points facing up and down. + The coords are still rectangular, since the hexes line up in straight horizontal rows, + but every odd-numbered row of hexes is offset slightly to the right. */ +@interface HexGrid : Grid +{ + CGFloat _side, _capHeight; + CGPathRef _cellPath; +} + +/** Similar to -addAllCells, but only adds enough cells to create a hexagonal board. */ +- (void) addCellsInHexagon; + +@end + + +@interface Hex : GridCell + +@property (readonly) Hex *nw, *ne, *e, *se, *sw, *w; // Absolute directions (n = increasing row#) +@property (readonly) Hex *fl, *fr, *r, *br, *bl, *l; // Relative to player (upside-down for player 2) + +@end \ No newline at end of file diff -r 000000000000 -r e9f7ba4718e1 Source/HexGrid.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/HexGrid.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,200 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "HexGrid.h" +#import "Game.h" + + +@implementation HexGrid + + +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + spacing: (CGSize)spacing + position: (CGPoint)pos +{ + // Ignore given spacing.height; set it to make the hexagons regular. + CGFloat capHeight = spacing.height / 2 * tan(M_PI/6); + CGFloat side = spacing.height / 2 / cos(M_PI/6); + spacing.height = side + capHeight; + + self = [super initWithRows: nRows columns: nColumns + spacing: spacing + position: pos]; + if( self ) { + _capHeight = capHeight; + _side = side; + self.bounds = CGRectMake(-1, -1, + (nColumns+0.5)*spacing.width + 2, + nRows*spacing.height+capHeight + 2); + _cellClass = [Hex class]; + } + return self; +} + + +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns + frame: (CGRect)frame; +{ + // Compute the horizontal spacing: + CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns, + (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) )); + return [self initWithRows: nRows columns: nColumns + spacing: CGSizeMake(s,s) + position: frame.origin]; +} + + +- (void) dealloc +{ + CGPathRelease(_cellPath); + [super dealloc]; +} + + +- (void) addCellsInHexagon +{ + int size = _nRows - !(_nRows & 1); // make it odd + for( int row=0; row<_nRows; row++ ) { + int n; // # of hexes remaining in this row + if( row < size ) + n = size - abs(row-size/2); + else + n = 0; + int c0 = floor(((int)_nRows+1-n -(row&1))/2.0); // col of 1st remaining hex + + for( int col=0; col<_nColumns; col++ ) + if( col>=c0 && col)srcHolder to: (id)dstHolder +{ + Hex *src=(Hex*)srcHolder, *dst=(Hex*)dstHolder; + if( [bit valueForKey: @"King"] ) + if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r + || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) + || (src.l.bit.unfriendly && dst==src.l.l) || (src.r.bit.unfriendly && dst==src.r.r) ) + return YES; + return dst==src.fl || dst==src.fr + || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr); +} + +- (void) bit: (Bit*)bit movedFrom: (id)srcHolder to: (id)dstHolder +{ + Hex *src=(Hex*)srcHolder, *dst=(Hex*)dstHolder; + int playerIndex = self.currentPlayer.index; + BOOL isKing = ([bit valueForKey: @"King"] != nil); + + [[NSSound soundNamed: (isKing ?@"Funk" :@"Tink")] play]; + + // "King" a piece that made it to the last row: + if( dst.row == (playerIndex ?0 :8) ) + if( ! isKing ) { + [[NSSound soundNamed: @"Blow"] play]; + bit.scale = 1.4; + [bit setValue: @"King" forKey: @"King"]; + // don't set isKing flag - piece can't capture again after being kinged. + } + + // Check for a capture: + Hex *capture = nil; + if(dst==src.fl.fl) + capture = src.fl; + else if(dst==src.fr.fr) + capture = src.fr; + else if(dst==src.bl.bl) + capture = src.bl; + else if(dst==src.br.br) + capture = src.br; + else if(dst==src.l.l) + capture = src.l; + else if(dst==src.r.r) + capture = src.r; + + if( capture ) { + [[NSSound soundNamed: @"Pop"] play]; + Bit *bit = capture.bit; + _numPieces[bit.owner.index]--; + [bit destroy]; + + // Now check if another capture is possible. If so, don't end the turn: + if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) ) + return; + if( isKing ) + if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) + || (dst.l.bit.unfriendly && dst.l.l.empty) || (dst.r.bit.unfriendly && dst.r.r.empty)) + return; + } + + [self endTurn]; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/KlondikeGame.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/KlondikeGame.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,34 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Game.h" +@class Deck, Stack; + + +/** The classic card solitaire game Klondike. + See: http://en.wikipedia.org/wiki/Klondike_(solitaire) */ +@interface KlondikeGame : Game { + Deck *_deck, *_sink; + Deck *_aces[4]; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/KlondikeGame.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/KlondikeGame.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,181 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "KlondikeGame.h" +#import "Deck.h" +#import "PlayingCard.h" +#import "Stack.h" + + +#define kStackHeight 500 + + +/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! + ** This sample application uses Objective-C 2.0 garbage collection. + ** Therefore, the source code in this file does NOT perform manual object memory management. + ** If you reuse any of this code in a process that isn't garbage collected, you will need to + ** add all necessary retain/release/autorelease calls, and implement -dealloc methods, + ** otherwise unpleasant leakage will occur! + **/ + + +@implementation KlondikeGame + + +- (id) initWithBoard: (CALayer*)board +{ + self = [super initWithBoard: board]; + if (self != nil) { + [self setNumberOfPlayers: 1]; + + _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]]; + [_deck shuffle]; + _deck.position = CGPointMake(kCardWidth/2+16,kCardHeight/2+16); + [board addSublayer: _deck]; + + _sink = [[Deck alloc] init]; + _sink.position = CGPointMake(3*kCardWidth/2+32,kCardHeight/2+16); + [board addSublayer: _sink]; + + for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) { + Deck *aces = [[Deck alloc] init]; + aces.position = CGPointMake(kCardWidth/2+16+(kCardWidth+16)*(suit%2), + 120+kCardHeight+(kCardHeight+16)*(suit/2)); + [board addSublayer: aces]; + _aces[suit] = aces; + } + + for( int s=0; s<7; s++ ) { + Stack *stack = [[Stack alloc] initWithStartPos: CGPointMake(kCardWidth/2, + kStackHeight-kCardHeight/2.0) + spacing: CGSizeMake(0,-22)]; + stack.frame = CGRectMake(260+s*(kCardWidth+16),16, kCardWidth,kStackHeight); + stack.backgroundColor = nil; + stack.dragAsStacks = YES; + [board addSublayer: stack]; + + // According to the rules, one card should be added to each stack in turn, instead + // of populating entire stacks one at a time. However, if one trusts the Deck's + // -shuffle method (which uses the random() function, seeded with a high-entropy + // cryptographically-strong value), it shouldn't make any difference :-) + for( int c=0; c<=s; c++ ) + [stack addBit: [_deck removeTopCard]]; + ((Card*)stack.bits.lastObject).faceUp = YES; + } + + [self nextPlayer]; + } + return self; +} + + +- (BOOL) clickedBit: (Bit*)bit +{ + if( [bit isKindOfClass: [Card class]] ) { + Card *card = (Card*)bit; + if( card.holder == _deck ) { + // Click on deck deals 3 cards to the sink: + for( int i=0; i<3; i++ ) { + Card *card = [_deck removeTopCard]; + if( card ) { + [_sink addCard: card]; + card.faceUp = YES; + } + } + [self endTurn]; + return YES; + } else if( card.holder == _sink ) { + // Clicking the sink when the deck is empty re-deals: + if( _deck.empty ) { + [_deck addCards: [_sink removeAllCards]]; + [_deck flip]; + [self endTurn]; + return YES; + } + } else { + // Click on a card elsewhere turns it face-up: + if( ! card.faceUp ) { + card.faceUp = YES; + return YES; + } + } + } + return NO; +} + + +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src +{ + if( [bit isKindOfClass: [DraggedStack class]] ) { + Card *bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0]; + if( ! bottomSrc.faceUp ) + return NO; + } + return YES; +} + + +- (BOOL) canBit: (Bit*)bit moveFrom: (id)src to: (id)dst +{ + if( src==_deck || dst==_deck || dst==_sink ) + return NO; + + // Find the bottom card being moved, and the top card it's moving onto: + PlayingCard *bottomSrc; + if( [bit isKindOfClass: [DraggedStack class]] ) + bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0]; + else + bottomSrc = (PlayingCard*)bit; + + PlayingCard *topDst; + if( [dst isKindOfClass: [Deck class]] ) { + // Dragging to an ace pile: + if( ! [bit isKindOfClass: [Card class]] ) + return NO; + topDst = (PlayingCard*) ((Deck*)dst).topCard; + if( topDst == nil ) + return bottomSrc.rank == kRankAce; + else + return bottomSrc.suit == topDst.suit && bottomSrc.rank == topDst.rank+1; + + } else { + // Dragging to a card stack: + topDst = (PlayingCard*) ((Stack*)dst).topBit; + if( topDst == nil ) + return bottomSrc.rank == kRankKing; + else + return bottomSrc.color != topDst.color && bottomSrc.rank == topDst.rank-1; + } +} + + +- (Player*) checkForWinner +{ + for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) + if( _aces[suit].cards.count < 13 ) + return nil; + return _currentPlayer; +} + + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Piece.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Piece.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,47 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Bit.h" + + +/** A playing piece. A concrete subclass of Bit that displays an image. */ +@interface Piece : Bit +{ + @private + NSString *_imageName; +} + +/** Initialize a Piece from an image file. + imageName can be a resource name from the app bundle, or an absolute path. + If scale is 0.0, the image's natural size will be used. + If 0.0 < scale < 4.0, the image will be scaled by that factor. + If scale >= 4.0, it will be used as the size to scale the maximum dimension to. */ +- (id) initWithImageNamed: (NSString*)imageName + scale: (CGFloat)scale; + +- (void) setImage: (CGImageRef)image scale: (CGFloat)scale; +- (void) setImage: (CGImageRef)image; +- (void) setImageNamed: (NSString*)name; + +@property (copy) NSString* imageName; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Piece.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Piece.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,132 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Piece.h" +#import "QuartzUtils.h" + + +@implementation Piece + + +- (id) initWithImageNamed: (NSString*)imageName + scale: (CGFloat)scale +{ + self = [super init]; + if (self != nil) { + self.imageName = imageName; + [self setImage: GetCGImageNamed(imageName) scale: scale]; + self.zPosition = kPieceZ; + } + return self; +} + + +- (id) initWithCoder: (NSCoder*)decoder +{ + self = [super initWithCoder: decoder]; + if( self ) { + self.imageName = [decoder decodeObjectForKey: @"imageName"]; + // (actual image (self.contents) was already restord by superclass) + } + return self; +} + +- (void) encodeWithCoder: (NSCoder*)coder +{ + [super encodeWithCoder: coder]; + [coder encodeObject: _imageName forKey: @"imageName"]; +} + + +- (void) dealloc +{ + [_imageName release]; + [super dealloc]; +} + + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@[%@]", + [self class], + _imageName.lastPathComponent.stringByDeletingPathExtension]; +} + + +@synthesize imageName=_imageName; + + +- (void) setImage: (CGImageRef)image scale: (CGFloat)scale +{ + self.contents = (id) image; + self.contentsGravity = @"resize"; + self.minificationFilter = kCAFilterLinear; + int width = CGImageGetWidth(image), height = CGImageGetHeight(image); + if( scale > 0 ) { + if( scale >= 4.0 ) + scale /= MAX(width,height); // interpret scale as target dimensions + width = ceil( width * scale); + height= ceil( height* scale); + } + self.bounds = CGRectMake(0,0,width,height); + self.imageName = nil; +} + +- (void) setImage: (CGImageRef)image +{ + CGSize size = self.bounds.size; + [self setImage: image scale: MAX(size.width,size.height)]; +} + +- (void) setImageNamed: (NSString*)name +{ + [self setImage: GetCGImageNamed(name)]; + self.imageName = name; +} + + +- (BOOL)containsPoint:(CGPoint)p +{ + // Overrides CGLayer's implementation, + // returning YES only for pixels at which this layer's alpha is at least 0.5. + // This takes into account the opacity, bg color, and background image's alpha channel. + if( ! [super containsPoint: p] ) + return NO; + float opacity = self.opacity; + if( opacity < 0.5 ) + return NO; + float thresholdAlpha = 0.5 / self.opacity; + + CGColorRef bg = self.backgroundColor; + float alpha = bg ?CGColorGetAlpha(bg) :0.0; + if( alpha < thresholdAlpha ) { + CGImageRef image = (CGImageRef)self.contents; + if( image ) { + // Note: This makes the convenient assumption that the image entirely fills the bounds. + alpha = MAX(alpha, GetPixelAlpha(image, self.bounds.size, p)); + } + } + return alpha >= thresholdAlpha; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/PlayingCard.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/PlayingCard.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,55 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Card.h" + +typedef enum { + kSuitClubs, + kSuitDiamonds, + kSuitHearts, + kSuitSpades +} CardSuit; + +typedef enum { + kColorRed = 0, + kColorBlack = 1 +} CardColor; + +typedef enum { + kRankAce = 1, + kRankJack = 11, + kRankQueen = 12, + kRankKing = 13 +} CardRank; + + +/* A standard Western playing card. Serial numbers go from 1..52. */ +@interface PlayingCard : Card + +@property (readonly) CardSuit suit; +@property (readonly) CardColor color; +@property (readonly) CardRank rank; + +@property (readonly) NSString *suitString, *rankString, *faceSymbol; +@property (readonly) CGColorRef suitColor; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/PlayingCard.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/PlayingCard.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,111 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "PlayingCard.h" +#import "QuartzUtils.h" + + +@implementation PlayingCard + + ++ (NSRange) serialNumberRange; +{ + return NSMakeRange(1,52); +} + + +- (CALayer*) createFront +{ + CALayer *front = [super createFront]; + NSString *name = [NSString stringWithFormat: @"%@%@", + self.rankString, self.suitString]; + + CGColorRef suitColor = self.suitColor; + CATextLayer *label; + label = AddTextLayer(front, name, [NSFont systemFontOfSize: 18], + kCALayerMaxXMargin | kCALayerMinYMargin); + label.foregroundColor = suitColor; + label = AddTextLayer(front, name, [NSFont systemFontOfSize: 18], + kCALayerMaxXMargin | kCALayerMaxYMargin); + label.foregroundColor = suitColor; + label.anchorPoint = CGPointMake(1,1); + [label setValue: [NSNumber numberWithFloat: M_PI] forKeyPath: @"transform.rotation"]; + + label = AddTextLayer(front, self.faceSymbol, [NSFont systemFontOfSize: 80], + kCALayerWidthSizable | kCALayerHeightSizable); + label.foregroundColor = suitColor; + return front; +} + + +- (CardRank) rank {return (self.serialNumber-1)%13 + 1;} +- (CardSuit) suit {return (self.serialNumber-1)/13;} + +- (CardColor) color +{ + CardSuit suit = self.suit; + return suit==kSuitDiamonds || suit==kSuitHearts ?kColorRed :kColorBlack; +} + + +- (NSString*) suitString +{ + return [@"\u2663\u2666\u2665\u2660" substringWithRange: NSMakeRange(self.suit,1)]; +} + +- (NSString*) rankString +{ + CardRank rank = self.rank; + if( rank == 10 ) + return @"10"; + else + return [@"A234567890JQK" substringWithRange: NSMakeRange(rank-1,1)]; +} + +- (CGColorRef) suitColor +{ + static CGColorRef kSuitColor[4]; + if( ! kSuitColor[0] ) { + kSuitColor[0] = kSuitColor[3] = kBlackColor; + kSuitColor[1] = kSuitColor[2] = CGColorCreateGenericRGB(1, 0, 0, 1); + } + return kSuitColor[self.suit]; +} + + +- (NSString*) faceSymbol +{ + int rank = self.rank; + if( rank < kRankJack ) + return self.suitString; + else + return [@"\u265E\u265B\u265A" substringWithRange: NSMakeRange(rank-kRankJack,1)]; // actually chess symbols +} + + +- (NSString*) description +{ + return [NSString stringWithFormat: @"%@[%@%@]",self.class,self.rankString,self.suitString]; +} + + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/QuartzUtils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/QuartzUtils.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,70 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import + + +/** Constants for various commonly used colors. */ +extern CGColorRef kBlackColor, kWhiteColor, + kTranslucentGrayColor, kTranslucentLightGrayColor, + kAlmostInvisibleWhiteColor, + kHighlightColor; + + +/** Moves a layer from one superlayer to another, without changing its position onscreen. */ +void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index ); + +/** Removes a layer from its superlayer without any fade-out animation. */ +void RemoveImmediately( CALayer *layer ); + +/** Convenience for creating a CATextLayer. */ +CATextLayer* AddTextLayer( CALayer *superlayer, + NSString *text, NSFont* font, + enum CAAutoresizingMask align ); + + +/** Loads an image or pattern file into a CGImage or CGPattern. + If the name begins with "/", it's interpreted as an absolute filesystem path. + Otherwise, it's the name of a resource that's looked up in the app bundle. + The image must exist, or an assertion-failure exception will be raised! + Loaded images/patterns are cached in memory, so subsequent calls with the same name + are very fast. + The caller must NOT release the result, since it's retained in the cache. */ +CGImageRef GetCGImageNamed( NSString *name ); +CGColorRef GetCGPatternNamed( NSString *name ); + +/** Loads image data from the pasteboard into a CGImage. */ +CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb ); + +/** Creates a CGPattern from a CGImage. Caller must release it. */ +CGPatternRef CreateImagePattern( CGImageRef image ); + +/** Creates a CGColor that draws the given CGImage as a pattern. Caller must release it. */ +CGColorRef CreatePatternColor( CGImageRef image ); + +/** Returns the alpha value of a single pixel in a CGImage, scaled to a particular size. */ +float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ); + +/** Returns the center point of a CGRect. */ +static inline CGPoint GetCGRectCenter( CGRect rect ) { + return CGPointMake(CGRectGetMidX(rect),CGRectGetMidY(rect)); +} \ No newline at end of file diff -r 000000000000 -r e9f7ba4718e1 Source/QuartzUtils.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/QuartzUtils.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,273 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "QuartzUtils.h" + + +CGColorRef kBlackColor, kWhiteColor, + kTranslucentGrayColor, kTranslucentLightGrayColor, + kAlmostInvisibleWhiteColor, + kHighlightColor; + + +__attribute__((constructor)) // Makes this function run when the app loads +static void InitQuartzUtils() +{ + kBlackColor = CGColorCreateGenericGray(0.0, 1.0); + kWhiteColor = CGColorCreateGenericGray(1.0, 1.0); + kTranslucentGrayColor = CGColorCreateGenericGray(0.0, 0.5); + kTranslucentLightGrayColor = CGColorCreateGenericGray(0.0, 0.25); + kAlmostInvisibleWhiteColor = CGColorCreateGenericGray(1, 0.05); + kHighlightColor = CGColorCreateGenericRGB(1, 1, 0, 0.5); +} + + +void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index ) +{ + // Disable actions, else the layer will move to the wrong place and then back! + [CATransaction flush]; + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue + forKey:kCATransactionDisableActions]; + + CGPoint pos = [newSuperlayer convertPoint: layer.position + fromLayer: layer.superlayer]; + [layer retain]; + [layer removeFromSuperlayer]; + if( index >= 0 ) + [newSuperlayer insertSublayer: layer atIndex: index]; + else + [newSuperlayer addSublayer: layer]; + layer.position = pos; + [layer release]; + + [CATransaction commit]; +} + + +void RemoveImmediately( CALayer *layer ) +{ + [CATransaction flush]; + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue + forKey:kCATransactionDisableActions]; + [layer removeFromSuperlayer]; + [CATransaction commit]; +} + + +CATextLayer* AddTextLayer( CALayer *superlayer, + NSString *text, NSFont* font, + enum CAAutoresizingMask align ) +{ + CATextLayer *label = [[CATextLayer alloc] init]; + label.string = text; + label.font = font; + label.fontSize = font.pointSize; + label.foregroundColor = kBlackColor; + + NSString *mode; + if( align & kCALayerWidthSizable ) + mode = @"center"; + else if( align & kCALayerMinXMargin ) + mode = @"right"; + else + mode = @"left"; + align |= kCALayerWidthSizable; + label.alignmentMode = mode; + + CGFloat inset = superlayer.borderWidth + 3; + CGRect bounds = CGRectInset(superlayer.bounds, inset, inset); + CGFloat height = font.ascender; + CGFloat y = bounds.origin.y; + if( align & kCALayerHeightSizable ) + y += (bounds.size.height-height)/2.0; + else if( align & kCALayerMinYMargin ) + y += bounds.size.height - height; + align &= ~kCALayerHeightSizable; + label.bounds = CGRectMake(0, font.descender, + bounds.size.width, height - font.descender); + label.position = CGPointMake(bounds.origin.x,y+font.descender); + label.anchorPoint = CGPointMake(0,0); + + label.autoresizingMask = align; + [superlayer addSublayer: label]; + [label release]; + return label; +} + + +CGImageRef CreateCGImageFromFile( NSString *path ) +{ + CGImageRef image = NULL; + CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path]; + CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL); + if( src ) { + image = CGImageSourceCreateImageAtIndex(src, 0, NULL); + CFRelease(src); + if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*)); + } + return image; +} + + +CGImageRef GetCGImageNamed( NSString *name ) +{ + // For efficiency, loaded images are cached in a dictionary by name. + static NSMutableDictionary *sMap; + if( ! sMap ) + sMap = [[NSMutableDictionary alloc] init]; + + CGImageRef image = (CGImageRef) [sMap objectForKey: name]; + if( ! image ) { + // Hasn't been cached yet, so load it: + NSString *path; + if( [name hasPrefix: @"/"] ) + path = name; + else { + path = [[NSBundle mainBundle] pathForResource: name ofType: nil]; + NSCAssert1(path,@"Couldn't find bundle image resource '%@'",name); + } + image = CreateCGImageFromFile(path); + NSCAssert1(image,@"Failed to load image from %@",path); + [sMap setObject: (id)image forKey: name]; + CGImageRelease(image); + } + return image; +} + + +CGColorRef GetCGPatternNamed( NSString *name ) // can be resource name or abs. path +{ + // For efficiency, loaded patterns are cached in a dictionary by name. + static NSMutableDictionary *sMap; + if( ! sMap ) + sMap = [[NSMutableDictionary alloc] init]; + + CGColorRef pattern = (CGColorRef) [sMap objectForKey: name]; + if( ! pattern ) { + pattern = CreatePatternColor( GetCGImageNamed(name) ); + [sMap setObject: (id)pattern forKey: name]; + CGColorRelease(pattern); + } + return pattern; +} + + +CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb ) +{ + CGImageSourceRef src = NULL; + NSArray *paths = [pb propertyListForType: NSFilenamesPboardType]; + if( paths.count==1 ) { + // If a file is being dragged, read it: + CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]]; + src = CGImageSourceCreateWithURL(url, NULL); + } else { + // Else look for an image type: + NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]]; + if( type ) { + NSData *data = [pb dataForType: type]; + src = CGImageSourceCreateWithData((CFDataRef)data, NULL); + } + } + if(src) { + CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL); + CFRelease(src); + return image; + } else + return NULL; +} + + +float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ) +{ + // Trivial reject: + if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height ) + return 0.0; + + // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha. + static UInt8 sPixel[1]; + static CGContextRef sTinyContext; + if( ! sTinyContext ) { + sTinyContext = CGBitmapContextCreate(sPixel, 1, 1, + 8, 1, // bpp, rowBytes + NULL, + kCGImageAlphaOnly); + CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy); + } + + // Draw the image into sTinyContext, positioned so the desired point is at + // (0,0), then examine the alpha value in the pixmap: + CGContextDrawImage(sTinyContext, + CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height), + image); + return sPixel[0] / 255.0; +} + + +#pragma mark - +#pragma mark PATTERNS: + + +// callback for CreateImagePattern. +static void drawPatternImage (void *info, CGContextRef ctx) +{ + CGImageRef image = (CGImageRef) info; + CGContextDrawImage(ctx, + CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)), + image); +} + +// callback for CreateImagePattern. +static void releasePatternImage( void *info ) +{ + CGImageRelease( (CGImageRef)info ); +} + + +CGPatternRef CreateImagePattern( CGImageRef image ) +{ + NSCParameterAssert(image); + int width = CGImageGetWidth(image); + int height = CGImageGetHeight(image); + static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage}; + return CGPatternCreate (image, + CGRectMake (0, 0, width, height), + CGAffineTransformMake (1, 0, 0, 1, 0, 0), + width, + height, + kCGPatternTilingConstantSpacing, + true, + &callbacks); +} + + +CGColorRef CreatePatternColor( CGImageRef image ) +{ + CGPatternRef pattern = CreateImagePattern(image); + CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL); + CGFloat components[1] = {1.0}; + CGColorRef color = CGColorCreateWithPattern(space, pattern, components); + CGColorSpaceRelease(space); + CGPatternRelease(pattern); + return color; +} diff -r 000000000000 -r e9f7ba4718e1 Source/Stack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Stack.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,68 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Bit.h" +#import "BitHolder.h" + + +/** A holder for multiple Bits that lines them up in stacks or rows. + For example, this is used in solitaire card games for each pile in the "tableau". */ +@interface Stack : BitHolder +{ + CGPoint _startPos; // see properties below for descriptions + CGSize _spacing; + CGSize _wrapSpacing; + int _wrapInterval; + NSMutableArray *_bits; + BOOL _dragAsStacks; +} + +- (id) initWithStartPos: (CGPoint)startPos spacing: (CGSize)spacing; + +- (id) initWithStartPos: (CGPoint)startPos spacing: (CGSize)spacing + wrapInterval: (int)wrapInterval wrapSpacing: (CGSize)wrapSpacing; + +@property CGPoint startPos; // Position where first Bit should go +@property CGSize spacing; // Spacing between successive Bits +@property CGSize wrapSpacing; // Spacing between wrapped-around sub-piles +@property int wrapInterval; // How many Bits to add before wrapping +@property BOOL dragAsStacks; // If set to YES, dragging a Bit drags a DraggedStack +@property (readonly) NSArray *bits; // The Bits, in order +@property (readonly) Bit *topBit; // The topmost Bit (last item in self.bits) + +/** Adds a Bit to the end */ +- (void) addBit: (Bit*)bit; + +@end + + +/** A subset of a Stack, dragged out of it if its dragAsStacks flag is set. + This is used in typical card solitaire games. + A DraggedStack exists only during a drag; afterwards, its Cards + are incorporated into the destination Stack. */ +@interface DraggedStack : Bit + +- (id) initWithBits: (NSArray*)bits; + +@property (readonly) NSArray *bits; + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/Stack.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Stack.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,185 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Stack.h" +#import "QuartzUtils.h" + + +@implementation Stack + + +- (id) initWithStartPos: (CGPoint)startPos spacing: (CGSize)spacing + wrapInterval: (int)wrapInterval wrapSpacing: (CGSize)wrapSpacing +{ + self = [super init]; + if (self != nil) { + _startPos = startPos; + _spacing = spacing; + _wrapInterval = wrapInterval; + _wrapSpacing = wrapSpacing; + self.cornerRadius = 8; + self.backgroundColor = kAlmostInvisibleWhiteColor; + self.borderColor = kHighlightColor; + _bits = [[NSMutableArray alloc] init]; + } + return self; +} + +- (id) initWithStartPos: (CGPoint)startPos spacing: (CGSize)spacing; +{ + return [self initWithStartPos: startPos spacing: spacing + wrapInterval: INT_MAX wrapSpacing: CGSizeZero]; +} + + +- (void) dealloc +{ + [_bits release]; + [super dealloc]; +} + + +@synthesize spacing=_spacing, wrapSpacing=_wrapSpacing, startPos=_startPos, wrapInterval=_wrapInterval; +@synthesize dragAsStacks=_dragAsStacks; +@synthesize bits=_bits; + + +- (Bit*) topBit +{ + return [_bits lastObject]; +} + + +- (void) dump +{ + printf("Stack = "); + for( CALayer *layer in self.sublayers ) + printf("%s @z=%g ", [[layer description] UTF8String],layer.zPosition); + printf("\n"); +} + + +- (void) x_repositionBit: (Bit*)bit forIndex: (int)i +{ + bit.position = CGPointMake(_startPos.x + _spacing.width *(i%_wrapInterval) + _wrapSpacing.width *(i/_wrapInterval), + _startPos.y + _spacing.height*(i%_wrapInterval) + _wrapSpacing.height*(i/_wrapInterval)); +} + +- (void) addBit: (Bit*)bit +{ + if( [bit isKindOfClass: [DraggedStack class]] ) { + for( Bit *subBit in [(DraggedStack*)bit bits] ) + [self addBit: subBit]; + } else { + int n = _bits.count; + [_bits addObject: bit]; + ChangeSuperlayer(bit, self, n); + [self x_repositionBit: bit forIndex: n]; + } +} + + +- (void) setHighlighted: (BOOL)highlighted +{ + [super setHighlighted: highlighted]; + self.borderWidth = (highlighted ?6 :0); +} + + +- (Bit*) canDragBit: (Bit*)bit +{ + NSInteger index = [_bits indexOfObjectIdenticalTo: bit]; + if( index==NSNotFound ) + return nil; + if( _dragAsStacks && index < _bits.count-1 ) { + // Move bit and those above it into a temporary DraggedStack: + NSRange r = NSMakeRange(index,_bits.count-index); + NSArray *bitsToDrag = [_bits subarrayWithRange: r]; + [_bits removeObjectsInRange: r]; + DraggedStack *stack = [[DraggedStack alloc] initWithBits: bitsToDrag]; + [self addSublayer: stack]; + [stack release]; + stack.anchorPoint = CGPointMake( bit.position.x/stack.bounds.size.width, + bit.position.y/stack.bounds.size.height ); + stack.position = bit.position; + return stack; + } else { + [bit retain]; + [_bits removeObjectIdenticalTo: bit]; + return [bit autorelease]; + } +} + + +- (void) cancelDragBit: (Bit*)bit +{ + [self addBit: bit]; + if( [bit isKindOfClass: [DraggedStack class]] ) { + [bit removeFromSuperlayer]; + } +} + + +- (void) draggedBit: (Bit*)bit to: (id)dst +{ + int i=0; + for( Bit *bit in self.sublayers ) + [self x_repositionBit: bit forIndex: i++]; +} + + +- (BOOL) dropBit: (Bit*)bit atPoint: (CGPoint)point +{ + [self addBit: bit]; + return YES; +} + +@end + + + + +@implementation DraggedStack + + +- (id) initWithBits: (NSArray*)bits +{ + self = [super init]; + if( self ) { + CGRect bounds = CGRectZero; + for( Bit *bit in bits ) { + bounds = CGRectUnion(bounds, bit.frame); + [self addSublayer: bit]; + } + self.bounds = bounds; + self.anchorPoint = CGPointZero; + self.position = CGPointZero; + } + return self; +} + +- (NSArray*) bits +{ + return [self.sublayers.copy autorelease]; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/TicTacToeGame.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/TicTacToeGame.h Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,34 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "Game.h" +@class RectGrid, Dispenser; + + +/** Officially the world's second-lamest board game. + See: http://en.wikipedia.org/wiki/Tic-tac-toe */ +@interface TicTacToeGame : Game { + RectGrid *_grid; + Dispenser *_dispenser[2]; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/TicTacToeGame.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/TicTacToeGame.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,95 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import "TicTacToeGame.h" +#import "Grid.h" +#import "Dispenser.h" +#import "Piece.h" +#import "QuartzUtils.h" + + +@implementation TicTacToeGame + +- (void) x_createDispenser: (NSString*)imageName forPlayer: (int)playerNumber x: (int)x +{ + Piece *p = [[Piece alloc] initWithImageNamed: imageName scale: 80]; + p.owner = [self.players objectAtIndex: playerNumber]; + _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0 + frame: CGRectMake(x,16, 120,120)]; + [_board addSublayer: _dispenser[playerNumber]]; +} + +- (id) initWithBoard: (CALayer*)board +{ + self = [super initWithBoard: board]; + if (self != nil) { + [self setNumberOfPlayers: 2]; + + // Create a 3x3 grid: + CGFloat center = floor(CGRectGetMidX(board.bounds)); + _grid = [[RectGrid alloc] initWithRows: 3 columns: 3 frame: CGRectMake(center-150,16, 300,300)]; + [_grid addAllCells]; + _grid.allowsMoves = _grid.allowsCaptures = NO; + _grid.cellColor = CGColorCreateGenericGray(1.0, 0.25); + _grid.lineColor = kTranslucentLightGrayColor; + [board addSublayer: _grid]; + + // Create piece dispensers for the two players: + [self x_createDispenser: @"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ToolbarUtilitiesFolderIcon.icns" + forPlayer: 0 x: center-290]; + [self x_createDispenser: @"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ToolbarAdvanced.icns" + forPlayer: 1 x: center+170]; + + // And they're off! + [self nextPlayer]; + } + return self; +} + +- (void) nextPlayer +{ + [super nextPlayer]; + // Give the next player another piece to put down: + _dispenser[self.currentPlayer.index].quantity = 1; +} + +static Player* ownerAt( Grid *grid, int index ) +{ + return [grid cellAtRow: index/3 column: index%3].bit.owner; +} + +/** Should return the winning player, if the current position is a win. */ +- (Player*) checkForWinner +{ + static const int kWinningTriples[8][3] = { {0,1,2}, {3,4,5}, {6,7,8}, // rows + {0,3,6}, {1,4,7}, {2,5,8}, // cols + {0,4,8}, {2,4,6} }; // diagonals + for( int i=0; i<8; i++ ) { + const int *triple = kWinningTriples[i]; + Player *p = ownerAt(_grid,triple[0]); + if( p && p == ownerAt(_grid,triple[1]) && p == ownerAt(_grid,triple[2]) ) + return p; + } + return nil; +} + +@end diff -r 000000000000 -r e9f7ba4718e1 Source/main.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/main.m Fri Mar 07 11:43:02 2008 -0800 @@ -0,0 +1,28 @@ +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. + http://developer.apple.com/samplecode/GeekGameBoard/ + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +}