Click on the title image to go to the main page
 
 Main Page
 Download
 Documentation
 F.A.Q.
 Contact
 
 
 
SourceForge.net Logo
 
   
Tutorial 3: Connect 4 (beta 0.3)
Last Updated: 8th Aug, 2008, 20:01 GMT
 
Contents
 
 
Introduction
 

This tutorial shows how the turn-based game of Connect 4 can be created using the JOGRE 0.3 API. This tutorial has been updated from the old 0.2.3 to the newer 0.3 release which makes creating the games much easier and more powerful than before.

Connect 4 is a game where two players take turns to drop coloured pieces down columns. The aim of the game is for a player to create a 4 in a row pattern vertically, horizontally or diagonally.

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

 
 

This tutorial illustrates each part of creating this tutorial using source code listings and UML diagrams which show how each new class extends from the JOGRE API. The key of the UML diagrams are as follows:

 
 
[ top ]
 
 
Getting started
 

Before you start this tutorial ensure you have the "Jogre Source, Beta 0.3" distribution extracted somewhere (e.g. c:\) which is available on the download page and then built using the ANT utility. To build JOGRE, open a command prompt, change directory to /jogre and type "ant" to do a complete build.

The game key for this tutorial is c4tutorial - this is named to avoid confusion from the existing connect4 game.

 

In the previous tutorials all the basic framework source / folders / ANT scripts had to be coded by hand. In beta 0.3 there exists a code generator which does this for you, all you have to supply is the game key mentioned previously.

The game generator is located in the /jogre/util directory and be run either from the command line (java -jar gamegen.jar) or using a GUI front end. To run the GUI click on gamegengui.bat or gamegengui.sh (depending on OS) in the /jogre/util directory. It should look something like the following: -

 
 

Ensure the game_id is set to c4tutorial and the directory is specified correctly e.g. c:\jogre\games. You can adjust the optional parameters also. Click the Generate button and the all the basic source files, ANT scripts, Eclipse project files etc should all be created for you in the /jogre/games/c4tutorial folder.

The game generator creates various folders and 17 files which every game uses. For this tutorial, these files are:-

  1. build.xml - Build file which ANT uses to compile source files and create applet jar file.
  2. .classpath - Classpath file which Eclipse uses.
  3. client.bat - Windows batch file for running client.
  4. client.sh - Script file for running client.
  5. .cvsignore - CVS ignore file.
  6. .cvsignore - CVS ignore file.
  7. game_labels.properties - Properties file for storing language strings
  8. game.properties - Game specific properties e.g. number of players etc
  9. .project - Project file which Eclipse uses.
  10. C4tutorialApplet.java - Java class for creating an applet.
  11. C4tutorialClientFrame.java - Java class containing main method for running application.
  12. C4tutorialController.java - MVC controller class i.e. handles mouse clicks etc.
  13. C4tutorialModel.java - MVC model class i.e. holds data describing game state.
  14. C4tutorialServerController.java - Server controller class.
  15. C4tutorialTableFrame.java - Java class containing code for table frame.
  16. C4tutorialComponent.java - MVC view class i.e. renders the connect 4 graphics.
  17. C4tutorialModelTest.java - JUnit test class for testing model.

The c4tutorial/images folder should contain the following 6 images (right click and select "Save Pictures As...").

 
c4tutorial_icon.gif jogre_title.gif c4tutorial_title.gif no_piece.gif red_piece.gif yellow_piece.gif
 

Load up Eclipse IDE and import the Jogre API, Server and newly generally C4tutorial game. To import the API for example click "File - Import - Existing Projects Into Workspace". Select Next and ensure the "Select root directory:" is set to /jogre/api. When you click finish the API should be a new project in Eclipse. Do the same for the Jogre server (root dir = /jogre/server) and c4tutorial game (root dir = /jogre/games/c4tutorial). Eclipse should look like the following screenshot.

 
 

Our first step is to edit the important game.properties file which every game has in its base folder. Ensure the c4tutorial game.properties looks like the following: -

 
game.properties
# Game ID
game.id=c4tutorial

# Must be a "colour.1" to "colour.x" where x = max.num.players
player.colour.0=255,0,0
player.colour.1=255,255,0

# Other colours
background.colour=210,210,230
title.colour=0,0,0

# Declare images
image.1=images/no_piece.gif
image.2=images/red_piece.gif
image.3=images/yellow_piece.gif
 

The game.property file contains game properties including the important game.id property which sets the game key. It also includes the colours of players (connect 4 is red and yellow) and background colours using 0-255 RGB values. Finally, since applet support is in Beta 0.3 the images must be defined in the property files. This is necessary as the images are read from an applet jar file.

In JOGRE there are 3 special images that each game must have. They are: -

  1. jogre_title.gif - Global title image that each game displayed at the top left hand of screen.
  2. <game_id>_title.gif (e.g. c4tutorial_title.gif) - Game specific title graphic displayed at top right hand side of screen.
  3. <game_id>_icon.gif (e.g. c4tutorial_icon.gif) - Small icon graphic for each game.

In this connect 4 tutorial we have 3 additional images and these must be defined in the game.properties file.

Now update the game_labels.properties (multiple for each language) to the following. It is also located in the /jogre/games/c4tutorial folder.

 
game_labels.properties
# Client
game.label=C4tutorial
jogre.client.title=Jogre C4tutorial Client
jogre.client.welcome.message=Welcome to Jogre C4tutorial!
jogre.table.title=Jogre C4tutorial Table

# Must be a "player.label.0" to "player.label.x"
player.label.0=Red
player.label.1=Yellow
 

The game_labels.property files contains six labels which the API makes use of. Most of the properties are self-explanatory and simply set the name of the 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 Connect 4 these are "yellow" and "red". Additional labels can be inserted here if labels are required in the game.

 
[ top ]
 
 
Creating a Server
 

In JOGRE Beta 0.3 a single server exists which can handle multiple games. To ensure that the server will support a game the game id (from game.properties) must be inserted in the /jogre/server/server.xml file. It also defines the minimum and maximum number of players for a game. For connect 4 these are both 2.

 
server.xml

    <!-- List of supported games. -->
    <supported_games>
        <game id="battleships" host="true" minPlayers="2" maxPlayers="2" />
        <game id="checkers" host="true" minPlayers="2" maxPlayers="2" />
        <game id="c4tutorial" host="true" minPlayers="2" maxPlayers="2" />
        <game id="chess" host="true" minPlayers="2" maxPlayers="2" />
        <game id="dots" host="true" minPlayers="2" maxPlayers="2" />

        ...

    </supported_games>
 
[ top ]
 
 
Running a Server
 

To run the JOGRE server execute the /jogre/server/server.bat (windows) or /jogre/server/server.sh (linux) file.

 
 

If the c4tutorial line was inserted correctly into the /jogre/server/server.xml you should see the line in the server output. The server is now ready to accept connections from clients.

 
[ top ]
 
 
Running a client
 

When changes are made to the jogre properties files it is good practise to run an ANT script.

 
 

The game client can be run from the command prompt or through eclipse. First of all restart the Jogre Server. The c4tutorial client can now execute and connect to the server. This is achieved by going to the jogre/games/c4tutorial folder and executing the client.bat / client.sh file. These files contain the following: -

java -classpath .;classes;..\..\api\jogre.jar org.jogre.c4tutorial.client.C4tutorialClientFrame

If you prefer to run the client straight from Eclipse then create click "Run - Run configurations" and create the following config: -

 
 

If the client loads successfully then the client will appear with a logon screen. Use a username from the the users.xml file (located in the jogre/server/data/xml folder). The default usernames are bob, dave, sharon and john. 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 Connect 4 MVC (Model/View/Controller)
 

The next step is to create the actual game of "connect 4" using the MVC design pattern (see the tic-tac-toe tutorial for more information on MVC) by editting 3 of our generated classes. hese classes are as follows:

 
Location Class/Interface Name API Extends Synopsis
org/jogre/c4tutorial/client C4tutorialModel JogreModel Model which holds the data for a game of Connect4.
C4tutorial4Controller JogreController Controller which updates the model and listens to user input on the visual board.
C4tutorialComponent AbstractBoardComponent
(-> JogreComponent)
Connect4 visual board component (view of the model).
 
The following clickable image shows these three MVC classes as a clickable UML diagram.
 
 

These classes all extend classes from the JOGRE API. The 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.

 
C4TutorialModel.java

...
public class C4tutorialModel extends JogreModel {

    // Declare constants to define model
    public static final int COLS = 7;
    public static final int ROWS = 6;

    public static final int BLANK = -1;
    public static final int RED   = 0;
    public static final int YELLOW  = 1;

    // model (2 dimensional int array)
    private int [][] data = new int [COLS][ROWS];

    /**
     * Constructor which creates the model.
     */
    public C4tutorialModel () {
        super (GAME_TYPE_TURN_BASED);
        reset ();
    }

    /**
     * Reset the pieces.
     */
    public void reset () {
        for (int x = 0; x < COLS; x++)
            for (int y = 0; y < ROWS; y++)
                data [x][y] = BLANK;

        refreshObservers();     // inform any graphical observers
    }

    /**
     * Return data
     *
     * @param x
     * @param y
     * @return
     */
    public int getData (int x, int y) {
        return data [x][y];
    }

    /**
     * Set data at specified point.
     *
     * @param x
     * @param value
     */
    public void setData (int x, int value) {
        // Find the next available empty space
        for (int y = ROWS - 1; y >= 0; y--) {
            if (getData(x, y) == BLANK) {
                data [x][y] = value;
                refreshObservers();     // update any views on this model
                return;
            }
        }
    }

    public XMLElement flatten() {
        return null;
    }

    public void setState(XMLElement message) {}
}
 

NOTE: If you are getting "type cannot be resolved" errors in this tutorial then imports need to be added. To organise imports in Eclipse press <cntl> + <shift> + o.

The model class C4tutorialModel is similar to the TicTacToe model in the previous tutorial. It's data is stored in a 2 dimensional integer array of 7 x 6 (7 columns by 6 rows). The data on each of these points can be either -1, 0 or 1 (empty, red or yellow). The class also contains a reset () method which wipes all the data. There is also getter and setter methods. The set method takes a value (red or yellow) and a column. The method starts at the bottom of the board and loops until it finds an empty place and gives it a value. The refreshObservers () method (from JogreModel) is then called which will refresh any component registered with this data i.e. C4tutorialComponent.

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

 
C4tutorialComponent.java

...
public class C4tutorialComponent extends AbstractBoardComponent {

    // Declare constants which define what the board looks like
    private static final int NUM_OF_COLS = 7;
    private static final int NUM_OF_ROWS = 6;
    private static final int CELL_SIZE = 50;
    private static final int CELL_SPACING = 0;
    private static final int BORDER_WIDTH = 0;

    // Link to the model
    protected C4tutorialModel model;

    protected int curMousePoint = -1;

    /**
     * Constructor which creates the board.
     *
     * @param model
     */
    public C4tutorialComponent (C4tutorialModel model) {
        // Call constructor in AbstractBoardComponent
        super (NUM_OF_ROWS, NUM_OF_COLS, CELL_SIZE, CELL_SPACING,
                BORDER_WIDTH, 0, false, false, false);

        this.model = model;      // link to model

        // set colours
        Color bgColor = new Color (34, 34, 102);
        setColours (bgColor, bgColor, 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 Connect 4 piece on the board (loop through the model)
        for (int x = 0; x < C4tutorialModel.COLS; x++) {
            for (int y = 0; y < C4tutorialModel.ROWS; y++) {
                // get piece from model
                int piece = model.getData(x, y);

                // Now get screen co-ordinates (AbstractBoardComponent)
                Point screen = getScreenCoords (x, y);

                // Now get screen co-ordinates (AbstractBoardComponent)
                Image image = GameImages.getImage(piece + 2);
                g.drawImage (image, screen.x, screen.y, null);
            }
        }

        // Draw the box around the board
        if (curMousePoint != -1) {
            g.setColor (new Color (200, 200, 200));
            Point screen = getScreenCoords (curMousePoint, 0);
            g.drawRect (screen.x, 0, CELL_SIZE - 1, (CELL_SIZE * NUM_OF_ROWS) - 1);
        }
    }

    /**
     * Return the current mouse point.
     *
     * @return
     */
    public int getCurMousePoint() {
        return curMousePoint;
    }

    /**
     * Set the current mouse point to another point.
     *
     * @param newPoint
     */
    public void setCurMousePoint (int newPoint) {
        curMousePoint = newPoint;
    }
}
 

The C4tutorialComponent class extends an AbstractBoardComponent which is a helper class used for creating game boards (e.g. chess, checkers, etc). This class has a link to the connect 4 model which it renders. Its constructor calls the super constructor in AbstractBoardComponent, sets the number of rows, columns, cellspacing etc.

The most important method for any class which extends JogreComponent is the paintComponent method as it is responsible for actually rendering the graphics onto the screen. This method can be called from a model using the refreshObservers() method call. The paintComponent method starts with a call to super.paintComponent (g) which renders board from AbstractBoardComponent. It then loops through the rows / columns of the C4tutorialModel and gets the screen co-ordinate of each position. It then draws a graphic onto the screen at this point depending on its value in the model (no piece, red piece or yellow). Finally, if it is a players go it draws a box around the position of the mouse to show which column the piece will drop down. Getter and setter methods for setting this mouse point are included and these are called from the controller (next).

 
C4tutorialController.java

...
public class C4tutorialController extends JogreController {

    // links to game data and the board component
    protected C4tutorialModel model;
    protected C4tutorialComponent boardComponent;

    /**
     * Constructor.
     *
     * @param model
     * @param boardComponent
     */
    public C4tutorialController (C4tutorialModel model, C4tutorialComponent boardComponent) {
        super(model, boardComponent);

        this.model = model;            // set fields
        this.boardComponent = boardComponent;
    }

    /**
     * Start method.
     *
     * @see org.jogre.client.JogreController#start()
     */
    public void start() {
        model.reset ();
    }

    // Overwrite the mousePressed method
    public void mousePressed (MouseEvent e) {
        if (isGamePlaying() && isThisPlayersTurn ()) {      // ensure game has started
            // 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 < C4tutorialModel.COLS &&
                    board.y >= 0 && board.y < C4tutorialModel.ROWS) {

                // check model at this point is BLANK and it is this persons go
                if (model.getData(board.x, 0) == C4tutorialModel.BLANK) {
                    int value = getCurrentPlayerSeatNum();
                    move (board.x, value);    // move

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

    // Show the column that the mouse is on.
    public void mouseMoved (MouseEvent e) {
        if (isGamePlaying () && isThisPlayersTurn ()) {
            Point point = boardComponent.getBoardCoords (e.getX(), e.getY());

            // If user has moved to a new column
            if (point.x != boardComponent.getCurMousePoint()) {
                // Check to see if there is room left
                if (model.getData(point.x, 0) != C4tutorialModel.BLANK)
                    boardComponent.setCurMousePoint (-1);
                else
                    boardComponent.setCurMousePoint (point.x);

                // update the board
                boardComponent.repaint();
            }
        }
    }

    /**
     * Update the model
     *
     * @param x
     * @param value
     */
    public void move (int x, int value) {
        boardComponent.setCurMousePoint (-1);

        // update model and change player turn
        model.setData (x, value);

        // next players turn
        nextPlayer ();
    }

    // Check to see if the game is over or not.
    private void checkGameOver () {
        // Do later
    }
}
 

The C4tutorialController controller class updates the model and listens to user input on the C4tutorialComponent. This class extends the JogreController class which is an adapter class which implements all the methods from MouseMotionListener, MouseListener and KeyListener.

The controller class has links to both the model and the view (see the UML diagram previous) which are both set in the constructor. The mousePressed and mouseMoved methods are overwritten to response to user input. Both methods start with the line:

if (isGamePlaying() && isThisPlayersTurn ()) { ...

This ensures that the method will not execute any further unless the game has started and is this particular players turn to go. Both methods then convert the large pixel co-ordinate into smaller board co-ordinate (0 .. 6) using the helper method getBoardCoords() from AbstractBoardComponent. If the C4tutorialComponent didn't extend from AbstractBoardComponent then this method would have be created manually (e.g. check out Propinqity game which creates a hexagonal board).

In the mousePressed method the model is check to ensure there is space for a piece to be dropped. If this is the case then the move is made. The mouseMoved method simply sets the curMousePoint (this was mentioned earlier in C4tutorialComponent) which draws a box to show where the user has the mouse on the component.

Finally, the C4tutorialTableFrame must be filled in to create instances of the model/view/controller classes.

 
C4tutorialTableFrame.java

...
public class C4tutorialTableFrame extends JogreTableFrame {

    private C4tutorialModel model;
    private C4tutorialComponent boardComponent;
    private C4tutorialController controller;

    /**
     * Constructor which passes a client connection thread and a table.
     *
     * @param conn
     */
    public C4tutorialTableFrame (TableConnectionThread conn) {
        super (conn);

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

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

        // Add view to a panel and set on table frame
        double pref = TableLayout.PREFERRED;
        double [][] sizes = {{10, pref, 10, pref, 10},    // horizontal sizes
                             {10, pref, 10}};             // vertical sizes
        JogrePanel panel = new JogrePanel (sizes);

        panel.add (boardComponent, "1,1");
        panel.add (new PlayerComponent (conn, 1, true), "3,1,l,t");
        panel.add (new PlayerComponent (conn, 0, true), "3,1,l,b");

        setGamePanel (panel);   // add panel to table frame

        // Set up MVC classes in super class
        setupMVC (model, boardComponent, controller);
        pack();
    }
}
 

The way this is done for Connect4 is identical as the TicTacToe tutorial. The steps are summarised as follows:

  1. Create model.
  2. Create view and pass in the model.
  3. Ensure that the view observes the model.
  4. Create a controller and pass in the model and view.
  5. Register the controller on the view.
  6. Add view to a panel and set this as the main game panel using the setGamePanel method.
  7. Finally, call the important setupMVC method.

To test these changes, recompile the classes again and run the JogreServer 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 successful then you should see the clients update themselves but not each other.

 
 

For the clients to keep in sync requires communication to the server. This is discussed in the next chapter.

 
[ top ]
 
 
Server communication
 

All communication is transmitted as XML which is much more powerful than using flat Strings in alpha 0.1.

In JOGRE Beta 0.3, communication is either a client message or a table message. A table message always contains a table attribute and is delegated to the specified table frame automatically. Click here for the JOGRE communication protocol.

The JogreClientFrame now has a link to the ClientConnectionThread and the JogreTableFrame has a link to the new TableConnectionThread class. These classes automatically add table numbers automatically to communication objects which makes the programmers life easier.

Also, using layers, the messy manual delegation between the ClientConnectionThread, the JogreClientFrame and the JogreTableFrame is now unnecessary as the (e.g. the method ticTacToeMove (CommTicTacToeMove move) from the TicTacToeClientFrame class in tutorial 1).

These are the main differences between 0.1 and 0.2 communication. The following UML diagram shows a class diagram of the JOGRE communication model.

 
 

Communication is sent:

  • client -> server - communication is sent using the send () methods from ClientConnectionThread and TableConnecionThread (depending if it is a game message or a table message)
  • .
  • server -> client - communication is received from the server into the receiveMessages () method on the JogreClientFrame and JogreTableFrame respectively.

New convience methods for communication also exist in the JogreController class for sending / recieving properties (and XML objects). A very easy way to send a property to all the other players in a Connect 4 game is to use the sendProperty. To do this update the move method in C4tutorialController and insert a new receiveProperty method as follows.

 
C4tutorialController.java
    // Update the model
    public void move (int x, int value) {
        boardComponent.setCurMousePoint (-1);

        // update model and change player turn
        model.setData (x, value);

        // next player turn.
        nextPlayer ();

        // send move to other user
        sendProperty ("move", x, value);
    }

    // Receive
    public void receiveProperty (String key, int x, int value) {
        model.setData (x, value);
    }
 

The sendProperty method sends the String "move", the x board position and the piece value (yellow / red) to the server. The server then sends this to all the other clients through the recieveProperty method. This method simply updates the connect 4 model so the 2 clients are in synch.

To test, restart the server, run 2 clients and create a new game. This time you can see that each client look the same. However, you notice in the following screenshot that both "Dave" and "Bob" have 4-in-a-row, but neither of them have won the game. This is discussed later in the tutorial.

 
 
[ top ]
 
 
Model State
 

Game state is now kept on the server side using server controllers. This means that other users can now join a game in progress. The server controllers keep a copy of the model for each table on each game. When a user joins a table where the game is on progress they must be sent the state of the game as an XML message. Two methods must now be implemented for the C4tutorialModel class. These are XMLElement flatten () (turns state of model into an XML message) and void setState (XMLElement message) (sets state of model from an XML message).

The following is the flatten method in connect 4 model.

 
C4tutorialModel.java
    // Flatten the state of the model into an XML object.
    public XMLElement flatten () {

        // Retrieve empty state message from super class
        XMLElement state = new XMLElement(Comm.MODEL);

        // Flatten 2d data to a 1d array
        int [] data1D = JogreUtils.convertTo1DArray (data);

        // Flatten 1d array into a space delimited string and set attribute
        state.setAttribute ("pieces", JogreUtils.valueOf (data1D));

        return state;
    }
 

From earlier we noticed how the state of a game of connect 4 was stored as a 2-dimensional integer array. How do we store this as an attribute of an XML message? The first thing we do is create an empty <model> message.. We then convert the data field (2D integer array) into a 1D integer array using the JogreUtils class. We then convert this 1D int array into a spaced delimited string and set a "pieces" attribute in the <model> message. If for example Bob and Dave are playing a game connect 4 and another user shazza joins the table, the server will sent shazza the following message (once the connect 4 server controller exists):

<join_table table="1" username="shazza">
    <player_list owner="dave" curPlayer="dave">
        <player name="shazza" state="viewing" seat="-1"/>
        <player name="bob" state="started" seat="1"/>
        <player name="dave" state="started" seat="0"/>
    </player_list>
    <model pieces="-1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 0 -1 -1 -1 -1
                   -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
                   -1 -1 -1 -1 -1 -1 -1 -1 1 1" />
</join_table>
                    

The following is the setState which then takes the <model> message and sets the state of the model accordingly.

 
C4tutorialModel.java
    // Set the state of the model
    public void setState (XMLElement message) {

        // Retrieve "pieces" attribute into String
        String pieces = message.getStringAttribute ("pieces");

        // Convert space delimited string into an integer array
        int [] data1D = JogreUtils.convertToIntArray (pieces);

        // Convert from 1D to 2D and set data field
        this.data = JogreUtils.convertTo2DArray (data1D, COLS, ROWS);

        // If everything is read sucessfully then refresh observers
        refreshObservers();
    }
 

The setState method does the reverse of the flatten method. It retrieves the value of the "pieces" attribute into a String, converts this String to an 1d integer array and finally from a 1d integer array into the data field of C4tutorialModel (which is a 2d integer array). The last step is to call the refreshObservers method which will refresh the C4tutorialComponent.

However the server must have a copy of the model otherwise none of this would work. This is discussed in the next section.

 
[ top ]
 
 
Server Controllers
 

On the client side we use controllers (i.e. C4tutorialController) to controll user input and to send messages to the server to keep clients in sync. On the server side all standard messages are parsed using the default server table and game controllers. However a custom server controller can be installed which will receive all messages send from a particular game.

A controller must be located in the following location:

/jogre/games/<game key>/src/org/jogre/<game key>/server

i.e.

/jogre/games/c4tutorial/src/org/jogre/c4tutorial/server

The class name must also end in ServerController. When the Jogre Server loads up it will scan the <game key>/classes/org/jogre/<game key>/server folder of each game that is included in the server.xml file. If it detects a class ending in ServerController it will try to create an instance of it using reflection. Thus, the Jogre Server loads server controllers using a plug-in style architecture.

The next step is to add a receiveProperty method to the C4tutorialServerController class. This method will intercept messages from the client and update the model on the server.

 
C4tutorialServerController.java
    // Receive property from client and update model
    public void receiveProperty (JogreModel model, String key, int x, int y) {
        ((C4tutorialModel)model).setData (x, y);
    }
 

Each server controller must implement a one parameter constructor and 2 abstract methods. The constructor simply takes note of the gameKey. The startGame method is called when a new game starts at a particular table. In this case a new connect 4 model is initilised on the server. The gameOver method is called when a client requests that a game is over. This is verified on the server side to prevent client side hacking. This method is filled in the next section.

The abstract class ServerController also contains a number of adapter methods which can be overwritten if required. These adapater methods include userResigns, usersAgreeDraw and a number of receiveProperty and receiveObject methods. On the client side, other clients were kept in synch via the sendProperty method call and overriding the receiveProperty adapter method to receive properties. The receiveProperty is similiar on the server side with the additional JogreModel parameter as the server has to handle a number of different models for each table. When a client makes a move (and sends a property to other clients) the server controller receives it in the receiveProperty method. The model is then casted to a C4tutorialModel and kept in synch with the various clients models using the setData (x, y) method call.

To ensure that everything is working properly run a Jogre Server, 2 clients (e.g. dave, bob) and start a game. After a few moves run another user (e.g sharon) and join the game as a viewer. If everything is working the user sharon should see the state of the game. This is because the server will send sharon the state of the game in a <join_table> message. The state is created from the flatten() method in the previous section and the model is updated on the server controller using the setState() method also from the previous section.

The final piece of the puzzle is determining when a game is over which is discussed in the next section.

 
[ top ]
 
 
Game Over
 

In Connect 4 a game can end with a player winning (getting 4 of his pieces in row) or end in a draw (no spaces left on the board with neither player winning). It is quite similar to tic-tac-toe.

 

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

 
C4tutorialModel.java
    // return true if the game is won by this player
    public boolean isGameWon (int player) {
        for (int x = 0; x < COLS; x++) {
            for (int y = 0; y < ROWS; y++) {
                // check horizontally, vertically a diagonally 
                int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
                for (int i = 0; i < 4; i++) {

                if (x + i < COLS) {
                    if (data[x+i][y] == player)
                        c1 ++;
                }

                if (y + i < ROWS) {
                    if (data[x][y+i] == player)
                        c2 ++;
                }

                if (x + i < COLS && y + i < ROWS) {
                    if (data[x+i][y+i] == player)
                        c3 ++;
                }

                if (x - i >=0 && y + i < ROWS) {
                    if (data[x-i][y+i] == player)
                        c4 ++;
                }
            }

            // Has any of these been 4 in a row
            if (c1 == 4 || c2 == 4 || c3 == 4 || c4 == 4)
                return true;
            }
        }

        return false;
    }

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

The isGameWon method checks the int array in the C4tutorial Model horizontally, vertically and diagonally. It returns true if a 4 in a 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).

 
C4tutorialController.java
    // 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 (status);
            conn.send (gameOver);
        }
    }
 

The checkGameOver method in the C4tutorialController is updated to check the C4tutorialModel 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.

Previous to Beta 0.3 sending a <game_over> message to the server resulting in a game ending. This would create problems in a real-life environment as hackers would start sending fake <game_over> messages and increase their score. Since Beta 0.3 when the server receives this message it runs the abstract method gameOver in the correct server controller class to verify that the game is actually over (and thus can update scores). The implementation of this method is as follows:

 
C4TutorialServerController.java
    // Game over on server controller
    public void gameOver (ServerConnectionThread conn, int tableNum, int resultType) {
        C4tutorialModel model = ((C4tutorialModel)getModel(tableNum));
        int player = getSeatNum(conn.getUsername(), tableNum);

        // Status is either -1, DRAW or WIN
        int status = -1;
        if (model.isGameWon(player))
            status = IGameOver.WIN;
        else if (model.isNoCellsLeft ())
            status = IGameOver.DRAW;

        // Create game over object if a win or draw
        if (status != -1) {
            // Update the server data
            gameOver (conn, tableNum, conn.getUsername(), status);
        }
    }
 

When a client informs the server that a game is over the abstract gameOver method is called on the server controller. The model is retrieved for the specified table (getModel(int table)) and casted to a C4tutorialModel. The seat number of the current player is also noted. A status variable is created and a gameover check is performed. If the game is over a gameOver method is called which takes four parameters (client connection, client username, status and table number). This method updates the scores on the server persistent data. Currently this is stored as an XML file in the /jogre/server/data/xml/users.xml file. The server finally sends a message to each client with the updated scores.

Note: JOGRE can now store persistent data in a database. Switching to a database will require changing the server_data attribute in the <configuration> element in the server.xml file from "xml" to "database".

 
 
[ top ]
 
 
Applet Support
 

Applet support is available in the Beta 0.3 release. The game generator has created a C4tutorialApplet class which is similar to the C4tutorialClientFrame class. To run the applet we must create a c4tutorial.jar file. This involves running the package target in the ANT script.

 
 

This should create a jogre/games/c4tutorial/c4tutorial.jar file which is about 200KB in size.

To test the applet we can modify jogre/games/applet_test.html applet testing file. We need to add a link to the c4tutorial.jar to the <select name="game"> html combobox (line 45). Add the following option to the combobox:

<option value="c4tutorial/applet_c4tutorial.jar org.jogre.c4tutorial.client.C4tutorialApplet.class">C4Tutorial</option>

To test the applet run a server and open the applet_test.html file using your favourite browser (e.g. Firefox 1.5 / IE 6.0). Select the C4Tutorial option from the "Game" combobox, enter a username and click the "Connect" button. You should see something like the following:

 
 

The applet can also be run from Eclipse if you prefer which is tricker than running the client as an application. This is achieved by selecting Run - Run Configurations..., and selecting new Java Applet. In the "Main" tab ensure the Project is set to "JOGRE C4tutorial" and the Applet is set to "org.jogre.c4tutorial.client.C4tutorialApplet".

In the "Parameters" tab set the width and height to 600 x 400. Add 4 parameters e.g. (language=en, serverhost=localhost, serverport=1790 & username=bob). In the "Classpath" remove the "JOGRE C4tutorial" project from the user entrie and click Add Jar's and add the "applet_c4tutorial.jar" file to the classpath so it looks like the following: -

 
 

This brings the Connect4 tutorial to an end. Its aim is to illustrate the implementation of game using the JOGRE API beta 0.3 using screenshots, UML diagrams and full source code listings. More information is available in the other tutorials although they are out of date. 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