# HG changeset patch # User Jens Alfke # Date 1205705207 25200 # Node ID 428a194e3e59ed2afb59af89f58b36802c53e9ad # Parent af9b2b929b037e11892ad2b104ec4ca053ac80eb Game class now tracks board state and moves, as strings, and can step through its history. Fixed another bug in Go (you could drag your captured stones back to the board!) diff -r af9b2b929b03 -r 428a194e3e59 English.lproj/MainMenu.nib/designable.nib --- a/English.lproj/MainMenu.nib/designable.nib Wed Mar 12 15:51:32 2008 -0700 +++ b/English.lproj/MainMenu.nib/designable.nib Sun Mar 16 15:06:47 2008 -0700 @@ -1,31 +1,31 @@ - + 0 - 9B18 - 629 - 949 - 343.00 + 9C31 + 644 + 949.26 + 352.00 YES - + YES - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin YES - NSApplication + NSApplication FirstResponder - + NSApplication AMainMenu @@ -33,41 +33,41 @@ YES - GeekGameBoard - + GeekGameBoard + 1048576 2147483647 - NSImage + NSImage NSMenuCheckmark - - + + NSImage NSMenuMixedState submenuAction: - + GeekGameBoard YES About GeekGameBoard - + 2147483647 - + YES YES - - + + 1048576 2147483647 - + @@ -76,30 +76,30 @@ 1048576 2147483647 - + YES YES - - + + 1048576 2147483647 - + - Services - + Services + 1048576 2147483647 - + submenuAction: - + Services YES @@ -110,50 +110,50 @@ YES YES - - + + 1048576 2147483647 - + Hide GeekGameBoard - h + h 1048576 2147483647 - + Hide Others - + h 1572864 2147483647 - + Show All - + 1048576 2147483647 - + YES YES - - + + 1048576 2147483647 - + @@ -162,7 +162,7 @@ 1048576 2147483647 - + _NSAppleMenu @@ -170,15 +170,15 @@ - File - + File + 1048576 2147483647 - + submenuAction: - + File YES @@ -188,7 +188,7 @@ 1048576 2147483647 - + @@ -197,29 +197,29 @@ 1048576 2147483647 - + - Open Recent - + Open Recent + 1048576 2147483647 - + submenuAction: - + Open Recent YES Clear Menu - + 1048576 2147483647 - + _NSRecentDocumentsMenu @@ -229,12 +229,12 @@ YES YES - - + + 1048576 2147483647 - + @@ -243,7 +243,7 @@ 1048576 2147483647 - + @@ -252,7 +252,7 @@ 1048576 2147483647 - + @@ -261,26 +261,26 @@ 1179648 2147483647 - + Revert to Saved - + 2147483647 - + YES YES - - + + 1048576 2147483647 - + @@ -289,8 +289,8 @@ 1179648 2147483647 - - + + @@ -299,22 +299,22 @@ 1048576 2147483647 - + - Edit - + Edit + 1048576 2147483647 - + submenuAction: - + Edit YES @@ -324,7 +324,7 @@ 1048576 2147483647 - + @@ -333,18 +333,18 @@ 1179648 2147483647 - + YES YES - - + + 1048576 2147483647 - + @@ -353,7 +353,7 @@ 1048576 2147483647 - + @@ -362,7 +362,7 @@ 1048576 2147483647 - + @@ -371,16 +371,16 @@ 1048576 2147483647 - + Delete - + 1048576 2147483647 - + @@ -389,49 +389,49 @@ 1048576 2147483647 - + YES YES - - + + 1048576 2147483647 - + - Speech - + Speech + 1048576 2147483647 - + submenuAction: - + Speech YES Start Speaking - + 1048576 2147483647 - + Stop Speaking - + 1048576 2147483647 - + @@ -441,55 +441,55 @@ - Game - + Game + 1048576 2147483647 - + submenuAction: - + Game YES Klondike (Solitaire) - 1 + 1 1048840 2147483647 - + YES YES - - + + 1048576 2147483647 - + Checkers - 2 + 2 1048840 2147483647 - + 1 Go - 3 + 3 1048840 2147483647 - + 4 @@ -499,17 +499,17 @@ 1048840 2147483647 - + 2 Tic-Tac-Toe - 5 + 5 1048840 2147483647 - + 3 @@ -517,15 +517,15 @@ - View - + View + 1048576 2147483647 - + submenuAction: - + View YES @@ -535,18 +535,18 @@ 1048576 2147483647 - + YES YES - - + + 1048576 2147483647 - + @@ -555,31 +555,31 @@ 1572864 2147483647 - + Q3VzdG9taXplIFRvb2xiYXLigKY - + 1048576 2147483647 - + - Window - + Window + 1048576 2147483647 - + submenuAction: - + Window YES @@ -589,36 +589,36 @@ 1048576 2147483647 - + Zoom - + 1048576 2147483647 - + YES YES - - + + 1048576 2147483647 - + Bring All to Front - + 1048576 2147483647 - + _NSWindowsMenu @@ -626,15 +626,15 @@ - Help - + Help + 1048576 2147483647 - + submenuAction: - + Help YES @@ -646,9 +646,9 @@ 7 2 - {{57, 175}, {1084, 587}} + {{57, 145}, {1084, 617}} 1946157056 - + GeekGameBoard NSWindow @@ -659,19 +659,43 @@ 274 - {1084, 587} + {{0, 30}, {1084, 587}} - YES DemoBoardView + + + 290 + {{162, 1}, {754, 25}} + + YES + + -2080244224 + 0 + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 0.000000e+00 + 0.000000e+00 + 0.000000e+00 + 2 + 0 + YES + NO + + - {1084, 587} + {1084, 617} - {{0, 0}, {1440, 878}} - + @@ -911,7 +935,7 @@ - startGameFromMenu: + startGameFromMenu: @@ -919,7 +943,7 @@ - + startGameFromMenu: @@ -927,7 +951,7 @@ - + startGameFromMenu: @@ -935,7 +959,7 @@ - + startGameFromMenu: @@ -943,7 +967,7 @@ - + startGameFromMenu: @@ -965,6 +989,14 @@ 400 + + + _turnSlider + + + + 403 + @@ -1037,7 +1069,7 @@ - + 1 217 @@ -1080,7 +1112,7 @@ 75 - + 3 80 @@ -1118,13 +1150,13 @@ 77 - + 5 73 - + 1 79 @@ -1142,7 +1174,7 @@ 74 - + 2 125 @@ -1228,7 +1260,7 @@ YES - + 2 57 @@ -1422,6 +1454,7 @@ YES + @@ -1493,6 +1526,20 @@ + + 401 + + + YES + + + + + + 402 + + + @@ -1580,6 +1627,7 @@ 24.IBPluginDependency 24.ImportedFromIB2 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect 29.IBPluginDependency 29.ImportedFromIB2 29.WindowOrigin @@ -1589,15 +1637,18 @@ 296.editorWindowContentRectSynchronizationRect 297.IBPluginDependency 298.IBPluginDependency + 371.IBEditorWindowLastContentRect 371.IBPluginDependency 371.IBWindowTemplateEditedContentRect 371.NSWindowTemplate.visibleAtLaunch 371.editorWindowContentRectSynchronizationRect + 371.lastResizeAction 372.IBPluginDependency 375.IBPluginDependency 376.IBPluginDependency 377.IBPluginDependency 379.IBPluginDependency + 380.IBEditorWindowLastContentRect 380.IBPluginDependency 380.editorWindowContentRectSynchronizationRect 381.IBPluginDependency @@ -1606,6 +1657,8 @@ 384.IBPluginDependency 387.IBPluginDependency 393.IBPluginDependency + 401.IBPluginDependency + 402.IBPluginDependency 5.IBPluginDependency 5.ImportedFromIB2 56.IBPluginDependency @@ -1643,146 +1696,164 @@ YES - - - - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{497, 828}, {64, 6}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{522, 812}, {146, 23}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{436, 809}, {64, 6}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{271, 653}, {154, 183}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{514, 573}, {167, 43}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{525, 802}, {197, 73}} - + {{79, 825}, {473, 20}} + com.apple.InterfaceBuilder.CocoaPlugin {74, 862} {{85, 834}, {473, 20}} - - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{319, 763}, {234, 73}} - - - - {{117, 199}, {1084, 587}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{85, 58}, {1084, 617}} + com.apple.InterfaceBuilder.CocoaPlugin + {{85, 58}, {1084, 617}} {{117, 199}, {1084, 587}} - - - - - - + + YES + + YES + IBResizeActionFinalFrame + IBResizeActionInitialFrame + + + YES + {{60, 145}, {1084, 617}} + {{60, 175}, {1084, 587}} + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{313, 712}, {205, 113}} + com.apple.InterfaceBuilder.CocoaPlugin {{319, 721}, {205, 113}} - - - - - - - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{97, 651}, {250, 183}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin {{233, 633}, {199, 203}} - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin @@ -1806,7 +1877,7 @@ - 400 + 403 @@ -1816,19 +1887,10 @@ NSView enterFullScreen: - id - - - YES - - YES - - - YES - + id - IBProjectSource + IBProjectSource Source/BoardView.h @@ -1837,290 +1899,21 @@ BoardView startGameFromMenu: - + id - YES - - YES - - - YES - + _turnSlider + NSSlider - + IBProjectSource Source/DemoBoardView.h 0 - ../../GeekGameBoard.xcodeproj + ../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 af9b2b929b03 -r 428a194e3e59 English.lproj/MainMenu.nib/keyedobjects.nib Binary file English.lproj/MainMenu.nib/keyedobjects.nib has changed diff -r af9b2b929b03 -r 428a194e3e59 Source/Bit.m --- a/Source/Bit.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/Bit.m Sun Mar 16 15:06:47 2008 -0700 @@ -35,11 +35,26 @@ return clone; } +- (void) dealloc +{ + [super dealloc]; +} + + @synthesize owner=_owner; - (BOOL) isFriendly {return _owner.friendly;} - (BOOL) isUnfriendly {return _owner.unfriendly;} +/* +- (NSString*) identifier +{ + if( _identifier ) + return _identifier; + // Defaults to just identifying the owner: + return [NSString stringWithFormat: @"p%i", _owner.index+1]; +} +*/ - (CGFloat) scale { diff -r af9b2b929b03 -r 428a194e3e59 Source/BitHolder.m --- a/Source/BitHolder.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/BitHolder.m Sun Mar 16 15:06:47 2008 -0700 @@ -46,8 +46,12 @@ - (void) setBit: (Bit*)bit { if( bit != self.bit ) { - if( bit && _bit ) - [_bit destroy]; + if( _bit ) { + if( bit ) + [_bit destroy]; + else + [_bit removeFromSuperlayer]; + } setObj(&_bit,bit); ChangeSuperlayer(bit,self,-1); } diff -r af9b2b929b03 -r 428a194e3e59 Source/CheckersGame.h --- a/Source/CheckersGame.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/CheckersGame.h Sun Mar 16 15:06:47 2008 -0700 @@ -29,13 +29,8 @@ @interface CheckersGame : Game { int _numPieces[2]; + Grid *_grid; + NSMutableArray *_cells; } -// For subclasses (See HexchequerGame): -- (void) addPieces: (NSString*)imageName - toGrid: (Grid*)grid - forPlayer: (int)playerNum - rows: (NSRange)rows - alternating: (BOOL)alternating; - @end diff -r af9b2b929b03 -r 428a194e3e59 Source/CheckersGame.m --- a/Source/CheckersGame.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/CheckersGame.m Sun Mar 16 15:06:47 2008 -0700 @@ -30,46 +30,34 @@ @implementation CheckersGame -- (void) addPieces: (NSString*)imageName - toGrid: (Grid*)grid - forPlayer: (int)playerNum - rows: (NSRange)rows - alternating: (BOOL)alternating +- (Piece*) pieceForPlayer: (int)playerNum { - 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 { @@ -101,8 +133,12 @@ { Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder; int playerIndex = self.currentPlayer.index; + + if( self.currentMove.length==0 ) + [self.currentMove appendString: src.name]; + [self.currentMove appendString: dst.name]; + BOOL isKing = ([bit valueForKey: @"King"] != nil); - PlaySound(isKing ?@"Funk" :@"Tink"); // "King" a piece that made it to the last row: @@ -154,4 +190,22 @@ } +- (BOOL) applyMoveString: (NSString*)move +{ + int length = move.length; + if( length<4 || (length&1) ) + return NO; + GridCell *src = nil; + for( int i=0; i 0 ) + if( ! [self animateMoveFrom: src to: dst] ) + return NO; + src = dst; + } + return YES; +} + + @end diff -r af9b2b929b03 -r 428a194e3e59 Source/DemoBoardView.h --- a/Source/DemoBoardView.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/DemoBoardView.h Sun Mar 16 15:06:47 2008 -0700 @@ -28,6 +28,7 @@ @interface DemoBoardView : BoardView { CATextLayer *_headline; + IBOutlet NSSlider *_turnSlider; } - (IBAction) startGameFromMenu: (id)sender; diff -r af9b2b929b03 -r 428a194e3e59 Source/DemoBoardView.m --- a/Source/DemoBoardView.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/DemoBoardView.m Sun Mar 16 15:06:47 2008 -0700 @@ -43,7 +43,7 @@ @"TicTacToeGame", @"GoGame"}; /** Class name of the current game. */ -static NSString* sCurrentGameName = @"KlondikeGame"; +static NSString* sCurrentGameName = @"CheckersGame"; - (void) startGameNamed: (NSString*)gameClassName @@ -89,6 +89,8 @@ alignment: kCALayerWidthSizable | kCALayerMinYMargin]; [self startGameNamed: sCurrentGameName]; + + [_turnSlider bind: @"value" toObject: self withKeyPath: @"game.currentTurn" options: nil]; } @@ -106,6 +108,10 @@ { Game *game = self.game; if( object == game ) { + NSLog(@"maxTurn = %u, currentTurn=%u", self.game.maxTurn,self.game.currentTurn); + _turnSlider.maxValue = self.game.maxTurn; + _turnSlider.numberOfTickMarks = self.game.maxTurn+1; + Player *p = game.winner; NSString *msg; if( p ) { @@ -114,6 +120,7 @@ } else { p = game.currentPlayer; msg = @"Your turn, %@"; + NSLog(@"Game state = '%@'", self.game.stateString); } _headline.string = [NSString stringWithFormat: msg, p.name]; } diff -r af9b2b929b03 -r 428a194e3e59 Source/GGBLayer.h --- a/Source/GGBLayer.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/GGBLayer.h Sun Mar 16 15:06:47 2008 -0700 @@ -14,18 +14,24 @@ @interface GGBLayer : CALayer +{ + CABasicAnimation *_curAnimation; -#if TARGET_OS_ASPEN +#if ! TARGET_OS_ASPEN +} +#else // For some reason, the CALayer class on iPhone OS doesn't have these! -{ CGFloat _cornerRadius, _borderWidth; CGColorRef _borderColor, _realBGColor; unsigned int _autoresizingMask; } + @property CGFloat cornerRadius, borderWidth; @property CGColorRef borderColor; #endif - (void) redisplayAll; +- (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration; + @end diff -r af9b2b929b03 -r 428a194e3e59 Source/GGBLayer.m --- a/Source/GGBLayer.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/GGBLayer.m Sun Mar 16 15:06:47 2008 -0700 @@ -30,6 +30,43 @@ } +/* +- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key +{ + NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key); + [super addAnimation: anim forKey: key]; +} +*/ + + +- (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration +{ + //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08 + CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath]; + anim.duration= duration; + anim.fromValue = from; + anim.toValue = to; + anim.isRemovedOnCompletion = YES; + anim.delegate = self; + [self addAnimation:anim forKey: @"animateAndBlock:"]; + _curAnimation = (id)[self animationForKey: @"animateAndBlock:"]; + [self setValue: to forKeyPath: keyPath]; // animation doesn't update the property value + + // Now wait for it to finish: + while( _curAnimation ) { + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode + beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]]; + } +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag +{ + if( anim==_curAnimation ) { + _curAnimation = nil; + } +} + + #if TARGET_OS_ASPEN #pragma mark - diff -r af9b2b929b03 -r 428a194e3e59 Source/GGBUtils.h --- a/Source/GGBUtils.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/GGBUtils.h Sun Mar 16 15:06:47 2008 -0700 @@ -30,5 +30,6 @@ void setObjCopy( id *variable, id newValue ); +void PreloadSound( NSString* name ); void PlaySound( NSString* name ); void Beep( void ); \ No newline at end of file diff -r af9b2b929b03 -r 428a194e3e59 Source/GGBUtils.m --- a/Source/GGBUtils.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/GGBUtils.m Sun Mar 16 15:06:47 2008 -0700 @@ -42,6 +42,17 @@ } +void PreloadSound( NSString* name ) +{ +#if ! TARGET_OS_ASPEN + NSSound *sound = [[NSSound soundNamed: @"Pop"] copy]; + sound.volume = 0; + [sound play]; + [sound release]; +#endif +} + + void PlaySound( NSString* name ) { #if TARGET_OS_ASPEN diff -r af9b2b929b03 -r 428a194e3e59 Source/Game.h --- a/Source/Game.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/Game.h Sun Mar 16 15:06:47 2008 -0700 @@ -20,7 +20,7 @@ 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. */ -@class GGBLayer, Bit, Player; +@class GGBLayer, Bit, BitHolder, Player; @protocol BitHolder; @@ -30,6 +30,9 @@ GGBLayer *_board; NSArray *_players; Player *_currentPlayer, *_winner; + NSMutableString *_currentMove; + NSMutableArray *_states, *_moves; + unsigned _currentTurn; } /** Returns the human-readable name of this game. @@ -41,6 +44,13 @@ @property (readonly, copy) NSArray *players; @property (readonly) Player *currentPlayer, *winner; +@property (readonly) NSArray *states, *moves; +@property (readonly) unsigned maxTurn; +@property unsigned currentTurn; +@property (readonly) BOOL isLatestTurn; + +- (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst; + // Methods for subclasses to implement: @@ -48,6 +58,7 @@ it should add the necessary Grids, Pieces, Cards, Decks etc. to the board. */ - (id) initWithBoard: (GGBLayer*)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; @@ -56,6 +67,7 @@ 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. @@ -76,11 +88,18 @@ - (Player*) checkForWinner; +@property (copy) NSString* stateString; +- (BOOL) applyMoveString: (NSString*)move; + + // Protected methods for subclasses to call: /** Sets the number of players in the game. Subclass initializers should call this. */ - (void) setNumberOfPlayers: (unsigned)n; +/** The current move in progress. Append text to it as the user makes moves. */ +@property (readonly) NSMutableString* currentMove; + /** Advance to the next player, when a turn is over. */ - (void) nextPlayer; diff -r af9b2b929b03 -r 428a194e3e59 Source/Game.m --- a/Source/Game.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/Game.m Sun Mar 16 15:06:47 2008 -0700 @@ -22,6 +22,8 @@ */ #import "Game.h" #import "Bit.h" +#import "BitHolder.h" +#import "QuartzUtils.h" @interface Game () @@ -46,6 +48,9 @@ { self = [super init]; if (self != nil) { + _states = [[NSMutableArray alloc] init]; + _moves = [[NSMutableArray alloc] init]; + _currentMove = [[NSMutableString alloc] init]; _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.) @@ -60,11 +65,15 @@ { [_board release]; [_players release]; + [_currentMove release]; + [_states release]; + [_moves release]; [super dealloc]; } -@synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner; +@synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner, + currentMove=_currentMove, states=_states, moves=_moves; - (void) setNumberOfPlayers: (unsigned)n @@ -82,13 +91,35 @@ } +- (void) addToMove: (NSString*)str; +{ + [_currentMove appendString: str]; +} + + +- (BOOL) _rememberState +{ + if( self.isLatestTurn ) { + [_states addObject: self.stateString]; + return YES; + } else + return NO; +} + + - (void) nextPlayer { + BOOL latestTurn = [self _rememberState]; if( ! _currentPlayer ) { NSLog(@"*** The %@ Begins! ***", self.class); self.currentPlayer = [_players objectAtIndex: 0]; } else { self.currentPlayer = _currentPlayer.nextPlayer; + if( latestTurn ) { + [self willChangeValueForKey: @"currentTurn"]; + _currentTurn++; + [self didChangeValueForKey: @"currentTurn"]; + } } NSLog(@"Current player is %@",_currentPlayer); } @@ -96,16 +127,88 @@ - (void) endTurn { - NSLog(@"--- End of turn"); + NSLog(@"--- End of turn (move was '%@')", _currentMove); + if( self.isLatestTurn ) { + [self willChangeValueForKey: @"maxTurn"]; + [_moves addObject: [[_currentMove copy] autorelease]]; + [_currentMove setString: @""]; + [self didChangeValueForKey: @"maxTurn"]; + } + Player *winner = [self checkForWinner]; if( winner ) { NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner); + [self _rememberState]; self.winner = winner; } else [self nextPlayer]; } +- (unsigned) maxTurn +{ + return _moves.count; +} + +- (unsigned) currentTurn +{ + return _currentTurn; +} + +- (void) setCurrentTurn: (unsigned)turn +{ + NSParameterAssert(turn<=self.maxTurn); + if( turn != _currentTurn ) { + if( turn==_currentTurn+1 ) { + [self applyMoveString: [_moves objectAtIndex: _currentTurn]]; + } else { + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue + forKey:kCATransactionDisableActions]; + self.stateString = [_states objectAtIndex: turn]; + [CATransaction commit]; + } + _currentTurn = turn; + self.currentPlayer = [_players objectAtIndex: (turn % _players.count)]; + } +} + + +- (BOOL) isLatestTurn +{ + return _currentTurn == MAX(_states.count,1)-1; +} + + +- (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst +{ + if( src==nil || dst==nil || dst==src ) + return NO; + Bit *bit = [src canDragBit: src.bit]; + if( ! bit || ! [dst canDropBit: bit atPoint: GetCGRectCenter(dst.bounds)] + || ! [self canBit: bit moveFrom: src to: dst] ) + return NO; + + ChangeSuperlayer(bit, _board.superlayer, -1); + bit.pickedUp = YES; + dst.highlighted = YES; + [bit performSelector: @selector(setPickedUp:) withObject:nil afterDelay: 0.15]; + CGPoint endPosition = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: bit.superlayer]; + [bit animateAndBlock: @"position" + from: [NSValue valueWithPoint: NSPointFromCGPoint(bit.position)] + to: [NSValue valueWithPoint: NSPointFromCGPoint(endPosition)] + duration: 0.25]; + dst.bit = bit; + dst.highlighted = NO; + bit.pickedUp = NO; + + [src draggedBit: bit to: dst]; + [self bit: bit movedFrom: src to: dst]; + src = dst; + return YES; +} + + #pragma mark - #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN: @@ -148,6 +251,11 @@ } +- (NSString*) stateString {return @"";} +- (void) setStateString: (NSString*)s { } + +- (BOOL) applyMoveString: (NSString*)move {return NO;} + @end diff -r af9b2b929b03 -r 428a194e3e59 Source/GoGame.m --- a/Source/GoGame.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/GoGame.m Sun Mar 16 15:06:47 2008 -0700 @@ -87,6 +87,7 @@ [_captured[1] release]; [self nextPlayer]; + PreloadSound(@"Pop"); } return self; } @@ -104,9 +105,15 @@ } +- (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder +{ + return (srcHolder==nil); +} + + - (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder to: (id)dstHolder { - if( ! [dstHolder isKindOfClass: [Square class]] ) + if( srcHolder!=nil || ! [dstHolder isKindOfClass: [Square class]] ) return NO; Square *dst=(Square*)dstHolder; diff -r af9b2b929b03 -r 428a194e3e59 Source/Grid.h --- a/Source/Grid.h Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/Grid.h Sun Mar 16 15:06:47 2008 -0700 @@ -67,6 +67,7 @@ /** Removes a particular cell, leaving a blank space. */ - (void) removeCellAtRow: (unsigned)row column: (unsigned)col; +- (GridCell*) cellWithName: (NSString*)identifier; // protected: - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col diff -r af9b2b929b03 -r 428a194e3e59 Source/Grid.m --- a/Source/Grid.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/Grid.m Sun Mar 16 15:06:47 2008 -0700 @@ -124,10 +124,11 @@ - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col suggestedFrame: (CGRect)frame { - return [[[_cellClass alloc] initWithGrid: self + GridCell *cell = [[_cellClass alloc] initWithGrid: self row: row column: col - frame: frame] - autorelease]; + frame: frame]; + cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),col]; + return [cell autorelease]; } @@ -172,6 +173,16 @@ } +- (GridCell*) cellWithName: (NSString*)name +{ + for( CALayer *layer in self.sublayers ) + if( [layer isKindOfClass: [GridCell class]] ) + if( [name isEqualToString: ((GridCell*)layer).name] ) + return (GridCell*)layer; + return nil; +} + + #pragma mark - #pragma mark DRAWING: diff -r af9b2b929b03 -r 428a194e3e59 Source/HexchequerGame.m --- a/Source/HexchequerGame.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/HexchequerGame.m Sun Mar 16 15:06:47 2008 -0700 @@ -32,18 +32,31 @@ - (Grid*) x_makeGrid { - HexGrid *grid = [[[HexGrid alloc] initWithRows: 9 columns: 9 frame: _board.bounds] autorelease]; + HexGrid *grid = [[HexGrid alloc] initWithRows: 9 columns: 9 frame: _board.bounds]; + _grid = grid; CGPoint pos = grid.position; pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2); grid.position = pos; - [grid addCellsInHexagon]; grid.allowsMoves = YES; grid.allowsCaptures = NO; // no land-on captures, that is grid.cellColor = CreateGray(1.0, 0.25); grid.lineColor = kTranslucentLightGrayColor; - [self addPieces: @"Green Ball.png" toGrid: grid forPlayer: 0 rows: NSMakeRange(0,2) alternating: NO]; - [self addPieces: @"Red Ball.png" toGrid: grid forPlayer: 1 rows: NSMakeRange(7,2) alternating: NO]; + [grid addCellsInHexagon]; + for( int y=0; y<9; y++ ) { + for( int x=0; x<9; x++ ) { + GridCell *cell = [_grid cellAtRow: y column: x]; + if( cell ) + [_cells addObject: cell]; + } + } + self.stateString = @"111111111111111111-------------------------222222222222222222"; + + [self performSelector: @selector(applyMoveString:) withObject: @"C4D4" afterDelay: 2.0]; + [self performSelector: @selector(applyMoveString:) withObject: @"G3F3" afterDelay: 5.0]; + [self performSelector: @selector(applyMoveString:) withObject: @"D4E4" afterDelay: 8.0]; + [self performSelector: @selector(applyMoveString:) withObject: @"F3D4" afterDelay: 11.0]; + return grid; } @@ -64,8 +77,12 @@ { Hex *src=(Hex*)srcHolder, *dst=(Hex*)dstHolder; int playerIndex = self.currentPlayer.index; - BOOL isKing = ([bit valueForKey: @"King"] != nil); + + if( self.currentMove.length==0 ) + [self.currentMove appendString: src.name]; + [self.currentMove appendString: dst.name]; + BOOL isKing = ([bit valueForKey: @"King"] != nil); PlaySound(isKing ?@"Funk" :@"Tink"); // "King" a piece that made it to the last row: diff -r af9b2b929b03 -r 428a194e3e59 Source/QuartzUtils.m --- a/Source/QuartzUtils.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/QuartzUtils.m Sun Mar 16 15:06:47 2008 -0700 @@ -76,11 +76,11 @@ pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer]; [layer retain]; [layer removeFromSuperlayer]; + layer.position = pos; if( index >= 0 ) [newSuperlayer insertSublayer: layer atIndex: index]; else [newSuperlayer addSublayer: layer]; - layer.position = pos; [layer release]; [CATransaction commit]; diff -r af9b2b929b03 -r 428a194e3e59 Source/TicTacToeGame.m --- a/Source/TicTacToeGame.m Wed Mar 12 15:51:32 2008 -0700 +++ b/Source/TicTacToeGame.m Sun Mar 16 15:06:47 2008 -0700 @@ -29,21 +29,13 @@ @implementation TicTacToeGame -- (void) x_createDispenser: (NSString*)imageName forPlayer: (int)playerNumber +- (Piece*) pieceForPlayer: (int)playerNumber { - Piece *p = [[Piece alloc] initWithImageNamed: imageName scale: 80]; + Piece *p = [[Piece alloc] initWithImageNamed: (playerNumber ? @"O.tiff" :@"X.tiff") + scale: 80]; p.owner = [self.players objectAtIndex: playerNumber]; - CGFloat x = floor(CGRectGetMidX(_board.bounds)); -#if TARGET_OS_ASPEN - x = x - 80 + 160*playerNumber; - CGFloat y = 360; -#else - x += (playerNumber==0 ?-230 :230); - CGFloat y = 175; -#endif - _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0 - frame: CGRectMake(x-45,y-45, 90,90)]; - [_board addSublayer: _dispenser[playerNumber]]; + p.name = (playerNumber ?@"O" :@"X"); + return [p autorelease]; } - (id) initWithBoard: (GGBLayer*)board @@ -62,8 +54,20 @@ [board addSublayer: _grid]; // Create piece dispensers for the two players: - [self x_createDispenser: @"X.tiff" forPlayer: 0]; - [self x_createDispenser: @"O.tiff" forPlayer: 1]; + for( int playerNumber=0; playerNumber<=1; playerNumber++ ) { + Piece *p = [self pieceForPlayer: playerNumber]; + CGFloat x = floor(CGRectGetMidX(_board.bounds)); +#if TARGET_OS_ASPEN + x = x - 80 + 160*playerNumber; + CGFloat y = 360; +#else + x += (playerNumber==0 ?-230 :230); + CGFloat y = 175; +#endif + _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0 + frame: CGRectMake(x-45,y-45, 90,90)]; + [_board addSublayer: _dispenser[playerNumber]]; + } // And they're off! [self nextPlayer]; @@ -71,6 +75,34 @@ return self; } + +- (NSString*) stateString +{ + unichar str[10]; + for( int i=0; i<9; i++ ) { + NSString *ident = [_grid cellAtRow: i/3 column: i%3].bit.name; + if( ident==nil ) + str[i] = '-'; + else + str[i] = [ident characterAtIndex: 0]; + } + return [NSString stringWithCharacters: str length: 9]; +} + +- (void) setStateString: (NSString*)stateString +{ + for( int i=0; i<9; i++ ) { + Piece *piece; + switch( [stateString characterAtIndex: i] ) { + case 'X': case 'x': piece = [self pieceForPlayer: 0]; break; + case 'O': case 'o': piece = [self pieceForPlayer: 1]; break; + default: piece = nil; break; + } + [_grid cellAtRow: i/3 column: i%3].bit = piece; + } +} + + - (Bit*) bitToPlaceInHolder: (id)holder { if( holder.bit==nil && [holder isKindOfClass: [Square class]] ) @@ -79,6 +111,15 @@ return nil; } + +- (void) bit: (Bit*)bit movedFrom: (id)src to: (id)dst +{ + Square *square = (Square*)dst; + int squareIndex = 3*square.row + square.column; + [self.currentMove appendFormat: @"%@%i", bit.name, squareIndex]; + [super bit: bit movedFrom: src to: dst]; +} + - (void) nextPlayer { [super nextPlayer];