This is the Chapter 11 from our book "The Flash 8 Game Developing Handbook", ISBN 1-931769-47-8. CD-ROM included. You can buy this book from us for $30 only.
stop();Let's proceed with programming. In the data section of the main program, replace the semicolon to a comma:
// an array to store marks marked=new Array([0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]),and add a few variables:
// the game level (1 or 2) level=1, // the maximum depth of skimming through the moves (depends on the level) maxdepth=6, // a copy of maxdepth for the isWin function workdepth=0;Here the level variable contains what the btLevel button reads: one or two. The maxdepth variable is the maximum depth of skimming through the moves that is computed from the maxdepth=level+5 formula. I found this formula through experimentation. Zero will be assigned to the workdepth variable before a call to the isWin function so that the function can determine to which depth it has dug.
//*** The main program *** // placing the btLevel button clip attachMovie('btLevel','btLevel',leveldepth); btLevel._x=360; btLevel._y=348; // defining the onRollOver event handler btLevel.onRollOver=function() { // going to the frame with a highlighted digit btLevel.gotoAndStop(btLevel._currentframe+1); } // defining the onRollOut event handler btLevel.onRollOut=function() { // returning to the frame where the digit isn't highlighted btLevel.gotoAndStop(btLevel._currentframe-1); } // defining the onRelease event handler btLevel.onRelease=function() { // the level can be changed only at the beginning of the game if (numpieces == maxpieces) { // toggling the level between 1 and 2 level=3-level; // adjusting the maximum depth maxdepth=level+5; // going to the frame where the digit isn't highlighted btLevel.gotoAndStop(level*2); } } // defining the onReleaseOutside event handler btLevel.onReleaseOutside=function() { // returning to the frame where the digit isn't highlighted btLevel.gotoAndStop(btLevel._currentframe-1); }At the beginning of the program, insert the following lines immediately after the var line:
// The depth of the btLevel clip leveldepth=0,The code for the btLevel clip is simple: you wrote similar code earlier. Your task now is to display the game level and highlight an appropriate digit depending on the gamer's actions. In addition, you should toggle the value of the level variable between one and two.
// Skimming through the computer moves in the current position // starting from the lowest row because there can be more pieces thereInsert these lines before the lines you located:
// setting the depth of skimming through moves workdepth=0;You don't need to reset the workdepth variable to zero every time you call isWin. As you already know, this variable is always equal to zero outside the isWin function.
// returns an estimate of the move that led to the current position // 0 - the move was losing // 1 - the move was winning // 2 - unknown function isWin(rows) { if (++workdepth > maxdepth) { --workdepth; return 2; } var i,j,res,res2, rows1=new Array(rows[0],rows[1],rows[2]); for (i=2; i >= 0; i--) { // skimming over empty rows if (!rows1[i]) continue; // Making the opponent's moves in the ith row for (j=0; j < rows[i]; j++) { // A move: leaving j pieces in the ith row rows1[i]=j; // estimating the move res=isWin(rows1); // If the opponent's move is winning, the move that led to the // current position, was losing if (res == 1) { --workdepth; return 0; } if (res == 2) res2=2; } // restoring the number of pieces in the ith row rows1[i]=rows[i]; } --workdepth; if (res2 == 2) return 2; // If the opponent's all moves are losing, // the move that led to the current position, was winning return 1; }This version of the isWin function differs from the previous in the conditional statement discussed earlier and in the
--workdepth;statement placed before the return. In addition, the "2" estimate indicating that the result is unknown is implemented. This project is located in the nim1.fla and nim1.as files. Make sure to update the code attached to the third frame of the movie:
#include "nim1.as"Note: if you wish to check for the correctness of skimming through moves, set maxdepth to 100 and edit the code of the orbtLevel.onRelease event handler appropriately. When the maxdepth value is greater than 12, the program should consider moves up to the final position and win if possible.
// returns an estimate of the move that led to the current position // 0 - the move was losing // 1 - the move was winning // 2 - unknown function isWin(rows) { if (++workdepth > maxdepth) { --workdepth; return 2; } var i,j,res,res2,r0,r1,r2, rows1=new Array(rows[0],rows[1],rows[2]); // assigning the numbers of pieces to scalar variables to speed up // the program's work r0=rows[0]; r1=rows[1]; r2=rows[2]; // Checking whether exactly one row is empty. // In this case, if the other rows contain equal numbers of pieces // the previous move was winning if (!r0 && r1 && r1 == r2 || !r1 && r0 && r0 == r2 || !r2 && r0 && r0 == r1) { --workdepth; return 1; } for (i=2; i >= 0; i--) { // skimming over empty rows if (!rows1[i]) continue; // Making the opponent's moves in the ith row for (j=0; j < rows[i]; j++) { // A move: leaving j pieces in the ith row rows1[i]=j; // estimating the move res=isWin(rows1); // If the opponent's move is winning, the move that led to the // current position, was losing if (res == 1) { --workdepth; return 0; } if (res == 2) res2=2; } // restoring the number of pieces in the ith row rows1[i]=rows[i]; } --workdepth; if (res2 == 2) return 2; // If the opponent's all moves are losing, // the move that led to the current position, was winning return 1; }Before skimming through the moves, this isWin version checks whether there is exactly one empty row. If there is, it checks whether the other rows contain equal numbers of pieces. If the second condition is also true, the function decreases workdepth by one and returns one.
{ --workdepth; return 1; }This indicates that the move which led to the position was winning.
if (!r0 && r1 && r1 == r2is equivalent to
if (r0 == 0 && r1 > 0 && r1 == r2In other words, if the top row is empty, and the middle row isn't, and the numbers of pieces in the middle and bottom rows are equal, the function should return one immediately.
maxdepth=6,to
maxdepth=2,in the data section.
maxdepth=level+5;to
maxdepth=level+1;Now the program doesn't think too deep. The heuristic rule is quite nice.
// Making a random move function anyMove() { var i,j,k, n=50, rows=new Array(0,0,0); // assigning the rows array the number of pieces in three rows for (i=0; i < 3; i++) for (j=0; j < i+3; j++) rows[i]+=board[i][j]; // making n attempts to remove k pieces from the ith row do { // assigning i a random number, 0, 1, or 2 i=Math.floor(Math.random()*3); // if there are less than two pieces in the row, try another if (rows[i] < 2) continue; // assigning k a random number, 1, 2, or 3 k=Math.floor(Math.random()*3)+1; // if there are 3 pieces in the ith row, and k is equal to 3: if (rows[i] == 3 && k == 3) // : assign k a random number, 1 or 2 k=Math.floor(Math.random()*2)+1; // if there are 2 pieces in the ith row, assign k one if (rows[i] == 2) k=1; // removing k pieces from the ith row rows[i]-=k; // if there are rows with equal to numbers of pieces: if (rows[0] == rows[1] || rows[0] == rows[2] || rows[1] == rows[2]) // : undo the move { rows[i]+=k; k=0; } // after the move is made, break the loop if (k) break; } while (--n); // if no move is made, make a simple move if (!k) { // finding the row with the maximum number of pieces i=2; if (rows[1] > rows[2]) i=1; if (rows[0] > rows[1]) i=0; // removing one piece from the ith row rows[i]-=1; k=1; } // removing k pieces from the ith row for (j=0; j < i+3; j++) if (board[i][j]) { showPiece(0,j,i); if (!--k) break; } // the gamer's move mymove=0; // hide "Thinking..." text showThinking(0); }With this version of the anyMove function, the program makes varied moves, and it is interesting to play with it. However, the game is still for children. This version of the project is in the nim2.fla and nim2.as files.