#pragma rtGlobals=1 // Use modern global access method. #pragma ModuleName = IgorGames // Familiar Old Games in Igor Pro. For Pegagogical purposes. Really. #pragma IgorVersion = 6 #pragma version = 1 //Last Modified 2015/04/20 by Jamie Boyd //************************************************************************ //****************************MINE SWEEPER********************************* //************************************************************************ // As known and loved from MS Windows circa version 3 // MineSweeper demonstrates: // 1) Use of procedure pictures with custom controls // 2) Getting and Setting user data using a custom structure // 3) A Recursively called function // 4) checking bits with bitwise "and" (&) and setting bits with bitwise "or" (|) // 5) Using preferences to store data, in this case best times // 6) Use of a background task, which runs a timer and starts a new game when requested //************************************************************************ // menu items for Igor Games go under the Misc menu Menu "Misc" SubMenu "Igor Games" SubMenu "Mine Sweeper" "8 x 8, 10 mines", /Q, Mines_StartMineSweeper (8, 8, 10, 1) "16 x 16, 40 mines", /Q, Mines_StartMineSweeper (16, 16, 40, 2) "30 x 16, 99 mines", /Q, Mines_StartMineSweeper (30, 16, 99, 3) "Custom...", /Q,Mines_CustomSizer (8, 8, 10) "Show High Scores", /Q, Mines_ShowHighScores () end end end //************************************************************************ // Picture of tiles for custom controls in MineSweeper game. Made up of 18 panels, arranged in a horizontal strip // Each panel is 24 x 24 pixels and shows the custom control in the following state // 0 = blank square with some subtle shading. This is the way all aquares start // 1= blank square as above with some emphasis for use with with mouse down (click-in-progress) // 2 = opened, inactive square with 0 neighbors (solid blank) // 3 = opened, inactive square labeled with with 1 in blue (1 neighbor has a mine) // 4 = opened, inactive square labeled with with 2 in blue/green (2 neighbors have mines) // 5= opened, inactive square labeled with with 3 in green (3 neighbors have mines) // 6= opened, inactive square labeled with with 4 in yellow/green (4 neighbors have mines) // 7= opened, inactive square labeled with with 5 in brown (5 neighbors have mines) // 8= opened, inactive square labeled with with 6 in orange (6 neighbors have mines) // 9= opened, inactive square labeled with with 7 in red (7 neighbors have mines) // 10= opened, inactive square labeled with with 8 in magenta (8 neighbors have mines) // 11 = square labeled with 9 in french puce (for completeness, not used by MineSweeper, as you can't have 9 neighbors, // but we could use the same tiles for other games that need 1-9 , e.g., sudoku) // 12 = opened, unexploded bomb, as shown at end of game // 13 = opened, exploded bomb. Ends the game // 14= Flagged square // 15 = Flagged square as above with some ephasis for use with with mouse down (click-in-progress) // 16= correctly flagged bomb, shown at end of game // 17 = incorrectly flagged bomb, shown at end of game STATIC CONSTANT kBlankTile = 0 STATIC CONSTANT kBlankMouseDown = 1 STATIC CONSTANT kOpen0 =2 // Opened tile with 0 neigbors // opened tiles with 1 to 9 neigbors go from 3 to 11 STATIC CONSTANT kOpenBomb = 12 STATIC CONSTANT kExplodedBomb = 13 STATIC CONSTANT kFlaggedTile = 14 STATIC CONSTANT kFlaggedMouseDown = 15 STATIC CONSTANT kFlaggedGood =16 STATIC CONSTANT kFlaggedBad = 17 STATIC CONSTANT kNumTiles = 18 // PNG: width= 432, height= 24 STATIC Picture Mines_Tiles ASCII85Begin M,6r;%14!\!!!!.8Ou6I!!!&(!!!!9#Qau+!#-,38cShk#[q4B9E5%m!<<*"!44?BTE"rl)J3JY9E5 %m!!!!"YQ+Y'(]XO9zz!!!!fau[NB!"OI?$n;-Tl eU\54G<^d;UP\;nQ&HlR]MHWFW$',,g.L2$Q#E"TJE>-PXJIom)8-34`?*_(6G*C`mZe90gh)i0Zf? )7MG@l"&h&c9:/tAJZmsOm(]?1"jn'(-mg)a\..&K">#mgq98p"MRL^)OA%hOSSPSGB'7 9L`_PulH_l4`tW!L8Q!6$tcp%4\@s#DaWP66AfA"N6mn#%u6dB<>:@!nD7/8^@d<%GU8h![E0"k`d2 $os2OS+A(leG%YB;W)R)#T;YQHp]0elqiL*rX^^:@FAPijpd!p3%f&qWM1B7X'r3!$.p&l1LBLfF!H [fuJE-8&1k\-T-]:"jJ_2PYquPe5&\CBjFAis2Jml-tpQUp7$Kacl20O1nEN\^%'+N]b>>FMW@Jb-b 43LeXp=AWS4]Tk'CCT#'WN42J8sheJ?1;rZ!L7*6@qoedgZlQ.u;0P?D =0J@nR#>NGV$]8G7Mc@6%=CUjNpQns*8bCCJFhc)5qso^3<#_up,qe6S-+Ske/qYDk[S@lNX=d>#U? s@pRMX?0*D%3A:iXXT(FFhJ&)a+)eWdjB.TH6+O(N8SQ%O=9F8G+?*fof90RAcqOiG8AdnK9L]pn77>IBY)>qU=IjJ:Jc;`(:1Xa+lt]"@Q IC^4#Zh<.G0>@W/40nR-D=Y7V3e&G[@!4r4fe@TJ:L4qQ2lmGQS@52Hf`#[W/h`#"H*Ls7H]X/,=>R _"8(C?"iG5V125X-2Pm@F3lBo68N1].C:'CIZk:L\9*9 Cd*CfEr=d&jtn(BKM$m2TP?$&'/m/(-V+CS?HV$#YrB\#$m/Ui\I#Lm5j<=TUD o5<]VfF<0o,T<*`&Goug14_uW)$*Sg0/4[_QW F;Kc=RqX]m[TGDS?DW0raYbWU]WQS1Q=aEoaihSXnjRM#,CpdMl&2^>&H5!+KHk(P:%4I%5PL1)o<8 #o:3DNXoLqF`m]YGi.T(QF8\C52L![JA75#,OfZFFF[L;hVd_;rP7nfeCV>L>RrHjd"X$r*(gQ=VDn &u,?*TY-V3Ot:jaAp"b$.[K-8(G*SX)GL>mdJA4`n(c^Qj(*J.>eVJl\jIXE7=3D;hr,Td5HU!>/H).N-]h6pY?Xqm'=I_>c Bp42k4hYk^LHeX0R$=nH<;'6eOg@+"lD,l;TqY[2#`0S*1LAbnX,=I"(m=54i$+?LiNK'S@OGNXXZ4 OG;bGkR-RV[nao\4:TF8%r'e9PTjSRCl-9a)N4sZ"OOHB_Pq$>DFr*e2nNNE5])_hFCEDmc4\I+5 c^cVec/2]joCi1X+8bi4=FeAlk9k"KNf!M8RIgc:hKNj]PjTMTZGi.gNN%27SGd93Uj/!fe^]5\3<+ -QflXeZo!7LsSd1Z&.K0Xi)pR6o]S#m0'Hd<3_?GX9&*TgBB@kJea(;8IMOYhm5-2EB AI4Tf8ikc!%J#=3K>0ac@>#n!52UsJ0[XSp54@&)p$9(-^#FN"Koe$P7"9!eH=9uhgD'Q-'eE=Xdf4 UK#K]D=cpYL(9a["_`-`?I: ^C:,1crmfW78rhfc.@l@"ADpjI30q7LVF#Nr:r9/NrnU1/XHtN9RA;nKD'^t'X]*Y^f\*f7uWquSo. IkWN1$ddPic0t7=n^`tUE(m#aWgO?G<$iGm%Lb@^E*m'0hc\c\O\nD+>R]6<9Ie'n'0#SC4AtT=`rp H@S3bggCkNe(m'Tr9.:sa!5'_^r:s+A'T;co TdSl&?n-8$Eq/lB*rCO]^%<0#hOs^U(=.SR%[[VkOp#Y%*O:GkSoP.(l/(X_SO;j5'VLAj)N%jj8N8 'WBR^c`9@/If3/'+Or;+j%MCI;/rRAY"1Md$^YK@CDgbT*Z.FAfe;*&TmJ;MA2`t5iBOf_.De:@lK?M.GL%rq-+7H*&bamj\2n7BW4Q!72Vd@8WU=]ERfJ+X/0 E9P$b^OiW$rRi%u<`tAJU#FtH^Oq/XRm7$N9o&H]FUo[nl325C.]uRD D7oDJAfe,HAV0r%>+JZ<@O-?3"#e['hXb,>$23V]DG jg=H3t34j28((^2Rmn`m<@q4qsW;DpDpDME*cgk(]U9@JDf& G&P[LJY^J=\E!MEk=.".;LM"Q)W4E4]5SoB$o'rKI$ajC5Pq])he\pc_F="R"7AM;-es%t1K"^I1m9 iIP^#_t?#!\l4'G$ahJpANlEOmHL7-_3dfl,(M^G[>l;->"kQ.JIt)0QL6= L@2$We4sUlOfrMuWhX! (fUS7'8jaJc ASCII85End End //************************************************************************ // definition of a structure to hold information about each tile // Each tile has a structure containing needed information Static Structure Mines_TileInfo Int32 xPos // from 0 to nX-1 Int32 yPos // from 0 to nY -1 Int32 neighbors // number of neighbors containing mines (0-8) or -1 if this tile is itself mined int32 State // 0 if unflagged and unopened, 1 if flagged, 2 if opened, 6 if exploded Int32 mouseDown // set to 1 if mouse was previously set down in control. set to 2 for a right click EndStructure // constants for Tile State STATIC CONSTANT kTileBlank =0 STATIC CONSTANT kTileFlagged = 1 STATIC CONSTANT kTIleOpened =2 STATIC CONSTANT kTIleFlaggedAndOpened = 3 // bitwise combo of 1 and 2 STATIC CONSTANT kTileExploded = 6 // note that bit 1 (opened) is set. Can't be exploded without being opened. //************************************************************************ Function Mines_CustomSizer (xSize, ySIze, nMines) variable xSize, ySize, nMines prompt xSize, "Number of Rows:" prompt ySize, "Number of Columns" prompt nMines, "Number of Mines" doPrompt /Help = "Creates a custom sized Mine Field" "Custom Size", xSize, ySize, nMines if (V_Flag) return 1 endif variable err err = Mines_StartMineSweeper (xSize, ySize, nMines, 0) if (err) NVAR nXG = root:packages:IgorGames:Mines:NX NVAR nYG = root:packages:IgorGames:Mines:NY NVAR nMinesG = root:packages:IgorGames:Mines:Nmines Mines_CustomSizer (xSize, ySIze, nMinesG) endif end //************************************************************************ function Mines_StartMineSweeper (nX, nY, nMines, gameType) variable nX, nY, nMines, gameType // make and/or reference globals Mines_MakeGlobals () NVAR gameTypeG = root:packages:IgorGames:Mines:gameType gameTypeG = gameType NVAR nXG = root:packages:IgorGames:Mines:NX NVAR nYG = root:packages:IgorGames:Mines:NY NVAR nMinesG = root:packages:IgorGames:Mines:Nmines // Sanity check variable suggestedMines if (nX * nY <= nMines) suggestedMines = round (0.15 * nX * nY) doAlert 0, "You can't fit " + num2str (nMines) + " mines in a " + num2str (nx) + " by " + num2str (ny) + " matrix of tiles. Try maybe " + num2str (suggestedMines) + " mines." nMinesG = suggestedMines return 1 endif // num flagged is provided for user to count flags user has placed (rightly or wrongly) NVAR nFlagged = root:packages:IgorGames:Mines:NfLagged nFlagged =0 // num covered counts down the number of tiles not containing mines that are covered. // When it reaches 0, the game is won NVAR nCcovered = root:packages:IgorGames:Mines:Ncovered nCcovered = (nX * nY) - nMines // make or resize panel variable delOldTilesX = 0, delOldTilesY=0 doWindow/F MineSweeperPanel if ((V_Flag ==0) || ((nx!=nXG) || (nY != nYG))) variable panelXsize= nX * 25 + 4 variable panelYsize= nY * 25 + 26 if (V_Flag ) // window exists but is of different size if (nx < nXG) delOldTilesX =1 endif if (nY < nYG) delOldTIlesY =1 endif getWindow MineSweeperPanel wsizeOuter variable winKludge = (72/screenResolution) MoveWindow/W=MineSweeperPanel V_Left, V_Top, V_Left + (panelXsize * winKludge) ,V_Top +(panelYsize * winKludge) else // window does not exists NewPanel/K=1/N=MineSweeperPanel/W=(100,100, 100 +panelXsize ,100 +panelYsize) as "Mine Sweeper" ModifyPanel /w= MineSweeperPanel fixedSIze = 1 // Setvariable for number of flags user has placed SetVariable FlaggedSetvar win = MineSweeperPanel,pos={4,1},size={151,20},title="Flags:", frame=0, fsize = 14 SetVariable Flaggedsetvar win = MineSweeperPanel, format="%d (of " + num2str (nMines) + ")" SetVariable Flaggedsetvar win = MineSweeperPanel,limits={0,inf,0},value= root:packages:IgorGames:Mines:NfLagged,noedit= 1 // Setvariable to show elapsed time SetVariable TimerSetvar win = MineSweeperPanel,pos={140,1},size={60,24},title=" ",fSize=14 SetVariable TimerSetvar win = MineSweeperPanel,value= root:packages:IgorGames:Mines:TimeString,noedit= 1,frame=0 endif endif if (nMines != nMinesG) SetVariable FlaggedSetvar win = MineSweeperPanel, format="%d (of " + num2str (nMines) + ")" endif // Add tiles as custom controls variable iX, iY string tileName // set constant bits of structure STRUCT Mines_TileInfo TileInfo TileInfo.state =0 TIleInfo.mouseDown = 0 TileInfo.Neighbors=0 string userDataStr for (iy =0; iY < nY; iY +=1) for (ix = 0;iX < nX;iX +=1) tileName = "Tile" + num2str (ix) + "_" + num2str (iy) CustomControl $tileName win = MineSweeperPanel, pos = {2 + iX * 25, 24 + iY * 25}, proc=IgorGames#Mines_TileProc ,picture= {IgorGames#Mines_Tiles, kNumTiles} // add struct TileInfo.xPos = ix TileInfo.yPos = iy StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endfor // if needed, kill old tiles in remainder of this row if (delOldTilesX) for (;ix < nXG; iX +=1) tileName = "Tile" + num2str (ix) + "_" + num2str (iy) killcontrol /w=MineSweeperPanel $tileName endfor endif endfor // if needed, kill old tiles in remaining rows if (delOldTilesY) for (;iY < nYG; iY +=1) for (ix = 0;iX < nXG; iX +=1) tileName = "Tile" + num2str (ix) + "_" + num2str (iy) killcontrol /w=MineSweeperPanel $tileName endfor endfor endif // update globals now that we have adjusted number of tiles on the panel nXG = nX nYG = nY nMinesG = nMines // Hide some mines in the tiles variable iMine =0 do iX = floor (nX/2 + enoise (nX/2)) iy = floor (nY/2 + enoise (nY/2)) tileName = "Tile" + num2str (ix) + "_" + num2str (iy) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TIleInfo, userDataStr if (TileInfo.neighbors >= 0) TileInfo.neighbors = -1 iMine +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr Mines_AddANeighbor (iX, iY) endif while (iMine < nMines) // Start time string at 0 and init start ticks NVAR Result = root:packages:IgorGames:Mines:bkgGlobal Result = 0 // for during a game SVAR timeString = root:packages:IgorGames:Mines:TimeString timeString = "00:00" NVAR startTicks = root:packages:IgorGames:Mines:StartTicks CtrlNamedBackground MinesTimer, stop CtrlNamedBackground MinesTimer, period=60, burst = 0, proc=Mines_BKGTask startTicks = ticks CtrlNamedBackground MinesTimer, start return 0 end //************************************************************************ // Makes global variables, if not already made function Mines_MakeGlobals () if (!(dataFolderExists ("root:packages"))) newDataFolder root:packages endif if (!(dataFolderExists ("root:packages:IgorGames"))) newdataFolder root:packages:IgorGames endif if (!(dataFolderExists ("root:packages:IgorGames:Mines"))) newdataFolder root:packages:IgorGames:Mines variable/G root:packages:IgorGames:Mines:NX = 8 variable/G root:packages:IgorGames:Mines:NY = 8 variable/G root:packages:IgorGames:Mines:Nmines = 10 variable/G root:packages:IgorGames:Mines:NfLagged variable/G root:packages:IgorGames:Mines:Ncovered variable/G root:packages:IgorGames:Mines:StartTicks variable/G root:packages:IgorGames:Mines:gameType // 0-3 for custom size, small, medium, large variable/G root:packages:IgorGames:Mines:bkgGlobal // set to 1 to stop timer with a loss, set to 2 to stop timer with a win variable/G root:packages:IgorGames:Mines:winningTicks String/G root:packages:IgorGames:Mines:TimeString // Waves for showing high scores make/t/o/n = (10, 3) root:packages:IgorGames:Mines:highScoresWave WAVE/T highScoresWave = root:packages:IgorGames:Mines:highScoresWave SetDimLabel 1,0,Rank,highScoresWave SetDimLabel 1,1,Name,highScoresWave SetDimLabel 1,2,Time,highScoresWave variable iRank for (iRank =1;iRank <= 10; iRank +=1) highScoresWave [iRank-1] [0] = num2str (iRank) endfor endif end //************************************************************************ // Adds a mine to the neighboring mines count of each tile bordering matX, matY Function Mines_AddANeighbor (matX, matY) variable matX, matY NVAR nX = root:packages:IgorGames:Mines:NX NVAR nY = root:packages:IgorGames:Mines:NY variable lastX =nx -1 variable lasty = ny -1 STRUCT Mines_TileInfo TIleInfo string tileName, userDataStr // add to left upper if ((matX > 0) && (matY > 0)) tileName= "Tile" + num2str (matX -1) + "_" + num2str (matY-1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to upper if (maty > 0) tileName = "Tile" + num2str (matX) + "_" + num2str (matY - 1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors !=-1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to right upper if ((matx < lastX) && (maty > 0)) tileName = "Tile" + num2str (matX + 1) + "_" + num2str (matY - 1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to right if (matx < lastX) tileName = "Tile" + num2str (matX + 1) + "_" + num2str (matY) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to lower right if ((matX < lastX) && (maty < lastY)) tileName = "Tile" + num2str (matX + 1) + "_" + num2str (matY + 1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to lower if (matY < lastY) tileName = "Tile" + num2str (matX) + "_" + num2str (matY + 1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to lower left if ((matx > 0) && (maty < lastY)) tileName = "Tile" + num2str (matX -1) + "_" + num2str (matY + 1) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif // add to left if (matx > 0) tileName = "Tile" + num2str (matX -1) + "_" + num2str (matY) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.neighbors != -1) TileInfo.Neighbors +=1 StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endif end //*************************************************************************** // Code for Mine Sweeper Tile Custom Control. // First, make constants for custom control function events we are interested in. On Pro 6.20 or later, // you can define them by adding this include statement to your procedure file: // #include static constant kCCE_mouseup= 2 static constant kCCE_frame= 12 static constant kCCE_mousedown = 1 static constant kCCE_mouseup_out =3 static constant kCCE_Mode = 11 //*************************************************************************** // Procedure for tile custom control Static Function Mines_TileProc(s) STRUCT WMCustomControlAction &s STRUCT Mines_TileInfo TileInfo switch (s.eventCode) case kCCE_frame: // calculate the correct frame, read it to the WMCustomControlAction struct StructGet/S TileInfo, s.userdata Switch (TileInfo.State) case kTileBlank: // if blank, draw blank s.curFrame = kBlankTile if ((TileInfo.mouseDown) && (!(TileInfo.State & kTIleOpened))) s.curFrame +=1 endif break case kTileFlagged: // if flagged but not opened, draw flagged s.curFrame = kFlaggedTile if ((TileInfo.mouseDown) && (!(TileInfo.State & kTIleOpened))) s.curFrame +=1 endif break case kTileOpened: // if opened but not flagged, draw number of neighbors if (TileInfo.neighbors == -1) s.CurFrame = kOpenBomb else s.curFrame = kOpen0 + TileInfo.neighbors endif break case kTIleFlaggedAndOpened: if (TileInfo.neighbors == -1) // correctly flagged s.curFrame = kFlaggedGood else s.curFrame = kFlaggedBad endif break case kTileExploded: // if exploded, draw exploded s.curFrame = kExplodedBomb break endSwitch break case kCCE_mousedown: // mouse down StructGet/S TileInfo, s.userdata if (!(TileInfo.State & kTIleOpened)) if (s.eventMod & 16) TileInfo.mouseDown =2 // set mouseDown bit in TileInfo struct else TileInfo.mouseDown =1 endif StructPut/S TileInfo, s.userdata endif break case kCCE_mouseup_out: // mouse up outside of control StructGet/S TileInfo,s.userdata if (!(TileInfo.State & kTIleOpened)) TileInfo.mouseDown =0 // un-set mouseDown bit in TileInfo struct StructPut/S TileInfo, s.userdata endif break case kCCE_mouseup: // mouse up StructGet/S TileInfo, s.userdata variable wasRIghtClick = (TileInfo.mouseDown == 2) TileInfo.mouseDown =0 if (!(TileInfo.State & kTIleOpened)) if (wasRIghtClick) // control key/right click was pressed, unflag a flagged tile or flag an unflagged tile NVAR nFlagged = root:packages:IgorGames:Mines:NfLagged if (TileInfo.State == kTileFlagged) TileInfo.State = kTileBlank nFlagged -=1 else TileInfo.State = kTileFlagged nFlagged +=1 endif StructPut/S TileInfo, s.userdata else // opening a Tile // If uncovering a bomb, show the bomb and end the game if (TileInfo.neighbors == -1) NVAR result = root:packages:IgorGames:Mines:BKGglobal result = 1 TileInfo.State = kTileExploded StructPut/S TileInfo, s.userdata Mines_Reveal() else // its not a bomb. Mark tile as opened // if no neigbors have bombs, open all blank neighbors recursively NVAR nCovered = root:packages:IgorGames:Mines:Ncovered if (TileInfo.neighbors ==0) if (TileInfo.State & kTileFlagged) TileInfo.State = kTileOpened StructPut/S TileInfo, s.userdata nCovered -= 1 endif Mines_OpenBlanks (TileInfo.xpos, TileInfo.yPos) else TileInfo.State = kTileOpened StructPut/S TileInfo, s.userdata nCovered -= 1 endif if (nCovered == 0) // all mines have been uncovered, game is won // set winning ticks right away, so user gets actual time for high scores NVAR winningTicks = root:packages:IgorGames:Mines:winningTicks winningTicks =ticks NVAR result = root:packages:IgorGames:Mines:BKGglobal result = 2 Mines_Reveal() endif endif endif endif break endSwitch return 0 End //*********************************************************************** // called periodically to update timer display, and perhaps start a new game Function Mines_BKGTask(s) STRUCT WMBackgroundStruct &s // check if window is still open, and return 1 to quit task if not open if (CmpStr (WinList("MineSweperPanel", " ", "WIN:64" ), "MineSweeperPanel") ==0) return 1 else NVAR result = root:packages:IgorGames:Mines:bkgGlobal NVAR startTicks = root:packages:IgorGames:Mines:StartTicks if (result == 0) // during a game SVAR TimeStr = root:packages:IgorGames:Mines:TimeString variable elapsedTicks = ticks - startTicks TimeStr = Secs2Time(round (elapsedTicks/60), 5, 0) if (elapsedTicks < 216000) TimeStr = TImeStr [3, StrLen (timeStr)-1] endif else // end of a game variable playAgain // 0 to quit, 1 to play again, 2 to ask to play again NVAR gameType = root:packages:IgorGames:Mines:gameType // 0 for arbitrary, 1 for small, 2 for medium, 3 for large NVAR nX = root:packages:IgorGames:Mines:NX NVAR nY = root:packages:IgorGames:Mines:NY NVAR nMines = root:packages:IgorGames:Mines:Nmines String alertStr if (result == 2) // stopping with a win NVAR winningTicks = root:packages:IgorGames:Mines:winningTicks variable score = winningTicks -startTicks // save this right away to check high scores variable HighScoreCode = (Mines_CheckHighScores (score, gameType)) if (HighScoreCode == 0) //no high score playAgain = 2 alertStr = "Congrats! You Won." elseif (HighScoreCode ==1) //a high score and user does not want replay playAgain = 0 elseif (HighScoreCode ==3) playAgain = 1 // a high score and replay requested endif elseif (result == 1) // stopping with a loss playAgain = 2 alertStr = "BOOM! You Lost" endif if (playAgain == 2) DoAlert/T=alertStr 1, "Do You Want to Play Again?" if (V_Flag ==1) // yes clicked playAgain = 1 elseif (V_Flag ==2) // no clicked playAgain =0 endif endif if (playAgain) Mines_StartMineSweeper (nX, nY, nMines, gameType) else KillWindow MineSweeperPanel return 1 // to stop playing endif endif endif return 0 // Continue background task End //*********************************************************************** // uncovers all unflagged squares surrounding a tile with no bombs. Made to be called recursively Function Mines_OpenBlanks (matX, matY) variable matX, matY NVAR nX = root:packages:IgorGames:Mines:NX NVAR nY = root:packages:IgorGames:Mines:NY NVAR nCovered = root:packages:IgorGames:Mines:Ncovered variable lastX =nx -1 variable lasty = ny -1 STRUCT Mines_TileInfo TileInfo string TileName, UserDataStr // look at this tile tileName = "Tile" + num2str (matX ) + "_" + num2str (matY ) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 if (TileInfo.neighbors == 0) // look left upper if ((matX > 0) && (matY > 0)) tileName = "Tile" + num2str (matX -1) + "_" + num2str (matY -1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX -1, matY-1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look upper if (matY > 0) tileName = "Tile" + num2str (matX) + "_" + num2str (matY -1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX, matY-1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look right upper if ((matX < lastX) && (matY > 0)) tileName = "Tile" + num2str (matX +1) + "_" + num2str (matY -1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX + 1, matY-1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look right if (matX < lastX) tileName = "Tile" + num2str (matX +1) + "_" + num2str (matY) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX + 1, matY) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look right lower if ((matX < lastX) && (matY < lastY)) tileName = "Tile" + num2str (matX +1) + "_" + num2str (matY + 1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX + 1, matY + 1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look lower if (matY < lastY) tileName = "Tile" + num2str (matX) + "_" + num2str (matY + 1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX, matY + 1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look lower left if ((matX > 0) && (matY < lastY)) tileName = "Tile" + num2str (matX -1) + "_" + num2str (matY + 1) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX - 1, matY + 1) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif // look left if (matX > 0) tileName = "Tile" + num2str (matX -1) + "_" + num2str (matY) UserDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if ((!(TileInfo.state & kTileFlagged)) && (!(TileInfo.state & kTileOpened))) if (TileInfo.neighbors == 0) Mines_OpenBlanks (matX - 1, matY) else TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr nCovered -=1 endif endif endif endif endif end //*********************************************************************** // Reveals what is behind all the tiles, called at the end of the game Function Mines_Reveal() NVAR nX = root:packages:IgorGames:Mines:NX NVAR nY = root:packages:IgorGames:Mines:NY variable iX, iY string tileName, userDataStr STRUCT Mines_TileInfo TileInfo for (iy =0; iY < nY; iY +=1) for (ix = 0;iX < nX;iX +=1) tileName = "Tile" + num2str (ix) + "_" + num2str (iy) userDataStr =GetUserData("MineSweeperPanel", tileName, "" ) StructGet/S TileInfo, userDataStr if (TileInfo.state < kTileExploded) TileInfo.state = TileInfo.state | kTileOpened StructPut/S TileInfo, userDataStr CustomControl $tileName win = MineSweeperPanel, userdata = userDataStr endif endfor endfor end //*********************************************************************** // Struct to hold name and time of top 10 scores. We make one for each of the three cannonical puzzle sizes // and store the three structures in one preferences file, MineSweeper.bin // Last modified 2015/02/09 by Jamie Boyd constant kMineSweeperHighScoresVers = 101 Structure MineSweeperHighScoresStruct uint32 version // Preferences structure version number. 100 means 1.00. Struct MineSweeperScoreStruct scores [10] // array of 10 high scores EndStructure //*********************************************************************** // Struct to hold name and time of a single high score // Last modified 2015/02/09 by Jamie Boyd Structure MineSweeperScoreStruct char Name [128] //name of high scoreer uint32 timeTicks // time takes to solve puzzle, in ticks EndStructure //*********************************************************************** // checks if this time is one of the ten best for this size of minefield, and allows user to insert their name, if so desired, called after a winning game // returns a bit-wise field. bit 0 is set if there was a high score. bit 1 is set if user wants to play again (but only following a high score) Function Mines_CheckHighScores (score, gameType) variable score // time taken to finish the game variable gameType // 0 for arbitrary, 1 for small, 2 for medium, 3 for large // variable for returnCode, set it to 0 to start variable returnCode = 0 // Load pakage prefs for this game size STRUCT MineSweeperHighScoresStruct prefsStruct string fileName LoadPackagePreferences "IgorGames" ,"MineSweeper.bin", gameType, prefsStruct variable iS if ((V_bytesRead == 0) || (prefsStruct.Version != kMineSweeperHighScoresVers))// record does not exist yet, initialize the structure to "anonymous" and biggest time possible prefsStruct.Version = kMineSweeperHighScoresVers for (iS =0; iS < 10; iS +=1) prefsStruct.scores[iS].timeTicks = 2^32-1 prefsStruct.scores[iS].Name = "anonymous" + num2char (0) endfor endif // did we get a high score? for (iS =0; iS < 10; iS +=1) if (score < prefsStruct.scores[iS].timeTicks) break endif endfor // is score in the top 10? if (iS < 10) // then there was a high score returnCode = 1 variable rank = iS variable playAgain string yourName = "anonymous", promptMessage sprintf promptMessage, "Congratulations on the #%d high Score", rank +1 prompt yourName, "Enter Your Name in the Pantheon:" prompt playAgain "Play Again?", popup, "YES;NO" doPrompt promptMessage, yourName, playAgain if (V_flag) yourName = "anonymous" playAgain = 0 endif // name field is limited to 128 characters, the last of which we set to null if (strLen (yourName) > 127) yourName = yourName [0, min (127, strLen (yourName)-1)] endif if (playAgain == 1) returnCode += 2 * playAgain endif // Insert high score and name // first move lower-ranking scores down, deleting the previous #10 position for (iS =9; iS > rank; iS -=1) prefsStruct.scores[iS].timeTicks = prefsStruct.scores[iS -1].timeTicks prefsStruct.scores[iS].Name = prefsStruct.scores[iS -1].name endfor prefsStruct.scores[rank].timeTicks = score prefsStruct.scores[rank].name = yourName + num2char (0) SavePackagePreferences /FLSH=1 "IgorGames" , "MineSweeper.bin", gameType, prefsStruct endif return returnCode end //************************************************************************ // Creates a panel to show high scores for MineSweeper Function Mines_ShowHighScores () doWindow/F MineSweeperScores if (V_Flag == 0) Mines_MakeGlobals () NewPanel/K=1 /N=MineSweeperScores/W=(2,45,237,263) as "Mine Sweeper Best Times" ListBox HighScoresList win = MineSweeperScores,pos={5,5},size={225,183} // bottom = 5 + 183 = 188, 30 pixels free at bottom ListBox HighScoresList win = MineSweeperScores,listWave=root:packages:IgorGames:Mines:highScoresWave ListBox HighScoresList win = MineSweeperScores,widths={41,85,83},userColumnResize= 1 PopupMenu ShowScoresPopMenu win = MineSweeperScores,pos={9,193},size={200,20},proc=Mines_ShowScoresSetPopMenuProc,title="Show Scores For" PopupMenu ShowScoresPopMenu win = MineSweeperScores,mode=1,popvalue="8 x 8 (10 mines)",value= #"\"8 x 8 (10 mines);16 x 16 (40 mines);30 X 16 (99 mines);\"" Mines_ReadHighScoresSet (1) // set resize hook SetWindow MineSweeperScores hook(PanelHook)=Mines_HighScoresPanelHook endif end //************************************************************************ // Resize hook for High Scores panel. Resizes HighScoresList to use available space, and moves ShowScoresPopMenu to below HighScoresList // Perhaps useful if people with very long names, approaching 128 char limit, play Static Constant kMinHSPanelWidth =235 Static Constant kMinHSPanelHeight =218 Function Mines_HighScoresPanelHook(s) STRUCT WMWinHookStruct &s if (s.eventCode ==6) // test for minimum width, height variable doMove=0 if (s.winRect.right < kMinHSPanelWidth) s.winRect.right = kMinHSPanelWidth doMove += 1 endif if (s.winRect.bottom < kMinHSPanelHeight) s.winRect.bottom = kMinHSPanelHeight doMove +=2 endif if (doMove > 0) variable winFudge = 72/screenresolution GetWindow $s.WinName wsize if (doMove & 1) V_right=V_left + kMinHSPanelWidth * winFudge endif if (doMove & 2) V_bottom = V_top + kMinHSPanelHeight * winFudge endif movewindow /w=$s.winName V_left, V_top, V_right, V_bottom endif // adjust height of HighScoresList listBox variable ctrlWidth, ctrlHeight ctrlHeight = (s.winRect.bottom - s.winRect.Top - 35) ctrlWidth = (s.winRect.Right - s.winRect.Left - 10) ListBox HighScoresList win= $s.winName, size={(ctrlWidth),(ctrlHeight)} PopupMenu ShowScoresPopMenu win = MineSweeperScores,pos={9, (ctrlHeight + 10)} return 1 elseif (s.eventCode ==2) // Kill return 1 endif return 0 End //************************************************************************ // Calls function to read high scores based on chosen mines size from popmenu Function Mines_ShowScoresSetPopMenuProc(pa) : PopupMenuControl STRUCT WMPopupAction &pa switch( pa.eventCode ) case 2: // mouse up Mines_ReadHighScoresSet (pa.popNum) break case -1: // control being killed break endswitch return 0 End //************************************************************************ // function to read high scores for one of the three mines sizes Function Mines_ReadHighScoresSet (gameType) variable gameType WAVE/T highScoresWave = root:packages:IgorGames:Mines:highScoresWave STRUCT MineSweeperHighScoresStruct prefsStruct LoadPackagePreferences "IgorGames" ,"MineSweeper.bin", gameType, prefsStruct variable iS if ((V_bytesRead == 0) || (prefsStruct.Version != kMineSweeperHighScoresVers))// file does not exist yet, initialize the structure to "anonymous" and biggest time possible prefsStruct.Version = kMineSweeperHighScoresVers for (iS =0; iS < 10; iS +=1) prefsStruct.scores[iS].timeTicks = 2^32-1 prefsStruct.scores[iS].Name = "anonymous" + num2char (0) endfor SavePackagePreferences /FLSH=1 "IgorGames" , "MineSweeper.bin", gameType , prefsStruct endif // Read prefs into high scores wave variable scoreTime string timeStr for (iS =0; iS < 10; iS +=1) scoreTime = prefsStruct.scores[iS].timeTicks if (scoreTime == 2^32 -1) // no high score for this position yet highScoresWave [iS] [1] = "--------" highScoresWave [iS] [2] = "--:--:---" else highScoresWave [iS] [1] = prefsStruct.scores[iS].Name TimeStr = Secs2Time(scoreTime/60, 5, 1) if (scoreTime < 216000) // strip off hours if time is less than 1 hour. TimeStr = TimeStr [3, strlen (TImeStr)-1] endif highScoresWave [iS] [2] =TimeStr endif endfor end //************************************************************************ //**************************** 15-puzzle ********************************* //************************************************************************ // As known and loved from the Apple Macintosh desk accessory included with every Mac system prior to System 7.5 // 15-puzzle demonstrates: // 1) Image manipulation/image indexing with p,q,r // 2) Graph window hook function // 3) Run-length encoding - with variable byte-length data // 4) getting and setting single bytes in multi-byte values // 5) recursive function that returns a list that gets built up through each recursive call Menu "Misc" SubMenu "Igor Games" "Fifteen Puzzle", /Q , FIfteen_Start () end end //************************************************************************ // *********************Code to convert images for storage in a procedure file*************** //************************************************************************ // The Default puzzle image is of the numbers 1 to 15, 80 x 80 points. We always want this available // Instead of storing the instructions to create an image wave in the procedure file, we: // 0) if neccessary, convert RGB image to single byte wave for display with RainBow256 // 1) make a run length encoded version of the image // 2 convert the RLE bytes to hex, which saves space when stored in ASCII (ASCII16 - less eficient than ASCII85, but easier to code) // 3) Create a function containing the instructions to build the hex string and return its contents. // 4) When we want the image "reconstituted", call a function that gets the hex string, converts it to bytes, and undoes the RLE //************************************************************************ //Converts an r,g,b wave to an unsigned byte wave mapped to the rainbow256 colortable, except the darkest // values are set to 0, and the brightest values to 255, so display the image with colortable scaled from // 1 to 254 and set minRGB to black and maxRGB to white. Function fifteen_rgbWave2RainBow256 (inPutRGBWave, outPutName) Wave inPutRGBWave String outPutName if ((WaveDims(inputRGBWave) != 3) & (DimSize(inPutRGBWave, 2 ) != 3)) printf "The wave %s was not an RGB image.\r", nameofwave (inPutRGBWave) return 1 else make/o/b/u/n = ((dimSize (inputRGBWave, 0)), (dimSize (inputRGBWave, 1))) $outPutName WAVE outPutWave = $outPutName outPutWave = fifteen_rgb2RainBow256 (inPutRGBWave [p] [q] [0],inPutRGBWave [p] [q] [1],inPutRGBWave [p] [q] [2]) return 0 endif end //************************************************************************ //Converts an r,g,b triplet to an unsigned byte value mapped to the rainbow256 colortable // Used by fifteen_rgbWave2RainBow256 Function fifteen_rgb2RainBow256 (rC,gC,bC) variable rC,gC,bC // range from 0 to 255 variable colorScal if (((rC < 5) && (gC < 5)) && (bC < 5)) colorScal = 0 elseif (((rc > 250) && (gc > 250)) && (bc > 250)) colorScal = 255 else if ((rc > gc) && (rc > bc)) // red is max colorScal = 1 if (gc > bc) // green is next colorScal += (gc/rc) * 56 else //red is max, blue is next, but purple gets shorted in this color table colorscal = 1 endif elseif ((gc > rc) && (gc > bc)) // green is max colorScal = 111 if (rc > bc) // green is max, red is next colorscal -= (rc/gc)*56 else // green is max, blue is next colorScal += (bc/gc) * 56 endif else // blue color is max if (rc > gc) colorscal = 226 // blue is max, red is next colorscal += (rc/bc) * 28 else // blue is max, green is next colorscal -= (gc/bc)*56 endif endif endif return colorScal end //************************************************************************ // Transforms a 2D greyscale image into custom format string with run length encoded single byte data in hex. // RLE data contains pairs of values, first of pair is color, second is number of consecutive occurrences // If there are more than 255 consecutive occurences, a 0 where run length would normally be indicates that the // two bytes that follow encode a two byte value for run length. So it takes one byte to contain a one byte value, // and three bytes to contain a two byte value, and, by extension, five bytes (0,0,b0,b1,b2) to contain a 3 byte value, // and 7 bytes to contain a 4 byte value (0,0,0, b0,b1,b2,b3), or, bytes needed = 2n-1 function/S fifteen_ImageWave2HexStr (xStart, xSize, yStart, ySize, zMin, zMax, zLevels, inWave, procName, chunkSize) WAVE inWave // 2D image wave, assumed to be greyscale image variable xStart, xSize, yStart, ySize // define region of interest in image to transform variable zMin, zMax // start and end of greyscale range to scale to zRange. variable zLevels // make 255 for 8 bits, or less to posterize to get fewer levels, more compression string procName // name of procedure that will be printed variable chunkSize // variable iX=xStart, xEnd = xStart + xSize variable iY=yStart, yEnd = yStart + ySize variable iInput, nInputs = xSIze * ySize // make a temp copy of input data, and make it linear duplicate/r= [xStart, xStart + xSIze -1] [ySTart, yStart + ySIze -1]/FREE inWave, inWaveLinear redimension/n = (nInputs) inWaveLinear // make a temp free wave to hold RLE data we generate variable worstCaseSize = nInputs * 2 // if no compression obtained, we would need this many points make/b/u/FREE/N = (worstCaseSize) RLEdata variable nRLE =0 // number of elements in the RLE data, starts at 0 and increases with every entry // first two entries in RLEData are xSize, then ySize, in the "0 means more bytes are coming" format described above variable iLengthByte, nLengthBytes // x size first fifteen_CustomBytes (xSize, RLEdata, nRLE) // then y Size fifteen_CustomBytes (ySize, RLEdata, nRLE) // Fill rest of RLE data with value/run length pairs, with some run length data being more than one byte variable iRunVal, iRunStart // value and run length for a value/runLength pair // iRunVal is set correctly for next run inside the loop, so set it initially to first input value to "prime the pump", iInPut = 0 iRunVal= fifteen_getScaledVal (inWaveLinear [iInPut], zMin, zMax, zLevels) for (;iInput < nInPuts;) // enter color Value RLEdata [nRLE] = iRunVal // equals scaled value of inWaveLinear [iInPut] nRLE +=1 // save start position of this run iRunStart=iInPut // keep looking at subsequent point til the scaled color value is different for (iInPut +=1;iInPut < nInPuts;) iRunVal = fifteen_getScaledVal (inWaveLinear [iInput], zMin, zMax, zLevels) if (iRunVal == RLEdata [nRLE -1]) iInput +=1 else break endif endfor // at end of loop, iRunVal = color value of next run, iInput points to start of next run // add data for run length, in custom format where 0 is signal for extra byte data fifteen_CustomBytes ((iInPut - iRunStart), RLEdata, nRLE) endfor duplicate/o/r=[0,nRLE -1] RLEdata RLEWAVE_temp_IN printf "RLE compression for %s = %.3f %.\r", nameofwave (inwave), (1 - nRLE/nInputs) * 100 // transform RLE data into a string of Hex digits string HexString = "" variable iRLE string hexByte for (iRLE = 0; iRLE < nRLE; iRLE +=1) sprintf hexByte, "%.2X", RLEdata [iRLE] HexString += hexByte endfor // print a function that returns the string of hex digits. A quick and dirty way to store picture data in a procedure file so its data is easily accesible within a function if ((CmpStr (procName, "") != 0) && (chunkSize > 0)) variable iChunk, nStr = strlen (HexString) variable nChunks = floor (nStr/chunkSize) printf "Function/S Fifteen_Pic_%s ()\r\r", procName printf "\tString rStr = \"%s\"\r", HexString [0, chunkSize -1] for (iChunk =1; iChunk < nChunks ; iChunk +=1) printf "\trStr += \"%s\"\r", HexString [(iChunk * chunkSize), ((iChunk + 1) * chunkSize) -1] endfor if (mod (nStr, chunkSize) !=0) printf "\trStr += \"%s\"\r", HexString [(iChunk * chunkSize), (strlen (HexString) -1)] endif printf "\treturn rStr\rend\r" endif return HexString end //************************************************************************ // Helper function for fifteen_ImageWave2HexStr to scale a value to a 8 bit range, possibly posterizing to a reduced number of levels Function fifteen_getScaledVal (Value, zMin, zMax, nLevels) variable value variable zMin variable zMax variable nLevels // scale ScaledVal from 0 to 1 variable range = zMax - zMin variable ScaledVal = (max (zMin, min (value, zMax)) - zMin)/range // posterize ScaledVal = round (ScaledVal * nLevels) // scale ScaledVal to 8 bits ScaledVal *= 255/nLevels return round (ScaledVal) end //************************************************************************ // Helper function for fifteen_ImageWave2HexStr to add a value to a byte wave in our custom extra byte format Function fifteen_CustomBytes (theValue, byteWave, iPos) variable theValue // the value (an integer, possibly multi-byte) to be added to the data wave wave byteWave // data wave, expected to be unsigned byte variable &iPos // position in the data wave to start adding data. WIll point to end of data when function returns theValue = round (theValue) // supposed to be an integer already // how many bytes wide is the value? variable nBytes = ceil (fifteen_logBase2 (theValue + 1)/8) // add extra "flag zeros" for each byte greater than 1 variable endBytePos= iPos + nBytes -1 for (;iPos < endBytePos; iPos +=1) byteWave [iPos] = 0 endfor // add data bytes, most significant first for (nBytes -=1; nBytes >= 0; nBytes -=1, iPos += 1) byteWave [iPos] = floor (theValue/(2^(nBytes * 8))) theValue -= byteWave [iPos] * (2^(nBytes * 8)) endfor end //************************************************************************ // A utility function to calculate base 2 logarithms, useed for fifteen_CustomBytes STATIC CONSTANT log2 = 0.301029995664 // base 10 logarithm of 2 Function fifteen_logBase2 (theValue) variable theValue return log (theValue)/log2 end //************************************************************************ // Reconstitutes an image from the custom hexString format made by fifteen_ImageWave2HexStr function fifteen_HexStr2ImageWave (hexStr, imageWaveName) string hexStr, imageWaveName // transform Hex string to bytes, store in temp FREE wave variable iStr, iPt, nPts = strlen (hexStr)/2 string hexDigit variable theVal make/B/U/FREE/n = (nPts) RLEdata for (iSTr =0, iPt =0; iPt < nPts; iPt +=1, iStr +=2) hexDigit = hexStr [iStr, iStr + 1] sscanf hexDigit, "%x", theVal RLEdata [iPt] =theVal endfor duplicate/o RLEdata RLEWAVE_temp_OUT // X size and Y size make a header at start of data variable xSize, ySize iPt = 0 xSize = fifteen_UnCustomBytes (RLEdata, iPt) ySIze = fifteen_UnCustomBytes (RLEdata, iPt) // make image wave, linear at first for ease of calculating offsets make/o/b/u/n = (xSize * ySize) $imageWaveName WAVE outWave = $imageWaveName // Undo RLE compression variable colorVal, runLength, outPos =0 for (; iPt < nPts;) // first the color byte colorVal = RLEdata [iPt] iPt +=1 // then the run length, which can be more than one byte runLength = fifteen_UnCustomBytes (RLEdata, iPt) outWave [outPos, outPos + runLength -1] = colorVal outPos += runLength endfor redimension/n = (xSize, ySize) outWave end //************************************************************************ // Helper function for fifteen_HexStr2ImageWave to interpret a value in our custom extra bytes format // where 0 is an alert for multibyte data, and the number of sequential zeroes codes for the // number of extra bytes a value needs FUnction fifteen_UnCustomBytes (byteWave, iPos) wave byteWave // data wave, expected to be unsigned byte variable &iPos // position in the data wave to start reading data. WIll point to next byte when function returns variable nBytes for (nBytes =0; byteWave [iPos] == 0; iPos +=1, nBytes +=1) endfor // at end of loop, first data byte is at iPos, and nBytes contains number of data bytes variable returnVal = 0 for (; nBytes >= 0; nBytes -=1, iPos += 1) returnVal += byteWave [iPos] * (2^(nBytes * 8)) endfor // at end of loop, return val contains custom format data, and iPos points to next value return returnVal end //************************************************************************ // The default puzzle picture, stored as a function that returns a string of hex characters of run-length encoded image data // make your own function from your own image using fifteen_ImageWave2HexStr, converting from RGB with fifteen_rgbWave2Rainbow256 if needed Function/S Fifteen_Pic_Numbers () String rStr = "5050FF0001490102FF101F05FF0E3F05FF145F03FF0D0104FF0F1F07FF0C3F07FF125F04FF0D0104FF0F1F01FF041F03FF0B3F01FF043F03FF105F02FF015F02FF0F0102FF151F02FF113F02FF105F02FF015F02FF0F0102FF141F03FF103F02FF105F02" rStr += "FF025F02FF0F0102FF131F03FF0E3F04FF105F02FF035F02FF0F0102FF121F03FF0F3F05FF0E5F02FF045F02FF0F0102FF111F02FF143F03FF0D5F09FF0E0102FF101F02FF163F02FF0D5F09FF0E0102FF0F1F02FF113F01FF043F03FF135F02FF0D0106" rStr += "FF0D1F08FF0B3F07FF145F02FF0D0106FF0D1F08FF0C3F05FF155F02FF00028C9107FF109F04FF0EBF08FF0DDF05FF0D9107FF0F9F06FF0DBF08FF0CDF07FF0C9102FF139F02FF049F01FF13BF02FF0CDF02FF03DF02FF0C9102FF129F03FF17BF02FF0D" rStr += "DF02FF03DF02FF0C9105FF0F9F07FF12BF02FF0EDF03FF01DF02FF0D9106FF0E9F08FF11BF02FF0FDF04FF129103FF0D9F03FF039F03FF0FBF02FF0FDF07FF119102FF0D9F02FF059F02FF0FBF02FF0EDF02FF04DF03FF109102FF0D9F02FF059F02FF0E" rStr += "BF02FF0FDF02FF05DF02FF0B9101FF039103FF0E9F02FF039F03FF0EBF02FF0FDF03FF03DF03FF0B9106FF0F9F07FF0EBF02FF11DF07FF0D9104FF129F04FF0FBF02FF12DF05FF00028EFE04FF0C0102FF060103FF0A1F02FF061F02FF0A3F02FF053F05" rStr += "FF0AFE07FF080104FF040107FF061F04FF041F04FF083F04FF043F07FF08FE03FF03FE02FF080104FF040102FF030102FF061F04FF041F04FF083F04FF043F01FF043F03FF07FE02FF05FE02FF090102FF030102FF050102FF071F02FF061F02FF0A3F02" rStr += "FF0A3F02FF07FE02FF05FE02FF090102FF030102FF050102FF071F02FF061F02FF0A3F02FF093F03FF07FE03FF03FE03FF090102FF030102FF050102FF071F02FF061F02FF0A3F02FF083F03FF09FE08FF090102FF030102FF050102FF071F02FF061F02" rStr += "FF0A3F02FF073F03FF0BFE04FF01FE02FF090102FF030102FF050102FF071F02FF061F02FF0A3F02FF063F02FF12FE02FF090102FF030102FF050102FF071F02FF061F02FF0A3F02FF053F02FF0DFE01FF04FE02FF0A0102FF040102FF030102FF081F02" rStr += "FF061F02FF0A3F02FF043F02FF0EFE07FF080106FF020107FF061F06FF021F06FF063F06FF023F08FF09FE04FF0A0106FF040103FF081F06FF021F06FF063F06FF023F08FF0001860002FF010001FF040001FF440002FF020007FF470008FF4B00012A03" rStr += "0001FF45DF03FF0200012A030003FF075F02FF045F05FF099102FF079103FF099F02FF039F07FF07DF01AA03DF01FF0100012A020001FF010001FF010001FF045F04FF035F07FF069104FF069104FF079F04FF039F07FF06DF01AA05DF012A030005FF04" rStr += "5F04FF035F01FF045F03FF059104FF059102FF019102FF079F04FF039F02FF0ADF01AA0700012A030003FF075F02FF095F02FF079102FF059102FF019102FF099F02FF039F02FF09DF01AA0800012A040002FF075F02FF085F02FF089102FF049102FF02" rStr += "9102FF099F02FF039F05FF06AA05DF01AA0200022A0200032A020002FF045F02FF055F04FF099102FF039102FF039102FF099F02FF039F06FF05AA05DF01AA0300012A020001FF0100022A030001FF035F02FF055F05FF089102FF029102FF049102FF09" rStr += "9F02FF079F03FF04AA05DF01AA0300012A020001FF0300012A020001FF035F02FF085F03FF079102FF029109FF089F02FF089F02FF04AA05DF01AA023702AA012A020001FF030002FF045F02FF095F02FF079102FF029109FF089F02FF089F02FF04AA05" rStr += "DF01AA02010137020003FF01DF01FF075F02FF035F01FF045F03FF079102FF089102FF099F02FF039F01FF039F03FF04AA05DF01AA02370101013701AA05DF01FF045F06FF015F07FF069106FF069102FF079F06FF019F06FF05AA05DF01AA0237020101" rStr += "3702AA04DF01FF035F06FF025F05FF079106FF069102FF079F06FF029F04FF06AA05DF01AA02370201023701AA02DF01AA01DF01FF3FAA04DF01AA020101370101013702AA01DF01AA02DF01FF40AA03DF01AA02370101023702AA01DF01AA02DF01FF40" rStr += "AA03DF01AA02370301013701AA01DF01AA02DF01FF02" return rStr end //************************************************************************ // ***********************Code to make and run the 15 puzzle************************ //************************************************************************ //************************************************************************ // makes global variables, and opens a graph displaying the puzzle image function FIfteen_Start () // ensure globals exist if (!(dataFolderExists ("root:packages:IgorGames:Fifteen"))) if (!(dataFolderExists ("root:packages:IgorGames"))) if (!(dataFolderExists ("root:packages"))) newDataFolder root:packages endif newdataFolder root:packages:IgorGames endif newdataFolder root:packages:IgorGames:Fifteen String/G root:packages:IgorGames:Fifteen:SourceWaveName // the src wave from which we get "puzzle chunks" to draw variable/G root:packages:IgorGames:Fifteen:xblockSize variable/G root:packages:IgorGames:Fifteen:yBlockSize Variable/G root:packages:IgorGames:Fifteen:isRGB Variable/G root:packages:IgorGames:Fifteen:nPlaced Variable/G root:packages:IgorGames:Fifteen:nMoves make/b/u/n= (80, 80) root:packages:IgorGames:Fifteen:puzzleWave // wave to hold positions of tiles make/b/u/n = (16, 4) root:packages:IgorGames:Fifteen:PuzzleStructure // x-position, yPosition, currentNum, correctNum WAVE PuzzleStructure = root:packages:IgorGames:Fifteen:PuzzleStructure PuzzleStructure [*] [0] = mod (p, 4) // x Position PuzzleStructure [*] [1] = floor (p/ 4) // y Position PuzzleStructure [0,14] [2,3] = p + 1 // current number, and correct number PuzzleStructure [15] [2,3] =0 // position of blank tile endif // do we already have puzzle open ? if (strlen (WinList("FifteenPuzzleWindow", "", "WIN:1" )) > 2) doWIndow/F FifteenPuzzleWindow return 1 endif // open puzzle window and draw some controls WAVE puzzleWave = root:packages:IgorGames:Fifteen:puzzleWave Display/K=1/W=(0,45,205,300)/N=FifteenPuzzleWindow as "Fifteen Puzzle" AppendImage/w = FifteenPuzzleWindow puzzleWave SetAxis/w = FifteenPuzzleWindow/A/R left ModifyGraph/w = FifteenPuzzleWindow margin(left)=12,margin(bottom)=12,margin(top)=12,margin(right)=12,width={Plan,1,bottom,left} ModifyGraph/w = FifteenPuzzleWindow nticks=0, axThick=0 ControlBar /W=FifteenPuzzleWindow 50 // Setvariable for number of tiles user has placed correctly SetVariable PlacedSetvar win = FifteenPuzzleWindow,pos={4,1},size={86,22},title="Placed:", frame=0, fsize = 14 SetVariable PlacedSetvar win = FifteenPuzzleWindow, format="%d" ,limits={0,15,0},value= root:packages:IgorGames:Fifteen:nPlaced,noedit= 1 // Setvariable to show number of moves made SetVariable nMovesSetVar win = FifteenPuzzleWindow,pos={90,1},size={125,22}, limits={0,inf,0}, title="Moves:",fSize=14 SetVariable nMovesSetVar win = FifteenPuzzleWindow,value= root:packages:IgorGames:Fifteen:nMoves,noedit= 1,frame=0 // Popup menu to choose image to make a puzzle. Also lists our functions that return strings to make images PopupMenu imagePopUp win = FifteenPuzzleWindow,pos={1,26},size={68,20},proc=Fifteen_NewImageProc,title="Image:" PopupMenu imagePopUp win = FifteenPuzzleWindow,mode=0,value= #"Fifteen_ListImages (\"root:\") + FunctionList(\"Fifteen_Pic_*\", \";\", \"VALTYPE:4:\" )" // setvariable shows name of chosen image SetVariable PuzzleImagevar win = FifteenPuzzleWindow,pos={71,28},size={131,15},title=" " SetVariable PuzzleImagevar win = FifteenPuzzleWindow,frame=0,value= root:packages:IgorGames:Fifteen:SourceWaveName,noedit= 1 // Set window hook function to process clicks on the puzzle SetWindow FifteenPuzzleWindow hook(puzzleHook )=Fifteen_PuzzleHook, hookEvents= 7 // make puzzle from default image STRUCT WMPopupAction pa pa.eventCode =2 pa.popStr = "Fifteen_Pic_Numbers" Fifteen_NewImageProc(pa) end //************************************************************************ // lists all 2D waves, and 3D waves with exactly 3 planes, skipping waves in the Packages folder // Used for popmenu that selects an image for the puzzle Function/s Fifteen_ListImages (sourceFolderStr) string sourceFolderStr DFREF sourceDFR = $sourceFolderStr variable iObj string objName, returnList = "" //iterate through objects for (iObj = 0; ; iObj += 1) objName = GetIndexedObjNameDFR(sourceDFR, 1, iObj) if (strlen (objName) > 0) WAVE thewave = $sourceFolderStr + possiblyquotename (objName) if ((WaveDims(thewave) == 2) || ((WaveDims (theWave) ==3) && (DimSize(theWave, 2 ) == 3))) returnList += sourceFolderStr + possiblyquotename(objname) + ";" endif else break endif endfor // iterate through subfolders, with recursion for (iObj =0; ; iObj +=1) objName = GetIndexedObjNameDFR (sourceDFR, 4, iObj) if (strlen (objName) > 0) if (stringMatch (objName, "!packages")) returnList += Fifteen_ListImages (sourceFolderStr + possiblyquotename (objName)) endif else break endif endfor return returnList end //************************************************************************ // Loads image chosen from popmenu into the 15 puzzle wave Function Fifteen_NewImageProc(pa) : PopupMenuControl STRUCT WMPopupAction &pa switch( pa.eventCode ) case 2: // mouse up SVAR SourceWaveName = root:packages:IgorGames:Fifteen:SourceWaveName // if chosen string is a fifteen_pic, make the image if (WhichListItem(pa.popStr, FunctionList("Fifteen_Pic_*", ";", "VALTYPE:4:" ), ";") > -1) string srcWave = pa.popStr srcWave = srcWave [12, strlen (pa.popStr) - 1] SourceWaveName = "root:packages:IgorGames:Fifteen:" + srcWave if (!(WaveExists ($SourceWaveName))) FUNCREF Fifteen_Pic_Numbers stringMaker = $pa.popStr fifteen_HexStr2ImageWave (stringMaker(), SourceWaveName ) endif else SourceWaveName = pa.popStr endif // reference the image wave, set globals WAVE sourceWave = $SourceWaveName NVAR xblockSize = root:packages:IgorGames:Fifteen:xblockSize xblockSize= floor (dimSize (sourceWave, 0)/4) NVAR yblockSize = root:packages:IgorGames:Fifteen:yblockSize yblockSize= floor (dimSize (sourceWave, 1)/4) NVAR isRGB = root:packages:IgorGames:Fifteen:isRGB isRGB=(WaveDims (sourceWave) == 3) // redimension the wave on the puzzle graph, and set colortable for a 2d image WAVE puzzleWave = root:packages:IgorGames:Fifteen:puzzleWave if (isRGB) redimension/y=(wavetype (sourceWave))/n= ((4*xBlockSize), (4 * yBlockSize), 3) puzzleWave else redimension/y=(wavetype (sourceWave))/n= ((4*xBlockSize), (4 * yBlockSize)) puzzleWave ModifyImage/W=FifteenPuzzleWindow $nameOfwave (puzzleWave) ctab= {1,254,Rainbow256,0} ModifyImage/W=FifteenPuzzleWindow $nameOfwave (puzzleWave) minRGB=(0,0,0),maxRGB=(65535,65535,65535) endif // scramble the puzzle - resets nPlaced and draws the tiles in scrambled locations Fifteen_Randomize (1) // reset nMoves to 0 NVAR nMoves = root:packages:IgorGames:Fifteen:nMoves nMoves = 0 break case -1: // control being killed break endswitch return 0 End //************************************************************************ // Randomizes the puzzle by swapping each tile in turn (0 -14) for another tile // chosen randomly. We don't touch the blank space (15) because things get hairy. // We then swap the blank space with a random tile. If the sum of the X // and Y positions of the swapped tile is odd (or if it's the blank space itself), // Then we swap two tiles (0 and 1, but it could be any two) because we need // to make an even number of swaps or the puzzle will be insolvable. Function Fifteen_Randomize (doDraw) variable doDraw WAVE PuzzleStructure = root:packages:IgorGames:Fifteen:PuzzleStructure PuzzleStructure [0,14] [2,3] = p + 1 // current number, and correct number PuzzleStructure [15] [2,3] =0 // position of blank tile NVAR nPlaced= root:packages:IgorGames:Fifteen:nPlaced nPlaced = 15 variable pos1, pos2, temp for (pos1=0; pos1 < 15; pos1 += 1) do pos2 = round (enoise(7) + 7 )// 0 to 14 while (pos2 == pos1) Fifteen_SwapPos (pos1, pos2, doDraw) endfor // now blank space variable doExtraSwap =0 pos1 = 15 // it already will be 15 at end of loop, but make it explicit pos2 = round (enoise(7.5) + 7.5) if (pos2 == 15) doExtraSwap =1 else Fifteen_SwapPos (pos1, pos2, doDraw) if (mod (PuzzleStructure [pos2] [0] + PuzzleStructure [pos2] [1], 2)==1) doExtraSwap =1 endif endif if (doExtraSwap) Fifteen_SwapPos (0, 1, doDraw) endif end //************************************************************************ // Swaps positions of two tiles Function Fifteen_SwapPos (pos1, pos2, doDraw) variable pos1, pos2, doDraw WAVE PuzzleStructure = root:packages:IgorGames:Fifteen:PuzzleStructure if (doDraw) SVAR srcWaveName = root:packages:IgorGames:Fifteen:SourceWaveName WAVE srcWave = $srcWaveName WAVE puzzleWave = root:packages:IgorGames:Fifteen:puzzleWave NVAR xblockSize = root:packages:IgorGames:Fifteen:xBlockSize NVAR yblockSize = root:packages:IgorGames:Fifteen:yBlockSize NVAR isRGB = root:packages:IgorGames:Fifteen:isRGB // draw first tile variable xPos1 = PuzzleStructure [pos1] [0] variable yPos1 = PuzzleStructure [pos1] [1] variable xPos2 = PuzzleStructure [pos2] [0] variable yPos2 = PuzzleStructure [pos2] [1] variable xStart = (xPos1 * xBlockSize) variable xEnd = xStart + xblockSIze -1 variable yStart = (yPos1 * yBlockSize) variable yEnd= yStart + yBlockSIze -1 variable srcPos = PuzzleStructure [pos2] [2] -1 variable xSrcStart variable ySrcSTart variable xOffset variable yOffset if (srcPos >= 0) xSrcStart =mod (srcPos, 4) * xBlockSize ySrcSTart= floor (srcPos/4) * yBlockSize xOffset = xSTart - xSrcStart yOffset = yStart - ySrcStart if (isRGB) puzzleWave [xStart,xEnd] [yStart, yEnd] [*]= srcWave [p - xOffset] [q - yOffset] [r] else puzzleWave [xStart,xEnd] [yStart, yEnd] = srcWave [p - xOffset] [q - yOffset] endif else if (isRGB) puzzleWave [xStart,xEnd] [yStart, yEnd] [*]= 255 else puzzleWave [xStart,xEnd] [yStart, yEnd] =255 endif endif // draw second tile xStart = (xPos2 * xBlockSize) xEnd = xStart + xBlockSize - 1 yStart = (yPos2 * yBlockSize) yEnd = yStart + yblockSIze -1 srcPos = PuzzleStructure [pos1][2] -1 if (srcPos >= 0) xSrcStart =mod (srcPos, 4) * xBlockSize ySrcSTart= floor (srcPos/4) * yBlockSize xOffset = xSTart - xSrcStart yOffset = yStart - ySrcStart if (isRGB) puzzleWave [xStart,xEnd] [yStart, yEnd] [*]= srcWave [p - xOffset] [q - yOffset] [r] else puzzleWave [xStart,xEnd] [yStart, yEnd] =srcWave [p - xOffset] [q - yOffset] endif else if (isRGB) puzzleWave [xStart,xEnd] [yStart, yEnd] [*]=255 else puzzleWave [xStart,xEnd] [yStart, yEnd] = 255 endif endif endif NVAR nPlaced= root:packages:IgorGames:Fifteen:nPlaced if (PuzzleStructure [pos1] [2] > 0) if (PuzzleStructure [pos1] [2] == PuzzleStructure [pos1] [3]) nPlaced -= 1 elseif (PuzzleStructure [pos1] [2] == PuzzleStructure [pos2] [3]) nPlaced +=1 endif endif if (PuzzleStructure [pos2] [2] > 0) if (PuzzleStructure [pos2] [2] == PuzzleStructure [pos2] [3]) nPlaced -= 1 elseif (PuzzleStructure [pos2] [2] == PuzzleStructure [pos1] [3]) nPlaced +=1 endif endif // swap positions recorded in puzzleData wave variable temp = PuzzleStructure [pos1] [2] PuzzleStructure [pos1] [2] = PuzzleStructure [pos2] [2] PuzzleStructure [pos2] [2] = temp end //************************************************************************ // Hook function for puzzle window which processes mouse clicks on the tiles Function Fifteen_PuzzleHook(s) STRUCT WMWinHookStruct &s Variable hookResult = 0 switch(s.eventCode) case 5: // mouse up NVAR nPlaced = root:packages:IgorGames:Fifteen:nPlaced if (nPlaced == 15) return 1 endif NVAR nMoves = root:packages:IgorGames:Fifteen:nMoves NVAR xblockSize = root:packages:IgorGames:Fifteen:xBlockSize NVAR yblockSize = root:packages:IgorGames:Fifteen:yBlockSize WAVE PuzzleStructure = root:packages:IgorGames:Fifteen:PuzzleStructure variable xPixel = AxisValFromPixel("FifteenPuzzleWindow", "bottom", s.mouseloc.h ) variable yPixel= AxisValFromPixel("FifteenPuzzleWindow", "left", s.mouseloc.v) variable xTile= floor(xPixel /xblockSize) variable yTile= floor(yPixel/ yBlockSIze) variable pos=yTile * 4 + xTile // look left if ((xTile > 0) && (PuzzleStructure [pos - 1] [2] == 0)) Fifteen_SwapPos (pos, pos-1, 1) nMoves += 1 hookResult = 1 // look right elseif ((xTile < 3) && (PuzzleStructure [pos + 1] [2] == 0)) Fifteen_SwapPos (pos, pos +1, 1) nMoves += 1 hookResult = 1 // look up elseif ((yTile > 0) && (PuzzleStructure [pos - 4] [2] == 0)) Fifteen_SwapPos (pos, pos-4, 1) nMoves += 1 hookResult = 1 // look down elseif ((yTile < 3) && (PuzzleStructure [pos + 4] [2] == 0)) Fifteen_SwapPos (pos, pos + 4, 1) nMoves += 1 hookResult = 1 endif NVAR nPlaced = root:packages:IgorGames:Fifteen:nPlaced if (nPlaced == 15) SVAR srcWaveName = root:packages:IgorGames:Fifteen:SourceWaveName WAVE srcWave = $srcWaveName WAVE puzzleWave = root:packages:IgorGames:Fifteen:puzzleWave NVAR xblockSize = root:packages:IgorGames:Fifteen:xBlockSize NVAR yblockSize = root:packages:IgorGames:Fifteen:yBlockSize NVAR isRGB = root:packages:IgorGames:Fifteen:isRGB variable xStart = 3 * xBlockSize variable xEnd = xStart + xBlockSIze - 1 variable yStart = 3 * yBlockSize variable yEnd = yStart + yBlockSIze - 1 if (isRGB) puzzleWave [xStart,xEnd] [yStart, yEnd] [*]= srcWave [p] [q] [r] else puzzleWave [xStart,xEnd] [yStart, yEnd] =srcWave [p] [q] endif endif break endswitch return hookResult // 0 if nothing done, else 1 End Function/S Sudoko_L1_1 () String rStr = "09090001710100068E0139010004AA010002E30100011C01E301C60155010006C6010004E30100011C01550100058E01710100018E01000B8E015501AA" rStr += "01FF0100018E010002AA0100043901AA010006C6010001" return rStr end Function/S Sudoko_L1_2 () String rStr = "090900014D010002E6010001660100011A010006CC014D010003CC01FF010003E60100054D010002FF01000266010001E6010001B3010001CC013301000" rStr += "11A010002330100059901000333014D010003E601330100061A0100014D010001CC01000233010001" return rStr end Function/S Sudoko_L2_1 () String rStr = "090900019901B3010002CC010001330100011A01CC010003E6010002FF0100081A010003FF010001990133010001CC0100031A010001B3010003E60100" rStr += "01FF014D0100013301000333010008CC010002B30100031A01660100011A010001CC010002FF0199010001" return rStr end Function/S Sudoko_L2_2 () String rStr = "090900019901CC010008B30100011A01000566010002E6010001B30100013301000333010001B301CC019901000B4D01E6011A010001CC010003E601000" rStr += "1FF010001B30100024D0100021A014D010001FF010001990100083301FF010001" return rStr end Function/S Sudoko_L3_1 () String rStr = "0909990100043301FF010001CC01E6010004660100023301000599010001E6010005CC0100044D01E6010005FF0100051A01000533010001B3010002CC" rStr += "010002FF010002660100041A02000199014D010001CC0100026601" return rStr end Function/S Sudoko_L3_2 () String rStr = "09090002CC011A0199010009330100031A01330100064D0100014D011A0166010004FF0100029901FF01E601000166010002330100041A01E6010009B30" rStr += "1E6010003CC01000A660199010002" return rStr end Function/S Sudoko_L4_1 () String rStr = "090900016601B3010004E601CC011A0199010002FF01E60100033301000933010002B3010006E601330100016601CC010006E601000299010009E60100" rStr += "031A0166010002B301FF0199011A0100064D01" return rStr end Function/S Sudoko_L4_2 () String rStr = "09090004FF010004CC01B301000133010004FF019901FF010003CC0100014D01E60100029901000233014D01000DCC0199014D010001E60100026601000" rStr += "2B3010003CC011A01FF01000499010001E601B301000433010004" return rStr end Function/S Sudoko_L5_1 () String rStr = "09090001FF011A010001990100063301FF0100024D010002B3010001990100056601E601000433010004330100011A010003CC010004B3010001E60100" rStr += "024D01FF01000566010001E6010002E60100031A010006CC01000133010002" return rStr end Function/S Sudoko_L5_2 () String rStr = "0909E601B30100034D0100011A01000366010001B301000199010006CC01FF010003B3010001330100021A01CC010001E60100091A010001FF010002E60" rStr += "166010001B30100046601FF0100051A0100024D01B30100034D010002E601000266019901" return rStr end