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.
Listing 11.11 contains the rel function that is called from the onRelease event handlers attached to every piece. If the piece that passed its coordinates to rel is marked, the function should remove the mark from the piece. In other words, it should remove the mark clip attached to the piece and set marked[y][x] to zero. The mark clip can be accessed using its full name eval('square'+x+y+'.piece.mark').removeMovieClip(). Here 'square'+x+y is the name of the square on the board. Appending the '.piece' string to this name gives you the name of the piece on this square. If you append '.mark' to the result, you'll get access to the instance of the mark clip attached to the piece. When you know the name of an object, you can call the eval function to obtain a reference to the object and use its methods and properties.// the user's move // this function is called when the btOK button is clicked function userMove() { var flag=0; // If this is the computer's move, exit the function. if (mymove) return; // removing all the marked pieces for (var i=0; i < 3; i++) for (var j=0; j < i+3; j++) if (marked[i][j]) { showPiece(0,j,i); // setting an indication that at least one piece was removed flag=1; } // If there were no move, nothing happens. if (flag) { if (!numpieces) // There are no pieces left. The game is over. { // You win! showResult(2); // Game over showGameOver(1); return; } // It is the computer's move now mymove=1; // The thinking function will start in 0.1 second // It will display "Thinking..." gameact.onEnterFrame=thinking; waittime=getTimer()+100; } }After the gamer marks pieces to be removed, he or she clicks the btOK button. However, some people can click this button needlessly, just for fun. The userMove function (Listing 11.12) foresees both situations. While removing the marked pieces in the loop, the function sets the flag variable indicating whether at least one piece was removed. Then it examines the indicator. If no pieces were removed, the function does nothing. Otherwise it checks whether the game is over by analyzing the numpieces variable. If the variable is equal to zero, the gamer has won. Otherwise it is the computer's turn to make a move. The function calls the thinking function which calls the computerMove function. Both calls are made after delays, so that the Thinking: text isn't displayed simultaneously with disappearance of the piece.
function fact(n) { if (n < 2) return 1; return n*fact(n-1); }Surprisingly, it works very quickly even if you specify
trace(fact(100));If you specify a greater value, say, 255, the result will be Infinity. If you specify
trace(fact(256));you'll see the following message in the Output window:
3 2 1 6The last line, 6, is the result of computation. At the first call, the fact function "sees" that n isn't less than two and executes the 3*fact(2) statement. The second call is made with the 2 argument, so the 2*fact(1) is executed. At the third level of recursion, the function doesn't need to call itself and returns one. However, it returns this value to itself, to the previous level, rather than to the main program. At this level, the 2*fact(1) expression turns into 2*1 and is computed. The result, which is two, is returned to the upper level. The 3*fact(2) expression becomes 3*2 and is computed. The result is passed to the trace function that displays it.
// the computer's move function computerMove() { // waiting for a specified time interval if (getTimer() < waittime) return; // deleting the wait method of the gameact clip delete gameact.onEnterFrame; // skimming through the variants of the computer's moves var i,j,k,res,nump,rows=new Array(0,0,0),rows1=new Array(0,0,0); // assigning the rows and rows1 arrays the numbers of the pieces // in the rows for (i=0; i < 3; i++) { for (j=0; j < i+3; j++) rows[i]+=board[i][j]; rows1[i]=rows[i]; } // Skimming through the computer moves in the current position // starting from the lowest row because there can be more pieces there for (i=2; i >= 0; i--) { // skimming over empty rows if (!rows[i]) continue; // making all possible 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 move is winning, the opponent will lose if (res == 1) { showResult(1); // making this winning move // i.e., removing rows[i]-j pieces from the ith row nump=rows[i]-j; // removing the pieces from the board for (k=0; k < i+3; k++) if (nump && board[i][k]) { showPiece(0,k,i); --nump; } // it is now the gamer's turn mymove=0; // hiding the "Thinking..." text showThinking(0); // if there are no more pieces, the game is over if (!numpieces) showGameOver(1); return; } } // restoring the number of pieces in the ith row rows1[i]=rows[i]; } // A winning move wasn't found. Making a random move anyMove(); }When preparing to skimming through moves, the computerMove function (Listing 11.13) creates two arrays, rows and rows1, and assigns their elements the numbers of pieces in the rows on the board: the zero elements of both arrays get the number of pieces in the top row, the first elements get the number in the middle row, and the second elements get the number in the bottom row:
for (i=0; i < 3; i++) { for (j=0; j < i+3; j++) rows[i]+=board[i][j]; rows1[i]=rows[i]; }The rows array will be used to make moves and pass the resulting positions to the isWin function, and the rows1 array will be used to undo losing moves. The external loop iterates through the ith row moving from the bottom to top to remove as many pieces as possible and have more chances to find a winning move quickly:
for (i=2; i >= 0; i--) { // skimming over empty rows if (!rows[i]) continue;The internal loop changes the j loop counter from zero to rows[i] which is the number of pieces in the ith row. During each iteration, all possible moves are made. That is, the code first leaves no pieces, then it leaves one piece, then two, and so on up to rows[i] 1 pieces:
for (j=0; j < rows[i]; j++) { // A move: leaving j pieces in the ith row rows1[i]=j;Then the res variable is assigned an estimate of the position after the next move is made:
res=isWin(rows1);If res is equal to one, the computer won, and the You lose message is displayed:
nump=rows[i]-j;In a loop, the function finds squares with pieces in the ith row and removes the pieces until the nump variable becomes equal to zero:
for (k=0; k < i+3; k++) if (nump && board[i][k]) { showPiece(0,k,i); --nump; }If no pieces are left after this move, the game is over. Otherwise its the gamer's turn, and the computerMove function returns control.
rows1[i]=rows[i];If the loop for i terminated naturally, this means the current position is loosing, and the anyMove function should be called to make a random move:
// returns an estimate of the move that led to the current position // 0 - the move was losing // 1 - the move was winning function isWin(rows) { var i,j,res, 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) return 0; } // restoring the number of pieces in the ith row rows1[i]=rows[i]; } // If the opponent's all moves are losing, // the move that led to the current position, was winning return 1; }If you are attentive enough, you noticed that the following two statements:
res=isWin(rows1); if (res == 1) return 0;can be replaced with one:
if (isWin(rows1)) return 0;This will speed up the execution a little. (By the way, if you used scalar variables instead of arrays in isWin function, the gain in performance would be even greater. However, don't do this in games where many moves are skimmed through.) This is true, but you should change this function to allow people play and have fun. In the current version, if the gamer carefully makes a move other than winning, he or she will get the You lose message immediately: the program will warn them that it is pointless to play further. Only when the gamer clicks the Comp. begins button, the computer doesn't skim through moves, but makes a random move. If it computed the best first move, the gamer would always lose without making a single move. Like 80% of similar positions, the initial position is winning. However, only one move is winning: you should remove two pieces from the upper row. You'll see this after you play with the project in the nim.fla and nim.as files located in the Example\Chapter 11 folder. You'll notice that computing moves takes a certain amount of time (about a few seconds) depending on the performance of your computer. I believe you have the quickest processor.