Java with Greenfoot Lesson 3: Tic-Tac-Toe Game Part II

In the last lesson (Lesson 2), we created the Tic-Tac-Toe Board and GameBall classes. We also added GameBall objects to the Board object. In this lesson, we will add a Player class such that a Player object interacts with GameBall objects in a meaningful way.

The game should work this way:

This game takes two players, who will take turn clicking. When the game starts, all nine cells on the board are blank. When the first player clicks at a blank cell, a Gold ball will be placed in that cell; when the second player clicks at a blank cell, a Steel ball will be placed in that cell.

altaltalt

 

alt

Create a Player Class

To interact with the GameBall objects, we need a class called Player. Right click on the Actor button and select New subclass… from the pop-up menu. At New class name, enter “Player”, then import the first image, ant.png, by selecting animals->ant.png, and click OK.

Now the Player class has been created but we still need to import another image to represent another player. To do so, right click on the Player icon and select Set Image… from the drop-down list. Import another image, ant-with-food.png, by selecting animals->ant-with-food.png.

NOTE: You can select other images but make sure they are not larger than 30×30 pixels (for the cells are 60×60 pixels each), or else the game would not work as planned.

 

Add States to GameBall Class

Before adding interaction between the Player class and the GameBall class, we need to add states to the GameBall class. Three states are needed: UNCLICKED, GOLD, and STEEL. We will add a member variable-ballState-to hold the state information and three member functions-setGold, setSteel, and reset-to access and control the member variable.

A Java class can have member variables and member functions. Member variables are like states or settings of an object, whereas member functions are mechanism to access and control these settings. When an object is created, it’s assigned a unique segment of memory space to hold its member variables. Moreover, an object has access to a shared set of class functions.

Take GameBall class for example, if we create two GameBall objects (object of the GameBall class type) called ball_one and ball_two, then each of them will have a ballState variable and will have access to setGold(), setSteel(), and reset() functions.

To implement the three states, we use Java’s enumerated type. An enumerated type has a finite number of named values. For example:

 

enum BallState { UNCLICKED, GOLD, STEEL };

To declare variables of this type:

BallState state = BallState.UNCLICKED;

When the GameBall object is first created, we would like its state to be UNCLICKED. As the game goes on, players set the GameBall state via set functions.

This is the code for GameBall so far:


publich class GameBall extends Actor {

};
public class GameBall extends Actor {
    enum BallState { UNCLICKED, GOLD, STEEL };
    BallState state = BallState.UNCLICKED;
    GameBall() {
        state = BallState.UNCLICK;
    };
    public void setGold(){
        state = BallState.GOLD;
    };
    public void setSteel(){
        state = BallState.STEEL;
    };
    Public void reset(){
        state = BallState.UNCLICKED;
    };
}

 

Next, let’s add code to change a GameBall’s look according to its state. Since a GameBall extends from the Actor class, it inherits a set of functions from the Actor class. Inheritance is a very important concept in Object-Oriented Programming. Simply put, it’s a way to build new classes based on existing classes. When a class extends or inherits from another class, it’s said to be the subclass of that class, which is called the superclass. A subclass has all member variables and functions that its superclass has, and more.A subclass can has additional variables and functions to those of its superclass, and they often do.

 

alt

Going back to the GameBall’s code, it is the subclass of the Actor, based on this statement:


public class GameBall extends Actor {
    //...
}

 

The Actor class can have many subclasses; in fact, most new classes you created in Greenfoot are subclasses of the Actor class. GameBall and Player are both subclasses of the Actor.

alt

The Actor class has a public function called setImage, which changes the image file path. Since GameBall inherits from Actor, it can call setImage function like this:

setImage("gold-ball.png");

Or like this:

super.setImage("gold-ball.png");

The setImage function is an example of Actor’s function. You can look up on more Actor’s functions by right-clicking on the Actor icon and selecting Open Document from the pop-up menu.

alt

You will see the documentation for the act and getImage functions, among others.

alt

Since we have not imported gold-ball.png, let’s do so now. Right click on GameBall icon, select Set Image… from the pop-up menu to open the Select class image window. Select objects->bold-ball.png, and then hit OK.

alt

Now we are ready to complete the GameBall code. Add an enumerated type call “BallState”, a member variable called “state”, and three public functions: “setGold”, “setSteel”, and “reset”. A member function can be public, protected, or private. A public function is accessible to all other classes and a private function is accessible only inside its own class. For example, if class A has a public function f1 and a private function f2, then class B can only access f1, but not f2. But f1 is accessible inside f2.

public class A {
private void f1( ) {
…
};public void f2( ){…

f1( ); //OK

};

}
public class B{
public void f3(){
A objA = new A( );
objA.f1( ); //ERRORobjA.f2( ); //OK}

}

The completed code of GameBall class is now as shown:

/**
* Write a description of class GameBall here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class GameBall  extends Actor
{
enum BallState { UNCLICKED, GOLD, STEEL };
BallState state = BallState.UNCLICKED;
GameBall(){
setImage("cell.jpg");
}
/**
* Act - do whatever the GameBall wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
public void act()
{
// Add your action code here.
}
public void setGold(){
setImage("gold-ball.png");
state=BallState.GOLD;
}
public void setSteel(){
setImage("steel-ball.png");
state = BallState.STEEL;
}
public void reset(){
setImage("cell.jpg");
state = BallState.UNCLICKED;
}
}

 

You may have noticed the empty act function which I will explain in the next step.

 

 

Let Player interact with GameBall

Next, we will add codes to the Player class so Player objects can interact with the GameBall objects. The Player class has two states: PLAYER1 and PLAYER2. Similar to what was done to GameBall, add an enumerated type call “PlayerMode“, a member variable called “mode“, and two public functions: “setPlayer1” and “setPlayer2“.

public class Player  extends Actor
{
enum PlayerMode {PLAYER1, PLAYER2 };
PlayerMode mode;
public Player(){
mode = PlayerMode.PLAYER1;
setImage("ant-with-food.png");
}
/**
* Act - do whatever the Player wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
public void act()
{
// Add your action code here.
}

public void setPlayer1(){
mode = PlayerMode.PLAYER1;
setImage("ant-with-food.png");
}
public void setPlayer2(){
mode = PlayerMode.PLAYER2;
setImage("ant.png");
}
}

Next, let’s add code to Player’s act function so that a Player object handles mouse clicks. The act function is called automatically and repeatedly by the Greenfoot framework; if you want your Actor to perform a task repeatedly, you would put the code related to that task inside the act function.

The first we need to add to act is the code to “glue” the Player to the mouse:

if (Greenfoot.mouseMoved(null)){
MouseInfo mouse = Greenfoot.getMouseInfo();
setLocation(mouse.getX(), mouse.getY());
}

 

This piece of code, when added to act function, will move the Player object to wherever the mouse is. Next, we will add code to check whether the Player object has collided with a GameBall object when the mouse clicks. This is the code to do so:

if (Greenfoot.mouseClicked(null)){
GameBall ball = (GameBall)getOneIntersectingObject(GameBall.class);
if(ball!=null){
if (mode == PlayerMode.PLAYER1 ){
ball.setSteel();
setPlayer2();
}
else if (mode == PlayerMode.PLAYER2){
ball.setGold();
setPlayer1();
}
}
}

With this code, each time the mouse clicks, the Player object would first check whether it has collided with a GameBall, is yes (ball is not null), then it switches mode to another player by calling either setPlayer1 or setPlayer2. Moreover, when player1 is playing, each mouse click turns a ball from to STEEL; on the other hand, when player2 is playing, each mouse click turns the ball to GOLD.

alt

The complete code for the Player class thus is now:

import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
* Write a description of class Player here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class Player  extends Actor
{
enum PlayerMode {PLAYER1, PLAYER2 };
PlayerMode mode;
public Player(){
mode = PlayerMode.PLAYER1;
setImage("ant-with-food.png");
}
/**
* Act - do whatever the Player wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
public void act()
{
// Add your action code here.
if (Greenfoot.mouseMoved(null)){
MouseInfo mouse = Greenfoot.getMouseInfo();
setLocation(mouse.getX(), mouse.getY());
}
if (Greenfoot.mouseClicked(null)){
GameBall ball = (GameBall)getOneIntersectingObject(GameBall.class);
if(ball!=null){
if (mode == PlayerMode.PLAYER1 ){
ball.setSteel();
setPlayer2();
}
else if (mode == PlayerMode.PLAYER2){
ball.setGold();
setPlayer1();
}
}
}
}

public void setPlayer1(){
mode = PlayerMode.PLAYER1;
setImage("ant-with-food.png");
}
public void setPlayer2(){
mode = PlayerMode.PLAYER2;
setImage("ant.png");
}
}

Finally, add the code to the Board class to add one Player to the board.

public class Board  extends World
{
GameBall cell_1, cell_2, cell_3, cell_4, cell_5, cell_6, cell_7, cell_8, cell_9;
Player player;
/**
* Constructor for objects of class Board.
*
*/
public Board()
{
// Create a new world with 20x20 cells with a cell size of 10x10 pixels.
...

player = new Player();
addObject(player, 0,0);

}
...
}

alt

Test the Game

Save all three classes-Board, Player, and GameBall-and compile the game by clicking the “Compile All” button. Then hit Run to try.

alt

You should be able to click each cell and turn them to gold or steel balls.

alt

If you have trouble running the example, you would need to debug your program. To do so, select Controls>Act, and you should see your error in red.

Image-0026

One common error in this lesson is forgetting to import the image files. For example, if the “ant.png” file is not imported, you would see this error:

Image-0025

To download the source code for this lesson, click HERE.

This concludes Lesson 3. In Lesson 4, we will complete the Tic-Tac-Toe game by adding code to determine how the game is won and code to jazz up the game a bit. I will also show you how to export a Greenfoot game and how to post it on either Greenfoot site or your own site.

 

2 thoughts on “Java with Greenfoot Lesson 3: Tic-Tac-Toe Game Part II”

Leave a Comment