6 Replies - 1654 Views - Last Post: 17 February 2013 - 02:51 PM Rate Topic: -----

#1 apophis3   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 19-January 13

TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 04:32 AM

Hi guys,
I'm trying to rewrite a code for TicTacToe game to get separate class for Cell, instead of the inner class. Could you please help me with that? I'm self-studying from Liang's Introduction to Java programming and have no one to ask. I had no problems with previous 640 pages of the book, have done all the exercises but now I'm lost.
I have created object of TicTacToe class in Cell class in the mouseClicked method and in the TicTacToe class I've written get method for whoseTurn and set method for text displayed on JLabel, but nothing works. It seems I don't get some basic knowledge how the classes work.
Here's original code and my pathetic attempts.

Thank you very much for your help.

Original code:

public class TicTacToe extends JApplet {
	   //player X begins. ' ' indicates game over
	   private char whoseTurn = 'X';

	   private Cell[][] cells = new Cell[3][3];
	   private JLabel jlblStatus = new JLabel("X's turn to play");

	   public TicTacToe() {
	      JPanel jp = new JPanel(new GridLayout(3, 3, 0, 0));
	      for (int i = 0; i < 3; i++)
	    	  for (int j = 0; j < 3; j++)
	    		  jp.add(cells[i][j] = new Cell());

	      jp.setBorder(new LineBorder(Color.RED, 1));
	      jlblStatus.setBorder(new LineBorder(Color.YELLOW));

	      add(jp, BorderLayout.CENTER);
	      add(jlblStatus, BorderLayout.SOUTH);	     
	   }

	   //if the cells are all occupied
	   public boolean isFull() {
	      for (int i = 0; i < 3; i++)
	    	  for (int j = 0; j < 3; j++) 
	      	    if (cells[i][j].getToken() == ' ')
	      	    	return false;

	      return true;
	   }

	   //if player with token won
	   public boolean isWon(char token) {
	      //check rows
	      for (int i = 0; i < 3; i++) {
	         if (cells[i][0].getToken() == token &&
	        	cells[i][1].getToken() == token &&
	        	cells[i][2].getToken() == token)
	         return true;
	      }
	 
	      //check columns
	      for (int j = 0; j < 3; j++) {
	         if (cells[0][j].getToken() == token && 
	        	 cells[1][j].getToken() == token &&
	        	 cells[2][j].getToken() == token)
	           return true;
	      }

	      //check diagonals
	      if ((cells[0][0].getToken() == token &&
	    	   cells[1][1].getToken() == token &&
	    	   cells[2][2].getToken() == token) ||

	    	   (cells[0][2].getToken() == token &&
	    	   cells[1][1].getToken() == token &&
	    	   cells[2][0].getToken() == token))
	        return true;

	      return false;
	   }
	   
	   public class Cell extends JPanel {
	      private char token = ' ';

	      public Cell() {
	    	  setBorder(new LineBorder(Color.BLACK, 1));
	    	  addMouseListener(new MyMouseListener());
	      }

	      public void setToken(char c) {
	         token = c;
	         repaint();
	      }

	      public char getToken() {
	         return token;
	      }

	      private class MyMouseListener extends MouseAdapter {
	         public void mouseClicked(MouseEvent e) {
		    //if cell is empty and game is not over
		    if (token == ' ' && whoseTurn != ' ') {
		       setToken(whoseTurn);

		       if (isWon(whoseTurn)) {
		    	   jlblStatus.setText("Player " + whoseTurn + " won! The game is over");
		    	   whoseTurn = ' ';
		       }

		       else if (isFull()) {
	               jlblStatus.setText("Draw! The game si over");
	               whoseTurn =  ' ';
		       }

		       else {
		    	   whoseTurn = ((whoseTurn == 'X') ? 'O': 'X');
		    	   jlblStatus.setText(whoseTurn + "'s turn to play");
		       }
		      }
	        }
	      }

	      protected void paintComponent(Graphics g) {  
	         super.paintComponent(g);

	         if (token == 'X') {
	        	 g.drawLine(10, 10, getWidth() - 10, getHeight() - 10);
	        	 g.drawLine(10, getHeight() - 10, getWidth() - 10, 10);
	         }

	         else if (token == 'O') 
	        	 g.drawOval(10, 10, getWidth() - 20, getHeight() -20);	
	      }
	   }

	   public static void main(String[] args) {
	         JFrame frame = new JFrame();
	         frame.add(new TicTacToe());
		 
	         frame.setTitle("TicTacToe game");
	         frame.setSize(250, 250);
	         frame.setLocationRelativeTo(null);
	         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	         frame.setVisible(true);
	    }
	}





my attemp at TictacToe class:

public class TicTacToe extends JApplet {
	   //player X begins. ' ' indicates game over
	   private char whoseTurn = 'X';

	   private Cell[][] cells = new Cell[3][3];
	   private JLabel jlblStatus = new JLabel("X's turn to play");

	   public TicTacToe() {
	      JPanel jp = new JPanel(new GridLayout(3, 3, 0, 0));
	      for (int i = 0; i < 3; i++)
	    	  for (int j = 0; j < 3; j++)
	    		  jp.add(cells[i][j] = new Cell());

	      jp.setBorder(new LineBorder(Color.RED, 1));
	      jlblStatus.setBorder(new LineBorder(Color.YELLOW));

	      add(jp, BorderLayout.CENTER);
	      add(jlblStatus, BorderLayout.SOUTH);	     
	   }

	   //if the cells are all occupied
	   public boolean isFull() {
	      for (int i = 0; i < 3; i++)
	    	  for (int j = 0; j < 3; j++) 
	      	    if (cells[i][j].getToken() == ' ')
	      	    	return false;

	      return true;
	   }

	   public boolean isWon(char token) {
	      //check rows
	      for (int i = 0; i < 3; i++) {
	         if (cells[i][0].getToken() == token &&
	        	cells[i][1].getToken() == token &&
	        	cells[i][2].getToken() == token)
	         return true;
	      }
	 

	      for (int j = 0; j < 3; j++) {
	         if (cells[0][j].getToken() == token && 
	        	 cells[1][j].getToken() == token &&
	        	 cells[2][j].getToken() == token)
	           return true;
	      }

	      if ((cells[0][0].getToken() == token &&
	    	   cells[1][1].getToken() == token &&
	    	   cells[2][2].getToken() == token) ||

	    	   (cells[0][2].getToken() == token &&
	    	   cells[1][1].getToken() == token &&
	    	   cells[2][0].getToken() == token))
	        return true;

	      return false;
	   }
	   
	   public char getWhoseTurn() {
		   return whoseTurn;
	   }
	   
	   public void setJlblStatus(String status) {
		   this.jlblStatus.setText(status);
	   }

	   public static void main(String[] args) {
	         JFrame frame = new JFrame();
	         frame.add(new TicTacToe());
		 
	         frame.setTitle("TicTacToe game");
	         frame.setSize(250, 250);
	         frame.setLocationRelativeTo(null);
	         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	         frame.setVisible(true);
	    }
	}




and cell class

	   public class Cell extends JPanel {
		  
	      private char token = ' ';

	      public Cell() {
	    	  setBorder(new LineBorder(Color.BLACK, 1));
	    	  addMouseListener(new MyMouseListener());
	      }

	      public void setToken(char c) {
	         token = c;
	         repaint();
	      }

	      public char getToken() {
	         return token;
	      }

	      private class MyMouseListener extends MouseAdapter {
	         public void mouseClicked(MouseEvent e) {
	        	 TicTacToe ttt = new TicTacToe();
	        	 char whose = ttt.getWhoseTurn();
		    //if cell is empty and game is not over
		    if (token == ' ' && whose != ' ') {
		       setToken(whose);

		       if (ttt.isWon(whose)) {
		    	   ttt.setJlblStatus("Player " + whose + " won! The game is over");
		    	   whose = ' ';
		       }

		       else if (ttt.isFull()) {
		    	   ttt.setJlblStatus("Draw! The game si over");
	               whose =  ' ';
		       }

		       else {
		    	   whose = ((whose == 'X') ? 'O': 'X');
		    	   ttt.setJlblStatus(whose + "'s turn to play");
		       }
		      }
	        }
	      }

	      protected void paintComponent(Graphics g) {  
	         super.paintComponent(g);

	         if (token == 'X') {
	        	 g.drawLine(10, 10, getWidth() - 10, getHeight() - 10);
	        	 g.drawLine(10, getHeight() - 10, getWidth() - 10, 10);
	         }

	         else if (token == 'O') 
	        	 g.drawOval(10, 10, getWidth() - 20, getHeight() -20);	
	      }
	   }




Is This A Good Question/Topic? 0
  • +

Replies To: TicTacToe game applet with separate class for Cell

#2 GregBrannon   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2250
  • View blog
  • Posts: 5,340
  • Joined: 10-September 10

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 04:46 AM

What problems, if any, are you having with the two classes you've shown?
Was This Post Helpful? 0
  • +
  • -

#3 jjh08   User is offline

  • D.I.C Head

Reputation: 55
  • View blog
  • Posts: 198
  • Joined: 13-July 12

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 05:00 AM

I'm assuming the original code is the author's. Based on what I see, the author defined a relationship between class Tic Tac Toe and the inner class Cell. I am not 100% sure, but I think when you removed that relationship by separating the implementation of the two classes, this probably broke the code. I think that the field private char token within inner class Cell is the main element that established the relationship between the two classes.
For example, for your separated TicTacToe class, you have this method:
public boolean isWon(char token)

However, token is not defined or initiated within TicTacToe but within the inner class:
private char token = ' ';

The relationship can be established.
I could be wrong but that is what I'm seeing right now.
Was This Post Helpful? 0
  • +
  • -

#4 GregBrannon   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2250
  • View blog
  • Posts: 5,340
  • Joined: 10-September 10

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 07:25 AM

It's a nice looking little ap, and you've done well with the logic. You've also made a good start at separating the classes, but here are some things to think about and then do to correct the operation after separation:

Each class is a collection of properties and states and each class should be responsible for managing those parts of itself to the greatest degree possible - that's encapsulation. Currently, some parts of the game state are correctly maintained by the class TicTacToe: whose turn it is, whether the game is still in progress or has concluded with a win and who the winner is. The Cell class maintains its 'token' which determines if the cell is available or has been chosen and which player has chosen it.

TicTacToe properties and states that Cell should not change or need to care about are: win/lose/draw, the ability to change which player's turn it is, or TicTacToe labels. These should be determined by and under the control of the TicTacToe instance. To that end, I suggest the following:

Each instance of cell should be aware of the "parent" TicTacToe instance. One way to accomplish this is to pass the TicTacToe instance to the Cell constructor,

// in the TicTacToe constructor:
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
        jp.add(cells[i][j] = new Cell( this ));


Note the use of the keyword 'this' in the last line. You're currently creating a new instance of TicTacToe with every mouse click, and that is giving you the results of a separate game for each cell rather than results accumulated on a single instance of the game.

The corresponding Cell constructor and TicTacToe field will need to be created:

public class Cell extends JPanel
{
    private TicTacToe parent;
    private char token;
    private char whoseTurn;

    public Cell( TicTacToe parent )
    {
        this.parent = parent;
        this.token = ' ';
        setBorder(new LineBorder(Color.BLACK, 1));
        addMouseListener(new MyMouseListener());
    }

. . . etc.


The Cell class mouse listener should be reduced to

private class MyMouseListener extends MouseAdapter
{
    public void mouseClicked(MouseEvent e)
    {
        //if cell is empty
        if ( token == ' ' )
        {
            token = parent.getWhoseTurn();
            repaint();

            // update the game state
            parent.updateState();
        }
    }
}

Note the addition of the TicTacToe.updateState() method. The code in the updateState() method is adapted from the code you had in the Cell's listener:

public void updateState()
{
    if ( isWon( whoseTurn ) )
    {
        setJlblStatus( "Player " + whoseTurn +
                " won! The game is over" );
    }
    else if ( isFull() )
    {
        setJlblStatus("Draw! The game is over");
    }
    else
    {
        toggleWhoseTurn();
    }

}

This method also calls a new method, toggleWhoseTurn() which simply does as it says.

The one item left (I think) is to give the TicTacToe class a flag to signal Cell's listener that the game has ended. You were using a blank player, ' ', which is okay, but I prefer a more obvious approach. I added a gameOver boolean to TicTacToe and added the following to Cell's listener:

//if cell is empty and game is not over
if ( token == ' ' && !parent.isGameOver() )


The gameOver flag is initilized to false in the TicTacToe()) constructor and then set appropriately in the updateState() method.

Let me know if you have any questions. Post your completed code when done.

This post has been edited by GregBrannon: 17 February 2013 - 01:09 PM

Was This Post Helpful? 1
  • +
  • -

#5 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7181
  • View blog
  • Posts: 14,969
  • Joined: 16-October 07

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 09:09 AM

So, your author tightly coupled the cell. Bad author, no cookie.

In fact, they put logic that doesn't belong to the cell in the cell. This would be an example of your cell:
class Cell extends JPanel {
	private char token = ' ';

	public Cell() {
		setBorder(new LineBorder(Color.BLACK, 1));
		// addMouseListener(new MyMouseListener());
	}

	public void setToken(char c) {
		token = c;
		repaint();
	}

	public char getToken() {
		return token;
	}

	protected void paintComponent(Graphics g) {
		super.paintComponent(g);

		if (token == 'X') {
			g.drawLine(10, 10, getWidth() - 10, getHeight() - 10);
			g.drawLine(10, getHeight() - 10, getWidth() - 10, 10);
		} else if (token == 'O') {
			g.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
		}
	}
}



I'd really like an enum for the cell value, but we'll avoid that for now.

Now, in the original code, the cell was registering for a listener in the constructor. The cell shouldn't be doing this, it's the form that wants to listen.

// crap syntax
//jp.add(cells[i][j] = new Cell());
Cell c = new Cell();
c.addMouseListener(new MyMouseListener());
cells[i][j] = c;
jp.add(c);



Hmm... why not take all that mouseClicked logic and put it somewhere useful, in your TicTacToe class:
private void processClick(Cell c) {
	if (c.getToken() == ' ' && whoseTurn != ' ') {
		c.setToken(whoseTurn);
		if (isWon(whoseTurn)) {
			jlblStatus.setText("Player " + whoseTurn + " won! The game is over");
			whoseTurn = ' ';
		} else if (isFull()) {
			jlblStatus.setText("Draw! The game si over");
			whoseTurn = ' ';
		} else {
			whoseTurn = ((whoseTurn == 'X') ? 'O' : 'X');
			jlblStatus.setText(whoseTurn + "'s turn to play");
		}
	}
}



With that code in that class that it should be in, your private mouse listener becomes:
private class MyMouseListener extends MouseAdapter {
	public void mouseClicked(MouseEvent e) {
		processClick((Cell)e.getSource());
	}
}



Hope this helps.
Was This Post Helpful? 1
  • +
  • -

#6 apophis3   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 19-January 13

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 12:34 PM

Thank you very much for your help. My attempt was really pathetic :) But now I finally understand how it works. I rewrote the code for both the classes. I don't know if I should post it here, if it's not against some rules.
Anyway, thank you once more.
Was This Post Helpful? 0
  • +
  • -

#7 pbl   User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8378
  • View blog
  • Posts: 31,956
  • Joined: 06-March 08

Re: TicTacToe game applet with separate class for Cell

Posted 17 February 2013 - 02:51 PM

You test are really complicated for nothing

Add to your Cell class a int value;
When the Cell contains a X set the value to 1
When the Cell conatins a O set the value to 10

If the total of the value of a row/col/diagonal is 3 the X won
If the total of the value of a row/col/diagonal is 30 the X won
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1