John.Wiley.and.Sons.Rapid.Mobile.Enterprise.Development.for.Symbian.OS.An.Introduction.to.OPL.Application.Design.and.Programming.May.2005.eBook-LinG (779881), страница 18
Текст из файла (страница 18)
Youshould familiarize yourself with the GETEVENT32 entry, which carriesthe full array explanation).What we read into FooX% and FooY% is the actual pixel coordinates inthe window the pen was tapped, not the actual coordinates of the entirescreen. We then work out the coordinates of the grid square (rather thanthe pixels).
Using the INT (integer) function means we will ignore thefraction of the answer, and take the whole number result only – whichwill be the square.5.4.2 Using a Cursor with Keyboard InputWhat is a Cursor?You’re probably familiar with the concept of a cursor. It’s a highlightedarea on a computer screen that shows where the next piece of informationwill be placed. In a word processing application, the vertical bar line isthe cursor.On the Othello board, we’ll want to have a way of highlighting thesquare where the next piece will be placed. This is what the cursor willbe for. To move the cursor, we’ll use the joystick on the Series 60 devices(which has a push in function we’ll use to let the player make the actualmove).
The Communicators also have a cursor pad and will use this samemethod, as the cursor pad on those devices sends the same keypressvalue as the Series 60 joysticks.READING THE PLAYER’S MOVE89The cursor position will be stored as two coordinates, and these will beGLOBAL variables. As well as the current cursor position, we’ll also needto know the previous cursor position.
Why? Imagine the cursor drawn onthe board. When we move to a new square, then we will draw the cursoron the new square. But what happens to the last drawn cursor on theprevious square? It’s still there. This is why we need to know the locationof the previous cursor, so we can wipe it out. So we’ll define:GLOBAL CursorX%,CursorY%,OldX%,OldY%Reading the Cursor KeysThe main work in reading the keys for moving the cursor is done insidethe PROC KeyBoardDriver:. This is what we have from Event Core:Mod%=Ev&(4) AND 255IF Mod% AND 2 : Shift%=1 : ELSE : Shift%=0: ENDIFIF Mod% AND 4 : Control%=1 : ELSE : Control%=0 : ENDIFrem Check for Direct keys hereIF Shift% : Key&=Key&-32 : ENDIFrem Check for HotKeys hereIF Key&<=300ActionHotKey:ENDIFWe’re going to add the cursor keys into the "Direct Keys" section.
Butbefore we do that, there’s one other thing to consider. What do we dowhen the cursor is at the edge of the board, and the player decides to tryand move the cursor off the board?Before changing the values of CursorX% and CursorY%, we’ll usean IF statement to check if this will push the value to less than zero (offthe top or left-hand edge) or if it will be larger than the width or heightof the board (remember we listed the width and height of the board asconstants – here’s another place we use those values).We’ll also need to check for the ‘fire’ key being pressed to place apiece on the board.
On Series 60 devices this is represented only bythe CBA4 value, so we’ll use the constant that holds the key value forCBA4 in the code. To make things a bit more like the built-in applications(always a good thing), we’ll ensure that pressing the Enter key (commonlyfound on Communicators) will also place a piece:rem Check for Direct keys hereIF Shift% : Key&=Key&-32 : ENDIFIF Key&=KKeyCBA4& OR Key&=KKeySpace% OR Key&=KKeyEnter%MakePlayerMove:(CursorX%,CursorY%,1)90USING GRAPHICS IN AN OTHELLO GAMEELSEIF Key&=KKeyLeftArrow%CursorX%=CursorX%-1IF CursorX%<0CursorX%=0ELSEDrawCursor:OldX%=CursorX%ENDIFELSEIF Key&=KKeyRightArrow%CursorX%=CursorX%+1IF CursorX%>KBoardWidth%CursorX%=KBoardWidth%ELSEDrawCursor:OldX%=CursorX%ENDIFELSEIF Key&=KKeyUpArrow%CursorY%=CursorY%-1IF CursorY%<0CursorY%=0ELSEDrawCursor:OldY%=CursorY%ENDIFELSEIF Key&=KKeyDownArrow%CursorY%=CursorY%+1IF CursorY%>KBoardHeight%CursorY%=KBoardHeight%ELSEDrawCursor:OldY%=CursorY%ENDIFThese lines all follow the same principle of Event Core – when an eventhappens, you do something.
Here it is either moving the cursor in one offour directions, or making a move. Once an event has happened, eitherwe return to the loop and wait for another event, or the conditions forthe end game have been met (either all the squares are full, or the playerhas resigned).Is the Move Legal?But how do we know if a move has been made successfully? Well, thiscomes down to thinking what you do when you make a move yourself. Alegal Othello move is when you capture some of your opponent’s pieces,by moving to a square not already occupied.
This means that you needto sandwich at least one of your opponent’s pieces between yours.Let’s work through this procedure:PROC MakePlayerMove%:(X%,Y%,User%)LOCAL DeltaX%,DeltaY%READING THE PLAYER’S MOVE91The first step, though, is to check that the move is into an empty square(one where the Board%() value of the square equals zero). A simple callto the BoardOut%: procedure can do this:IF BoardOut%:(X%,Y%)<>0RETURN 0ENDIFA simple test, and if the value returned is non-zero, then we’ll leave thisprocedure through the RETURN system, and pass back the value of 0. Thisisn’t used in the player checks, but will be used in the computer moveroutines (of which more later).When we place a piece, it must have at least one opposition piecenext to it.
If you follow that line up, then it needs to come across anotherplayer’s piece before reaching an empty square or the end of the board.The passed value User% lets us know whether we are testing for theplayer (User%=1) or the computer (User%=2):LOCAL DeltaX%,DeltaY%,FooX%,FooY%,Opponent%,Captured%...IF User%=1Opponent%=2ELSEOpponent%=1ENDIFDeltaX%=-2DODeltaX%=DeltaX%+1DeltaY%=-2DODeltaY%=DeltaY%+1IF DeltaX%<>0 AND DeltaY%<>0rem Check Surrounding piece for opponentIF BoardOut%:(X%+DeltaX%,Y%+DeltaY%)=Opponent%DOrem Found an opponent’s piece, let’s carry on upthe lineFooX%=X%+(2*DeltaX%) : FooY%=Y%+(2*DeltaY%)IF BoardOut%:(FooX%,FooY%)=Opponent%FooX%=FooX%+DeltaX% : FooY%=FooY%+DeltaY%ELSEIF BoardOut%:(FooX%,FooY%)=User%rem reverse direction and turn pieces overDOBoardIn:(FooX%,FooY%,User%)Captured%=Captured%+1FooX%=FooX%-DeltaX% :FooY%=FooY%-DeltaY%UNTIL (FooX%=X% AND FooY%=Y%)ELSEIF BoardOut%:(FooX%,FooY%)=0FooX%=-2 : REM Quick way to fulfill the UNTILconditionsENDIF92USING GRAPHICS IN AN OTHELLO GAMEUNTIL (FooX%=X% AND FooY%=Y%) OR FooX%=-1 ORFooX%=KGridWidth% OR FooY%=-1 ORFooY%=KGridHeight%ENDIFENDIFUNTIL DeltaY%=1UNTIL DeltaX%=1RETURN Captured%ENDPWe’ve two or three things going on in here.
The principle is that we willcheck the eight possible directions that a line could result from. We useDeltaX% and DeltaY% for this – they show the change that needs to beapplied to the coordinates of the nominated square (the delta) to checkthe direction. See Figure 5.5.If we come across an opponent’s piece, we’ll follow that direction(using the delta values that represent the direction) down that line untilwe come across one of four things:• another of our opponent’s pieces – in which case we’ll loop aroundagain to check the next piece in line• a blank square – in which case this line is not a sandwich, and doesn’tcount as part of a legal move. We stop checking down this line• the edge of the board – which naturally stops us following this line• we find a player’s piece (a sandwich is made) – so this is a legalmove, and we need to turn some pieces over.
We begin a newloop, starting with this far end of the sandwich, working back towhere the player made the move (the original X%,Y% coordinates),changing the value of all the pieces on the way in Board%() by usingBoardIn:(FooX%,FooY%,User%).Figure 5.5 Delta valuesREADING THE PLAYER’S MOVE93At the end of this, we return the number of captured pieces. If it wasan illegal move, then no pieces were captured and we RETURN 0.The routine calling MakePlayerMove%: will then know if it has towait for another move to be made, or if the computer can make itsmove now.Showing the MoveNow we’ve completed the move, and turned over any pieces that we’vecaptured, we need to show this on the screen (remember, all we’vebeen doing so far is changing values in the array).
We’ll set up twonew procedures, one similar to BoardIn: that will display the piecein a selected square, and a second to step through all the squaresin order:PROC ShowBoard:LOCAL FooX%,FooY%,FooPiece%PlayerScore%=0 : ComputerScore%=0FooX%=-1DOFooX%=FooX%+1FooY%=-1DOFooY%=FooY%+1FooPiece%=BoardOut%:(FooX%,FooY%)ShowPiece:(FooX%,FooY%,FooPiece%)IF FooPiece%=1PlayerScore%=PlayerScore%+1ELSEIF FooPiece%=2ComputerScore%=ComputerScore%+1ENDIFUNTIL FooY%=KGridHeight%-1UNTIL FooX%=KGridWidth%-1ShowAllScores:ENDPPROC ShowPiece:(X%,Y%,Type%)gUSE KBoardWindow%gAT X%*KGridSize%,Y%*KGridSize%gCOPY Id%(KPlayingPieces%,0,0,KGridSize%,KGridSize%,3)ENDPYet again we’re using the two nested DO...UNTIL loops to allow us tocheck through each square on the board. We use KGridWidth% andKGridHeight%, the constants we defined for the size of the board.
Notethat because we start at 0 and not 1, we need to subtract one to get 8squares (square 0 to square 7). As we step through, we look up what pieceis in that square (through the array), and then call the ShowPiece%:94USING GRAPHICS IN AN OTHELLO GAMEroutine. This will cause a ripple effect through the whole board, endingup with all the new pieces on display.Cleaning up the BoardOne advantage in a computer version of a board game such as Othellois that computers are great at keeping score. Inside the ShowBoard:procedure is an IF...ELSEIF...ENDIF statement, which counts thenumber of pieces the player and the computer have.
These are GLOBALvariables, and when we call PROC ShowAllScores: at the end of thisprocedure, these will be displayed on the screen.5.5 The Computer’s Move – Doing A.I.5.5.1 Creating Rules of ThumbMaking a computer play a good game is one of the most interestingchallenges to a programmer. At each move, Othello offers a small numberof choices.