1. Home
  2. Computing & Technology
  3. Delphi Programming
Your First Delphi Game: Tic Tac Toe
Page 3: Player X, your turn!
 More of this Feature
• Page 1: Preparing the GUI
• Page 2: Game Initialization
• Page 4: New Game?
 Join the Discussion
"Post your views and comments to this chapter of the free Delphi Programming Course"
Discuss!
 Related Resources
• A Beginner's Guide to Delphi Programming.TOC

• Free Code Delphi Games
• Free Source Delphi Projects
 From Other Guides
• VB Tic Tac Toe
• Java Tic Tac Toe

Until now we have created the Tic Tac Toe game GUI and we've initialized the variables. We are ready to make our first move.

X, your turn! (or O if you prefer)
Event OnClick for the particular lblCellX component will be called each time a player presses a (left) mouse button on the Label component making the playfield grid.

There is one more thing we have to check before we process the user input. It is very important to be sure that the current game is in progress. In the case that the game is over, we will simply exit this procedure.

After we verify that the game is in progress, we can process the user input - in a procedure GamePlay that expect an integer parameter named CellIndex. CellIndex would have the value set to 0 for the top-left corner cell (label) of the playfield and the value 8 for the bottom-right corner. To get the CellIndex we extract the cell number from the label name, for example, if the label clicked is named lblCell5, the CellIndex for that label (cell) is 5.

To create an OnClick event handler procedure for the first label (lblCell0), simply double click it, or select the lblCell0 component, go to Object Inspector, switch to Events and double click the column right to OnClick. Either way, the Code Editor receives the input focus, and you can enter the programming logic code for the OnClick handler:

procedure TfrMain.lblCell0Click(Sender: TObject);
var
  iWin : integer;
  CellIndex : 0..8;
begin
 if bGameOver = True Then Exit;
 if TLabel(Sender).Caption <> '' then
 begin
  ShowMessage('Cell occupied!');
  Exit;
 end;
 CellIndex := StrToInt(RightStr(TLabel(Sender).Name,1));
 iWin := GamePlay(CellIndex);
end;

Note 0: If the cell is occupied (was clicked before during the game), we exit the procedure. In order to "see" whether the cell is occupied we use the Sender parameter, cast it to TLabel and test the Caption property. If the cell already hosts 'X' or 'O', we display a message using the ShowMessage Delphi function.

Note 1 : to assign a value to CellIndex we use the RightStr function defined in the StrUtils.pas unit. You'll need to manually add this unit to the uses list in the interface uses clause of the Main.pas unit. If you forget to do that, the next time you try to compile your project, the compiler will report an error: "Undeclared Identifier 'RightStr'"!!

Note 2: The above event handler was coded with the following in mind: there is no need to have a separate event handler for each and every of 9 cells making the playfield. What we are preparing for, with the above code, is to use this one procedure for all lblCellX label components and their OnClick events. To have the lblCellX (where X stands for 0 to 8) share this specific OnClick event handler, do the following:
a) select the "rest" of the eight lblCell label components (Shift + Click 'em);
b) Go the Events page of the Object Inspector
c) In the right column to the OnCLick, from the drop down list pick the lblCell0Click.
If you experience difficulties, check the How To Share an Event Handler in Delphi.

Processing the Move
Before we start with processing, let me refresh your memory. iXPos and iOPos arrays hold information about already played moves by the X and O player. iMove tells us how many moves are already made.

The user's choice must be the regular move, which means that user didn't click on the already occupied cell.
If the user makes a regular move, we have to increase the counter (iMove) by one. Then we have to translate the coordinates form one-dimensional to two-dimensional array. The xo_Move value of 0 will be translated to [1,1], 1 to [1,2] ... 3 to [2,1] ... 8 to [3,3].

function TfrMain.GamePlay(xo_Move : Integer):integer;
var
 x, y : 1..3;
 iWin : integer;
begin
 Result := -1;

 Inc(iMove);
 x := (xo_Move Div 3) + 1;
 y := (xo_Move Mod 3) + 1;
...

Also we have to check which player has made the move because this routine will process the input from both players.

 if sPlaySign = 'O' then
 begin
   iOPos[x,y] := 1;
   iWin := CheckWin(iOPos);
 end
 else
 begin
   iXPos[x,y] := 1;
   iWin := CheckWin(iXPos);
 end;

 TLabel(FindComponent('lblCell' + IntToStr(xo_Move))).Caption := sPlaySign;

For example, when the player X places his mark in the top left corner of the playfield, the variables will have the following values:

X makes the move
Take a look at the values when O player makes his move in the middle of the playfield:

O makes the move

After every move we call the CheckWin function to look for the winning combination.

If the player succeeds to win, CheckWin will return the number of the winning combination. If there is no winner, CheckWin will return -1. In case that we have the winner the game will end, the current result will be placed on the scoreboard, and a congratulation message will pop up.

 Result := iWin;

 if iWin >= 0 then
 begin
   bGameOver := True;
   //mark victory

   if sPlaySign = 'X' then
   begin
    iXScore := iXScore + 1;
    lblXScore.Caption := IntToStr(iXScore);
   end
   else
   begin
    iOScore := iOScore + 1;
    lblOScore.Caption := IntToStr(iOScore);
   end;

   ShowMessage(sPlaySign + ' - Wins!');
 end;

If players played nine moves and there is no winner, it is a draw; and the current game is over. On the other hand, if the game is still in progress, we have to allow the other player to make his move.

 if (iMove = 9) AND (bGameOver = False) Then
 begin
  ShowMessage('It''s a Draw!');
  bGameOver := True
 end;

 if sPlaySign = 'O' Then
   sPlaySign := 'X'
 else
   sPlaySign := 'O';
end; //function GamePlay

Ok, we now have the main part of the code logic done, what's left is the CheckWin function.

Do we have a winner?
After each move we have to check if the game is over. Game could end in three ways: X player wins the game, O player wins the game, or it is a draw. To look for a possible winner we count the number of X's and O's in each row, column and diagonal. If the player manages to put three signs in row, column or diagonal, we have the winner.

Player X wins

Here's a portion of the CheckWin function, for the rest of the function look at the project code.

function TfrMain.CheckWin(iPos : TXOPosArray) : Integer;
var
 iScore : Integer;
 i : Integer;
 j : Integer;
begin
 Result := -1;

 //in rows?
 iScore := 0;
 for i := 1 to 3 do
 begin
  iScore := 0;
  Inc(Result);
  for j := 1 To 3 do Inc(iScore, iPos[i,j]);
  if iScore = 3 Then Exit
 end;//for i
...

Note that you will have to add these procedure's headers in the private section of the form declaration, as you did with the InitPlayGround procedure. Now the private section is looking like:

 private
   procedure InitPlayGround;
   function GamePlay(xo_Move : Integer) : integer;
   function CheckWin(iPos : TXOPosArray) : integer;

We now move to the last stage of our Tic Tac Toe development, the creation of the event handlers for 'New Game' and 'Reset Score'...

Next page > New Game? > Page 1, 2, 3, 4

A Beginner's Guide to Delphi Programming: Next Chapter >>
>> Your First MDI Delphi Project

Explore Delphi Programming
About.com Special Features

Holiday Central

What to eat, where to go, fun things to do and how to save money on the perfect gifts. More >

Family Tech Center

Stay connected and entertained with reviews on tips on the latest HDTVs, cellphones and more. More >

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.