Click on the title image to go to the main page
 
 Main Page
 Download
 Documentation
 F.A.Q.
 Contact
 
 
 
SourceForge.net Logo
 
   
Tutorial 1: Tic Tac Toe (alpha 0.1)
Last Updated: 22nd June, 2005, 13:12 GMT
 
Contents
 
 
Introduction
 

Tic-tac-toe using the JOGRE API. This tutorial includes all the source code and images used to create a simple server, client and the various components. This tutorial is suitable for anyone who wants to create their first game using the JOGRE games engine.

At the end of this tutorial you should have something which looks like the following:

 
 
[ top ]
 
 
Why Tic-Tac-Toe?
 

Tic-tac-toe, also called noughts and crosses and many other names, is a paper and pencil game between two players, O and X, who alternate in marking the spaces in a 33 board. A player wins by getting three of their own marks in a horizontal, vertical or diagonal row.

Tic-tac-toe is very simple and this simplicity makes it ideal to use as a tutorial to illustrate the JOGRE API in action.

 
[ top ]
 
 
Creating a New Project
 

The first step for creating a new project/game is to create a new directory underneath the main "jogre" directory i.e. jogre/tictactoe

Inside the jogre/tictactoe directory create source and images folders i.e. tictactoe/src and tictactoe/images. The tictactoe/images folder should contain the following 4 images.

 
jogre_icon.gif tictactoe_icon.gif x.gif o.gif
 

The source folders should be organised as follows:

 
TicTacToe src folders Description
tictactoe/src/org/jogre/tictactoe/awt Location of tic-tac-toe graphical components
tictactoe/src/org/jogre/tictactoe/client Location of tic-tac-toe client specific classes
tictactoe/src/org/jogre/tictactoe/comm Client/Server tic-tac-toe communication objects
tictactoe/src/org/jogre/tictactoe/server Location of tic-tac-toe server specific classes
 

NOTE: All the files have been created for this tutorial have been placed in the c:\Projects\jogre\tictacetoe directory although the jogre\tictacetoe directory can be placed anywhere.

 

The next step is to create a small build file for this project which is stored in the tutorial directory. The build program ANT can be used to compile and copy the properties files into a classes directory.

 
build.xml
<?xml version="1.0" encoding="utf-8"?>
<project name="TicTacToe" default="compile" basedir=".">

    <!-- Source code directory -->
    <property name="src.dir" value="${basedir}/src"/>
    <!-- Class directory -->
    <property name="class.dir" value="${basedir}/classes"/>
    <!-- Path to JOGRE.JAR file which must be included in classpath -->
    <path id="classpath">
        <pathelement location="../api/jogre.jar"/>
    </path>

    <!-- Init. -->
    <target name="init">
        <!-- Create the build directory structure used by compile -->
        <mkdir dir="${class.dir}"/>
    </target>

    <!-- Compile  -->
    <target name="compile" depends="init"
        description="compile the source " >

        <!-- Compile the java code from ${src} into ${classes} -->
        <javac srcdir="${src.dir}" destdir="${class.dir}">
            <classpath refid="classpath"/>
        </javac>

        <copy todir="${class.dir}">
            <fileset dir="${basedir}">
                <include name="*.properties"/>
            </fileset>
        </copy>
    </target>
</project>
 

The final step is to create two property files, game.properties and game_labels.properties. These are both stored in the tictactoe directory.

 
game.properties
# Version
version=0.1 beta

# Declare minimum and maximum number of players for this game
min.num.players=2
max.num.players=2

# Must be a "colour.1" to "colour.x" where x = max.num.players
player.colour.0=255,255,255
player.colour.1=0,0,0
 
game_labels.properties
# Server
jogre.server.title=JOGRE TicTacToe Server

# Client
jogre.client.title=JOGRE TicTacToe Client
jogre.client.welcome.message=Welcome to JOGRE TicTacToe!
jogre.table.title=Jogre TicTacToe Table

# Must be a "player.label.0" to "player.label.x"
# where x = max.num.players - 1 (from game.properties)
player.label.0=X
player.label.1=O
 

Most of these properties are self explanitory and simply set the name of the server, client and the welcome message that a client recieves. The player.label.x properties are the names of the player when they sit down at a table. For Tic-tac-toe these are "X" and "O". Chess for example is "White" and "Black".

 
[ top ]
 
 
Creating a Server
 

To implement a server two classes must be created which are TicTacToeServer and TicTacToeServerConnectionThread which extend the JogreServer and ServerConnectionThread from the JOGRE API respectively. Both these classes should be stored in the src/org/jogre/tictactoe/server directory. The following clickable image shows these two classes as a UML diagram.

 
 

As the diagram illustrates, both these classes extend classes from the JOGRE API. These two classes will now be explained in more detail using their source code. Any items on the source which are in bold are of some interest and will be discussed further.

 
TicTacToeServer.java
package org.jogre.tictactoe.server;

import java.net.Socket;

import org.jogre.common.JogreModel;
import org.jogre.server.JogreServer;
import org.jogre.server.ServerConnectionThread;

// TicTacToeServer Server.
public class TicTacToeServer extends JogreServer {

  // Constructor
  public TicTacToeServer (String [] args) {
    super (args); // Call super constructor and pass in command line args
  }

  // Return the correct type of connection thread for this server.
  public ServerConnectionThread connect (Socket connection, JogreServer server) {
    return new TicTacToeServerConnectionThread (connection, server);
  }

  // Return the correct game model (ignore)
  public JogreModel getJogreModel () {
    return null;
  }

  // Main method.
  public static void main (String [] args) {
    TicTacToeServer server = new TicTacToeServer (args);
    server.run ();      // Run the server
  }
}
 

The TicTacToeServer class contains a main method which creates an instance of itself and then runs. Arguments can passed to the server from the command line such as -port 1790 (sets the port to listen on). The class JogreServer which TicTacToeServer extends contains two abstract methods connect (Socket connection, JogreServer server) and getJogreModel ().

The connect method returns the correct type of connection thread (TicTacToeServerConnectionThread) for the server. This class is discussed next.

If a server stores the state of a game (and a user can join/view a game at any time) then the getJogreModel method sets the correct type of JogreModel. To make this tutorial simple we have decided not to store the state of a game and so this method simply returns null.

 
TicTacToeServerConnectionThread.java
package org.jogre.tictactoe.server;

import java.net.Socket;

import org.jogre.common.TransmissionException;
import org.jogre.server.JogreServer;
import org.jogre.server.ServerConnectionThread;

// Server thread which deals with any client requests.
public class TicTacToeServerConnectionThread extends ServerConnectionThread {

  // Constructor
  public TicTacToeServerConnectionThread (Socket clientSocket, JogreServer server) {
    super(clientSocket, server);
  }

  // This method reads a message going to the server and does something with it
  public void parse (String messageType, String inString) throws TransmissionException {
    if (messageType.equals ("Undefined")) {
      // Will fill this in later
    }
    else
      super.parse (messageType, inString);  // Super classes deals with most messages
  }
}
 

Each time a client connects to a server a new Thread is created which sits and deals with any requests coming from the client until the client logs off. The role of the TicTacToeServerConnectionThread class is to deal with any TicTacToe specific commands such as a player making a move. Its constructor recieves a socket and a link to the JogreServer class which are passed up to the super class.

This class extends ServerConnectionThread class which processes client logons, chat messages, table requests etc. For this class to process something from the client the extends parse (String messageType, String inString) method is overwritten. The parameter messageType is then checked and depending on what is the class processes it. This will be filled in later as required.

The next step is to compile these two classes using the ANT build.xml file or your favourite IDE. The Open Source IDE Eclipse is very good (JOGRE has been created primarily using Eclipse). If using an IDE / commmand line ensure that the jogre.jar file is included in the CLASSPATH. Also the compiled classes should be stored in the classes folder and the two properties files ( game.properties and game_labels.properties) are also copied into this directory (the ANT build file does this automatically).

 
[ top ]
 
 
Running a Server
 

Once the two server files have been compiled OK the next step is running the server. This is achieved by going to the jogre/tictactoe folder and typing the following java command:

java -classpath classes;..\api\jogre.jar org.jogre.tictactoe.server.TicTacToeServer

If the command is sucessful then the Server has been created and will list for client requests on the specified port (1790).

 
 
[ top ]
 
 
Creating a Client Frame/Table
 

Once a server has been sucessfully created the next step is to create a tic-tac-toe client frame which can connect to the server and interact with other users.

To create a client frame four classes must be created which are TicTacToeClientConnectionThread, an interface ITicTacToeClientClient, TicTacToeClientFrame and TicTacToeTableFrame. These three classes and one interface extend classes / interfaces from the JOGRE API. All these classes should be stored in the src/org/jogre/tictactoe/client directory.

 
Location Class/Interface Name Synopsis
org/jogre/tictactoe/client TicTacToeClientConnectionThread TicTacToe Client connection thread which sends/recieves information to/from the server.
ITicTacToeClient Interface which TicTacToeClientFrame implements and the TicTacToeConnectionThread delegates calls through.
TicTacToeClientFrame Tic-tac-toe client frame.
TicTacToeTableFrame Tic-tac-toe table frame.
 
The following clickable image shows these four classes as a UML diagram.
 
 

Once again, all these classes extend classes from the JOGRE API. These three classes will now be explained in more detail using their source code. Any items on the source which are in bold are of some interest and will be discussed further.

 
TicTacToeClientConnectionThread.java
package org.jogre.tictactoe.client;

import java.net.Socket;

import org.jogre.client.ClientConnectionThread;

// TicTacToe Client connection thread which sends/recieves information to/from the server.
public class TicTacToeClientConnectionThread extends ClientConnectionThread {

  protected ITicTacToeClient clientInterface;

  // Constructor for a client connection thread
  protected TicTacToeClientConnectionThread (
      Socket connection, String username, ITicTacToeClient clientInterface) {
    super(connection, username, clientInterface);

    this.clientInterface = clientInterface;    // link to client frame (through interface)
  }

  // This method reads a message going to the server and does something with it
  public void parse (String messageType, String inString) {
    if (messageType.equals ("Undefined")) {
      // Will fill this in later
    }
    else
      super.parse (messageType, inString);  // Super classes deals with most messages
  }
}
 

The TicTacToeClientConnectionThread class extends the ClientConnectionThread from the JORE API which sits in its own thread listening to Strings coming down the socket from the ServerConnectionThread (TicTacToeServer). The important method here is the parse method as it parses the message coming from the server. This will be filled in later in the tutorial when we create a small TicTacToe user move communication object.

 
ITicTacToeClient.java
package org.jogre.tictactoe.client;

import org.jogre.client.IClient;

// Interface which sits between the ClientConnectionThread a frame.
public interface ITicTacToeClient extends IClient {

  // Fill in later
}
 

The next file we create is an interface called ITicTacToeClient which extends the JOGRE API interface IClient. When a String is sent from the TicTacToeServer to a TicTacToeClientConnectionThread, it is then passed to a method in the TicTacToeClientFrame which must implement this interface. This inteface decouples the connection between the GUI and the network connection.

 
TicTacToeClientFrame.java
package org.jogre.tictactoe.client;

import java.net.Socket;

import org.jogre.awt.JogreClientFrame;
import org.jogre.awt.JogreTableFrame;
import org.jogre.client.ClientConnectionThread;
import org.jogre.client.IClient;

// Tic-tac-toe client frame
public class TicTacToeClientFrame extends JogreClientFrame implements ITicTacToeClient {

  public TicTacToeClientFrame () {
    super ();
  }

  // Return the correct client connection thread
  public ClientConnectionThread getClientConnectionThread (
    Socket connection, String username, IClient clientInterface
  ) {
    return new TicTacToeClientConnectionThread (
      connection, username, (ITicTacToeClient)clientInterface);
  }

  // Return the correct table frame (each game).
  public JogreTableFrame getJogreTableFrame (ClientConnectionThread conn, int tableNum) {
    return new TicTacToeTableFrame (conn, tableNum);
  }

  // Main method where this class gets run.
  public static void main (String [] args) {
    TicTacToeClientFrame frame = new TicTacToeClientFrame ();
  }
}
 

The TicTacToeClientFrame is class which is executed as it contains a main method. This class extends JogreClientFrame and must implement 2 of its abstract methods: 1) getClientConnectionThread which returns the correct thread (a TicTacToeClientConnecionThread) to the super class and 2) getJogreTableFrame which will return a TicTacToeTableFrame where each of the games are played.

 
TicTacToeTableFrame.java
package org.jogre.tictactoe.client;

import javax.swing.*;

import org.jogre.awt.JogreTableFrame;
import org.jogre.client.ClientConnectionThread;

// Jogre table frame
public class TicTacToeTableFrame extends JogreTableFrame {

  // Constructor which passes a client connection thread and a table
  public TicTacToeTableFrame (ClientConnectionThread conn, int tableNum) {
    super(conn, tableNum);
    
    // Set icon
    ImageIcon barIcon = new ImageIcon ("images/tictactoe_icon.gif");
    this.setIconImage(barIcon.getImage());
    
    // Fill in rest later
  }
}
 

The TicTacToe table frame is where each game is going to be played. This will be filled in later.

 
[ top ]
 
 
Running a client
 

Once these files have been compiled OK and a server is running then a client can execute and connect to the server. This is achieved by going to the jogre/tictactoe folder and typing the following java command:

java -classpath classes;..\api\jogre.jar org.jogre.tictactoe.client.TicTacToeClientFrame

If the command is sucessful then the client will be created and connect to the server on the specified port (1790). You should see something like the following:

 
 

As you can see the JogreTableFrame is blank and does not contain the actual game. This is created in the next section.

 
[ top ]
 
 
The Tic-Tac-Toe MVC (Model/View/Controller)
 

Now that the server, client frame and table frame have been set up the next step is to create the actual game of TicTacToe. This is achieved using a MVC (model/view/controller) style architecture. Click here for a website which explains this architecture and its benefits.

To create the game of TicTacToe another three classes are going to be created and initilised in the TicTacToeTableFrame class. These classes are as follows:

 
Location Class/Interface Name Synopsis
org/jogre/tictactoe/client TicTacToeModel Model which holds the data for a game of TicTacToe.
TicTacToeController Controller which updates the model and listens to user input on the visual board.
org/jogre/tictactoe/awt TicTacToeBoardComponent TicTacToe visual board component (view of the model).
 

The following clickable image shows these three MVC classes as a clickable UML diagram.

 
 

Once again, all these classes extend classes from the JOGRE API. These three classes will now be explained in more detail using their source code. Any items on the source which are in bold are of some interest and will be discussed further.

 
TicTacToeModel.java
package org.jogre.tictactoe.client;

import org.jogre.common.JogreModel;

// Model which holds the data for a game of TicTacToe
public class TicTacToeModel extends JogreModel {

  // Declare constans to define model
  public static final int BLANK = -1;
  public static final int X     = 0;
  public static final int O     = 1;

  // model (3x3 two dimensional integer array)
  private int [][] data = new int [3][3];

  // Constructor
  public TicTacToeModel() {
    super();
    reset ();
  }

  // Start method which calls reset method (resets the model).
  public void start() {
    reset ();
  }

  // reset the model back to zeros
  private void reset () {
    for (int x = 0; x < 3; x++) {
      for (int y = 0; y < 3; y++) {
        data [x][y] = BLANK;
      }
    }
    refreshObservers();     // inform view
  }

  // return data at a particular point
  public int getData (int x, int y) {
    return data [x][y];
  }

  // set data at a point
  public void setData (int x, int y, int value) {
    data [x][y] = value;
    refreshObservers();   // update any views on this model
  }
}
 

The model class TicTacToeModel class extends the JogreModel and must implement the abstract start () method which is called when a game starts (reset the data). This class holds a 3x3 two dimensional integer array for holding the data at each position on the TicTacToe board. The values in this array are either -1 (blank), 0 (X) or 1 (0). There are also getter and setter methods for accessing / updating this array. An important method call is the refreshObservers() in the setData method. This updates any view which is registered with this model.

The next class discussed is the view part of the MVC architecture which is the TicTacToeBoardComponent.java.

 
TicTacToeBoardComponent.java
package org.jogre.tictactoe.awt;

import java.awt.*;
import javax.swing.ImageIcon;

import org.jogre.awt.AbstractBoardComponent;
import org.jogre.tictactoe.client.TicTacToeModel;

// TicTacToe visual board component (view of the model).
public class TicTacToeBoardComponent extends AbstractBoardComponent {
  
  // Declare constants which define what the board looks like
  private static final int NUM_OF_ROWS_COLS = 3;
  private static final int CELL_SIZE = 100;
  private static final int CELL_SPACING = 1;
  private static final int BORDER_SPACING = 0;
  
  // Link to the model
  protected TicTacToeModel model;
  
  // Images for an X and an O
  private static final ImageIcon [] images = {
    new ImageIcon ("images/x.gif"),
    new ImageIcon ("images/o.gif")};
  
  // Constructor which creates the board
  public TicTacToeBoardComponent (TicTacToeModel model) {
    // Call constructor in AbstractBoardComponent
    super (NUM_OF_ROWS_COLS, NUM_OF_ROWS_COLS, CELL_SIZE, CELL_SPACING,
         0, 0, false, false, false);

    this.model = model;     // link to model

    // set colours
    setColours (Color.white, Color.white, Color.black, Color.white);
  }

  // Update the graphics depending on the model
  public void paintComponent (Graphics g) {
    // Draw the board (AbstractBoardComponent)
    super.paintComponent (g);

    // draw each TicTacToe piece on the board (loop through the model)
    for (int x = 0; x < 3; x++) {
      for (int y = 0; y < 3; y++) {
        // get piece from model
        int piece = model.getData(x, y);

        // Now get screen co-ordinates (AbstractBoardComponent)
        Point screen = getScreenCoords (x, y);
        
        // If piece isn't an blank piece then draw the image on the screen
        if (piece != TicTacToeModel.BLANK) {
          Image image = images [piece].getImage();
          g.drawImage (image, screen.x, screen.y, null);  // draw image
        }
      }
    }
  }
}
 

The TicTacToeBoardComponent class extends an AbstractBoardComponent which is a component for creating boards (ChessBoardComponent and CheckersBoardComponent both extend this class). AbstractBoardComponent extends a JogreComponent which extends a JComponent from the Java Swing classes and also implements the Observer interface. This means a reference of this class can be added TicTacToeModel class as an Observer (i.e. when the model changes this class refreshes itself).

A reference to the TicTacToeModel is passed into the constructor of an TicTacToeBoardComponent. To actually render the board the paintComponent method from JComponent is overwritten. The data of the TicTacToeModel is examined and images are rendered onto the component accordingly.

 
TicTacToeController.java
package org.jogre.tictactoe.client;

import java.awt.Point;
import java.awt.event.MouseEvent;

import org.jogre.client.JogreController;
import org.jogre.tictactoe.awt.TicTacToeBoardComponent;

// Controller which updates the model and listens to user input on the visual board.
public class TicTacToeController extends JogreController {
  
  // links to game data and the board component
  protected TicTacToeModel model;
  protected TicTacToeBoardComponent boardComponent;
  
  // Constructor
  public TicTacToeController(TicTacToeModel model, TicTacToeBoardComponent boardComponent) {
    super(model, boardComponent);

    this.model = model;           // set fields
    this.boardComponent = boardComponent;
  }
  
  // Overwrite the mousePressed method
  public void mousePressed (MouseEvent e) {

    // ensure game is started and it is this players turn
    if (isGamePlaying() && isThisPlayersTurn ()) {

      // get board point from screen position of mouse click
      Point board = boardComponent.getBoardCoords (e.getX(), e.getY());
      
      // ensure board point is in range (0 to 2)
      if (board.x >= 0 && board.x < 3 && board.y >= 0 && board.y < 3) {
        
        // check model at this point is BLANK and it is this persons go
        if (model.getData(board.x, board.y) == TicTacToeModel.BLANK) {

          int value = getCurrentPlayerSeatNum();
          move (board.x, board.y, value);   // move

          checkGameOver ();   // check to see if the games overv
        }
      }
    }
  }

  // Update the model
  public void move (int x, int y, int value) {
    // update model and change player turn
    model.setData (x, y, value);

    // next player turn.
    nextPlayer ();
  }

  // Check to see if the game is over
  private void checkGameOver () { }    // fill in later.
}
 

The TicTacToeController controller class updates the model and listens to user input on the TicTacToeBoardComponent. This class extends the JogreController class which is an adapter class which implements all the methods from MouseMotionListener, MouseListener and KeyListener. Any of these methods can be overwritten to do something.

In TicTacToeController the mousePressed method is overwritten. This method starts with an if statement which checks that the game is in play and it is this particular players turn to make a move. Both of these useful methods exist in the super class JogreController. The screen co-ordinate of the mouse click is then converted into a board point (between 0 and 2). This is done using the getBoardCoords of AbstractBoardComponent. The point is checked to see that its range and then the TicTacToeModel is checked to ensure that the use is clicking on a free space (TicTacToeModel.BLANK). The model is then updated and the turn changes to the other player using the nextPlayer method.

 
TicTacToeTableFrame.java
package org.jogre.tictactoe.client;

import javax.swing.*;

import org.jogre.awt.JogreTableFrame;
import org.jogre.client.ClientConnectionThread;
import org.jogre.tictactoe.awt.TicTacToeBoardComponent;

// Jogre table frame
public class TicTacToeTableFrame extends JogreTableFrame {

  private TicTacToeModel model;                        // model reference
  private TicTacToeBoardComponent boardComponent;      // board reference
  private TicTacToeController controller;              // controller reference

  // Constructor which passes a client connection thread and a table
  public TicTacToeTableFrame (ClientConnectionThread conn, int tableNum) {
    super(conn, tableNum);

    // Set icon
    ImageIcon barIcon = new ImageIcon ("images/tictactoe_icon.gif");
      this.setIconImage(barIcon.getImage());

    // Create model, view and register view to model
    model = new TicTacToeModel ();    // create model
    boardComponent = new TicTacToeBoardComponent (model); // board
    model.addObserver(boardComponent);          // board observes model

    // Create controller which updates model and controlls the view
    controller = new TicTacToeController (model, boardComponent);
    controller.setConnection(conn, tableNum);       // connection
    boardComponent.setController (controller);

    // Add view to a panel and set on table frame
    JPanel panel = new JPanel ();   // create a panel
    panel.add (boardComponent);     // add board to panel
    setGamePanel (panel);           // add panel to table frame

    // Set up MVC classes in super class (CONSTRUCTOR MUST CALL THIS)
    setupMVC (model, boardComponent, controller);
  }
}
 

One of the key classes in the game TicTacToeTableFrame must be updated now to hold references of the MVC classes, TicTacToeModel, TicTacToeBoardComponent and TicTacToeController. These are declare in the constructor. The most independent of these is the model (TicTacToeModel) which is declared first. The view (TicTacToeBoardComponent) is then declared and added as an observer to the model (when the model changes the board will repaint automatically).

The controller (TicTacToeController) is then declared which takes both the model and the view as parameters in its constructor. The connection to the server is then set on the controller and controller is then set on the view. Next the view is added to the TitTacToeTableFrame in the middle of the screen using the setGamePanel method. Finally, the last statement is an important one as it registers the MVC classes in the super class which will make use of them.

To test this, recompile the classes again and run a server and two clients. Get one of the clients to create a table which the other client should join. Both players should then sit down and start a game. If everything was sucessful then you should see the clients update themself but not each other.

 
 
[ top ]
 
 
Server communication
 

For the clients to update each other the TicTacToe move must be sent to the TicTacToeServerConnectionThread which will send the move to the client (TicTacToeClientConnectionThread) which will call a method in TicTacToeClientFrame through the ITicTacToeClient interface. The TicTacToeClientFrame will examine the message, figure out which table it is for and delegate to the correct table. The TicTacToeTableFrame will then update the TicTacToeModel.

 
 
CommTicTacToeMove.java
package org.jogre.tictactoe.comm;

import java.util.StringTokenizer;

import org.jogre.comm.CommTableMessage;
import org.jogre.common.TransmissionException;

// Immutable communication object for a tic tac toe move.
public class CommTicTacToeMove extends CommTableMessage {

  public static final String TIC_TAC_TOE = "TIC_TAC_TOE";    // header token

  private int x, y, value;   // position of move

  // Constructor which takes a move from a player.
  public CommTicTacToeMove (String username, int tableNum, int x, int y, int value) {
    super (username, tableNum);

    // set x and y
    this.x = x;
    this.y = y;
    this.value = value;
  }

  // Constructor which takes a String from a flatten () method.
  public CommTicTacToeMove (String inString) throws TransmissionException {
    super ();
    StringTokenizer st = new StringTokenizer (inString);
    st.nextToken();       // read header token
    try {
      this.username = st.nextToken();
      this.tableNum = Integer.parseInt(st.nextToken());
      this.x = Integer.parseInt(st.nextToken());
      this.y = Integer.parseInt(st.nextToken());
      this.value = Integer.parseInt(st.nextToken());
    }
    catch (NumberFormatException nfEx) {
      throw new TransmissionException ("Error parsing string: " + inString);
    }
  }

  // Flatten the fields of this object into a String
  public String flatten () {
    return TIC_TAC_TOE + " " + username + " " + tableNum + " " +
           x + " " + y + " " + value;
  }

  // get x
  public int getX () { return x; }

  // get y
  public int getY () { return y; }

  // get value
  public int getValue () { return value; }
}
 

The first class we will look at is CommTicTacToeMove which is stored in the src\org\jogre\tictactoe\comm folder. CommTicTacToeMove is a small class which can be transferred as a String across a Socket. This class implements the ITransmittable interface i.e. contains a flatten method which can turn the object into a String. The class must also contain a constructor with a single String parameter for converting the String back into an object.

This communication object contains the username of the player who made the move, table number of game, x and y position of move and the value of the move (either X or O).

 
TicTacToeController.java
  // (New import - don't copy line)

  import org.jogre.tictactoe.comm.CommTicTacToeMove;

  // (New method - don't copy line)

  // Update the model
  public void move (int x, int y, int value) {
    // update model and change player turn
    model.setData (x, y, value);

    // next player turn.
    nextPlayer ();

    // send move to other user
    CommTicTacToeMove move = new CommTicTacToeMove
      (conn.getUsername(), tableNum, x, y, value);
    conn.send(move);
  }
 

The controller (TicTacToeController) is the class that creates and sends the communication object. This is done in the move method and is sent to the server via the TicTacToeClientConnectionThread using its send method.

 
TicTacToeServerConnectionThread.java
  // (New import - don't copy line)

  import org.jogre.tictactoe.comm.CommTicTacToeMove;

  // (Update method - don't copy line)

  // This method reads a message going to the server and does something with it
  public void parse (String messageType, String inString) throws TransmissionException {
    if (messageType.equals (CommTicTacToeMove.TIC_TAC_TOE)) {
      // Create communication object and send other table
      CommTicTacToeMove move = new CommTicTacToeMove (inString);
      transmitToTable (move.getUsername(), move.getTableNum(), move);
    }
    else
      super.parse (messageType, inString);  // Super classes deals with most messages
  }
 

Once the communication object is sent to the server (TicTacToeServerConnectionThread) it is passed to its parse method. The header token is checked and if it is of type CommTicTacToeMove.TIC_TAC_TOE the CommTicTacToeMove is reconstructed and sent to the correct table (omitting the person who sent the message).

 
ITicTacToeClient.java
package org.jogre.tictactoe.client;

import org.jogre.client.IClient;
import org.jogre.tictactoe.comm.CommTicTacToeMove;

// Interface which sits between the ClientConnectionThread a frame.
public interface ITicTacToeClient extends IClient {

  public void ticTacToeMove (CommTicTacToeMove move);
}
 

The interface (ITicTacToeClient) which TicTacToeClientFrame implements must be updated so that it can read the move from the server.

 
TicTacToeClientConnectionThread.java
  // (New import - don't copy line)

  import org.jogre.tictactoe.comm.CommTicTacToeMove;
  import org.jogre.common.TransmissionException;

  // (Update method - don't copy line)

  // This method reads a message going to the server and does something with it.
  public void parse (String messageType, String inString)  {
    try {
      if (messageType.equals (CommTicTacToeMove.TIC_TAC_TOE)) {
        // Create communication object and send other table
        CommTicTacToeMove move = new CommTicTacToeMove (inString);
        clientInterface.ticTacToeMove (move);
      }
      else
        super.parse (messageType, inString);  // Super classes deals with most messages.
    }
    catch (TransmissionException transEx) {
      transEx.printStackTrace();
    }
  }
 

Now the thread (TicTacToeClientConnection) which listens to messages coming from the server (TicTacToeServerConnection) must be updated so that it can read this communication object. Its parse method is updated and the move is passed to the client (TicTacToeClientFrame) through the interface (ITicTacToeClient).

 
TicTacToeClientFrame.java
  // (New import - don't copy line)

  import org.jogre.tictactoe.comm.CommTicTacToeMove;

  // (New method - don't copy line)

  // Tic tac toe move
  public void ticTacToeMove (CommTicTacToeMove move) {
    // retreive correct table and send message to it
    int table = move.getTableNum();
    TicTacToeTableFrame frame = (TicTacToeTableFrame)getTableFrame (table);
    frame.ticTacToeMove (move);     // delegate to the correct table
  }
 

The client frame (TicTacToeClientFrame) must implement the new ticTacToeMove method which has been added to the ITicTacToeClient interface. The table number from the object is used to retrieve the correct TicTacToeTableFrame and the method ticTacToeMove in the table frame is then called.

 
TicTacToeTableFrame.java
  // (New import - don't copy line)

  import org.jogre.tictactoe.comm.CommTicTacToeMove;

  // (New method - don't copy line)

  // Tic tac toe move
  public void ticTacToeMove (CommTicTacToeMove move) {
    model.setData (move.getX(), move.getY(), move.getValue());
  }
 

Finally the new ticTacToeMove method is inserted into the TicTacToeTableFrame class which updates the model (model.setData()) with the other players move.

To test this, recompile the classes again, run a new server, two new clients and run a quick game. This time when a player makes a move the controller will update the model and send the move to the server as a comm object which the other user should recieve. The last thing to do is is to know when a game is over. This is explained in the next section.

 
 
[ top ]
 
 
Game Over
 

In TicTacToe a game can end with a player winning (getting 3 of his pieces in row) or end in a draw (no spaces left on the board with noone winning). The following images shows how a game can be won.

 
 

To implement game over checking, two methods were added to TicTacToeModel and the checkGameOver method in the TicTacToeController is implemented.

 
TicTacToeModel.java
  // (2 New methods - don't copy line)

  // return true if the game is won by this player
  public boolean isGameWon (int player) {
    // check y axis
    for (int x = 0; x < 3; x++) {
      int count = 0;
      for (int y = 0; y < 3; y++) {
        if (data[x][y] == player)
          count++;
      }
      if (count == 3) return true;
    }
    // check x axis
    for (int y = 0; y < 3; y++) {
      int count = 0;
      for (int x = 0; x < 3; x++) {
        if (data[x][y] == player)
          count++;
      }
      if (count == 3) return true;
    }
    // check diagonals
    if (data[0][0] == player && data [1][1] == player && data [2][2] == player)
      return true;
    if (data[0][2] == player && data [1][1] == player && data [2][0] == player)
      return true;
    return false;
  }

  // return true if all 9 spaces are taken
  public boolean isNoCellsLeft () {
    int count = 0;
    // check each cell
    for (int x = 0; x < 3; x++) {
      for (int y = 0; y < 3; y++) {
        if (data[x][y] != BLANK)
          count++;
      }
    }
    return (count == 9);    // if all 9 spaces filled return true
  }
 

The isGameWon method checks the int array in the TicTacToe horizontally, vertically and diagonally. It returns true if a full row is detected. The isNoCellsLeft method does a count of all the non blank cells in the int array. If its equal to nine it returns true (no empty cells).

 
TicTacToeController.java
  // (New import - don't copy line)

  import org.jogre.comm.CommGameOver;

  // (Update method - don't copy line)

  // Check to see if the game is over or not.
  private void checkGameOver () {
    // Status is either -1, DRAW or WIN
    int status = -1;
    if (model.isGameWon (getCurrentPlayerSeatNum()))
      status = CommGameOver.WIN;
    else if (model.isNoCellsLeft ())
      status = CommGameOver.DRAW;

    // Create game over object if a win or draw
    if (status != -1 && conn != null) {
      CommGameOver gameOver = new CommGameOver
        (conn.getUsername(), status, tableNum, 0);
      conn.send (gameOver);
    }
  }  
 

The checkGameOver method in the TicTacToeController is updated to check the TicTacToeModel for a win or a draw. A status integer is then set and a CommGameOver communications object is then created and sent to the server.

This brings the TicTacToe tutorial to an end. Its aim is to illustrate implementation of a simple game using the JOGRE API using screenshots, UML diagrams and full source code listings. If you have any comments on this tutorial, then feel free to contact its author Bob Marks.

 
[ top ]
 
 
 
Copyright 2004-2008, JOGRE API and JOGRE Games, by Bob Marks