In my last post, I explained my logic/thinking behind 2 of the 3 files that make up Tic-Tac-Toe V1:
GamePiece.mxml. Click here to play the game (right click to view/download the source). In this post, I’ll breakdown the remaining piece.
This piece is the real workhorse of the game. It houses not only the board where the pieces are laid out, but also the game logic itself.
Was that the best decision?
Probably not. If I wanted to swap out the game rules but keep the same pieces, I couldn’t do that. It’s not so common with Tic-Tac-Toe, but think of a card game. One deck of 52 cards can play an almost infinite number of games. Would it make much sense to put the Solitaire logic right inside the
CardDeck class file? Nope, it surely wouldn’t. I was in a hurry though so I did.
Let’s start off with the states. You’ll notice there’s only 2 states:
- The Base State – Here I have the the lines of the board drawn out. You’ll see that I went the cheap route regarding the grid of the game board. No fancy drawing APIs and no background image. Just four rules (2 vertical and 2 horizontal) make up the grid. You can see that I also manually lay out the 9 instances of the
GamePiececlass, giving them descriptive
idvalues such as “TopRight”, “BottomLeft”, etc. I’ll explain why later.
- “BlankSlate” – For this state, I pretty much remove the game pieces and the grid that “houses” them. In their place, I put a label up that gives the title of the game and a button that resets the board back to the base state.
Why is the BlankSlate not blank?
It was initially, hence the name. Then I thought, “Well, I need to tell the user what the name of the game is and let them start it.” So I added those items to the
blankSlate state. I should have either renamed the state or created a new state based on
<mx:Canvas ...other code... currentState="blankSlate">
By default, I set the
currentState of the
GameBoard class to
blankSlate in the root Canvas tag.
<mx:Button x="134.5" y="216" label="Start Game" click="reset()"/>
If we take a look at the button we add in the
blankState, we see that I call the
reset function when clicked. Let’s take a look at that function.
public function reset():void
lastPiece = "O";
winnerFound = false;
currentState = "";
It calls a function nameed
resetPieces(). That function just calls
reset() on the individual pieces to make sure they’re ready to be played. It preps the board by setting the properties back to values that we’ll see used later. It also sets the
currentState back to default, so that we get the board and pieces back on the stage.
<ns1:GamePiece x="4" y="4" id="TopLeft" click="pieceChosen(TopLeft)">
If you look at the pieces, you’ll see that there’s a click event handler. It calls the
pieceChosen function. Let’s take a look at that function.
public function pieceChosen(piece:GamePiece):void
In the declaration, we see that it expects a
GamePiece to be passed into it. Therefore, each piece passes in a reference to itself.
NOTE: We can’t use the
this keyword to pass in a reference to the
GamePiece instance. This is because in the MXML, the
this refers to the containing class (in this case,
GameBoard) and not the tag that has the handler code. Confusing? Try this. Look at the
click handler. You give it a function and that function is not in the
GamePiece class, but rather the parent class. Same thing with the
Let’s get back to the
var message:String = new String();
We setup a
message variable to hold a message for the end. This will be used to tell the players who won or if there was a tie.
if (!piece.used && !winnerFound)
Our first statement checks to make sure the piece is playable and that no one has won the game yet.
if (lastPiece == "O")
piece.currentState = "X";
lastPiece = "X";
piece.currentState = "O";
lastPiece = "O";
piece.used = true;
It sets the piece to the appropriate state based on the last piece played and resets the
lastPiece value. After that, it marks the
piece as being
Next, it checks for a winner. This function is probably the ugliest of the code base for two reasons:
- It’s horribly hardcoded. You must know every possible winning combination and hard code it.
- It’s terribly inefficient. It checks every possible combination in the same order each time, even if pieces that aren’t part of the winning combination weren’t played this turn.
Let’s take a look at a little snippet from the
if (TopLeft.currentState == lastPiece && MiddleLeft.currentState == lastPiece && BottomLeft.currentState == lastPiece)
As you can see, whether any of the -Left piece got played just now doesn’t really matter. It’s gonna check if those 3 pieces magically got turned on and won the game.
The logic inside the if statements isn’t so bad except the first line.
TopLeft.currentState = MiddleLeft.currentState = BottomLeft.currentState = lastPiece + "Winner";
Surely, there’s a function (let’s call it
checkCombination) that I can extract from that logic. Because if I change the name of the winning state, I’ll have to go through and modify it in 8 places. Regardless, this works for changing the winning pieces to their proper winning state.
winnerFound = true;
The two lines above our nice, but again we can detect the
winnerFound value in the
checkCombination function I propose above. We can then have that new function return our true/false value for us. (I’ll do that in the next iteration.)
checkForWinner function comes back true, than we’re done.
message = "Player " + lastPiece + " won. ";
We set the
message to the proper winner in preparation for the announcement.
What if no winner was found though, should we just let the next player go?
At first, that’s what I did. Then it dawned on me, “What if that’s the last playable piece? It’s a cats game and I gotta let the players know.” Therefore, I created the
checkForTie() function. It checks for one thing only: Are all the pieces used?
if (TopLeft.used == true && MiddleLeft.used == true && BottomLeft.used == true && TopMiddle.used == true && MiddleMiddle.used == true && BottomMiddle.used == true && TopRight.used == true && MiddleRight.used == true && BottomRight.used == true)
Once again, not efficient and surely not pretty, but it gets the job done. I need to rewrite that so that it’s a nice iterative loop that inspects each piece programmatically vs via a hardcoded list. It then returns
false depending on if all the pieces are used or not.
Back in the
pieceChosen function, we either continue playing the game or set the message to tie.
Alert.show( message + "Would you like to play again?", "Game Over", 3, this, playAgainHandler);
Lastly, if we haven’t moved on to the next turn, we pop up an alert. Here we display the message and ask if they want to play again. The
playAgainHandler takes care of their choice.
currentState = "blankSlate";
reset the board in either case. Then if they don’t want to play anymore, we return the board to the
That’s it! There’s the entire code for version 1 of my Tic-Tac-Toe game. It’s time to create Version 2! Stay tuned! It’ll mainly be optimizations to the code base vs a new look and feel. The latter won’t likely come until version 3.