This used to be homework but now it's personal as the term is over and I'm still working on learning Swing components. This program is my attempt to lift a procedural console TicTacToe implementation into an applet in the simplest (looking) way possible, eschewing the traditional button grid for a single JTextField and submit button.
It works fine through the first time, but after I reset the game with some logic in the init() function (this actually works, I was surprised), getText() returns the empty string down at line 155 (search on 'ARGH'). I'm basically taking shots in the dark at this point, so, if someone could have a look and critique, maybe point the way to the shortest route through this I'd really appreciate it!
/**
* TTTapp.java - TicTacToe
* Implements a simple console-based version of the classic
* game Tic Tac Toe
* @author Todd Howe
* @version 1.0
* @since 2012-08-22
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.*;
/**
* Plays a simple game of Tic Tac Toe.
*
* A 3x3 char array is generated to accomodate a simple Tic Tac Toe
* game. The player picks a position, the computer picks randomly
* and the results are tallied in the usual manner.
*/
public class TTTapp extends JApplet implements ActionListener {
static boolean checkWin=false; // control variable to detect wins
static boolean boardFull=false; // control variable to detect ties
static String response=""; // user input
static int gridPos; // (1-9) 1D rep of position on the TTT board
static int x, y; // cartesian TTT board coordinates
static char player; // X or O to indicate current player
static char[][] board = new char[3][3]; // 2d TTT board matrix
static int[][] magicsquare = { {8, 1, 6}, // sekrit weapon
{3, 5, 7},
{4, 9, 2} };
Container con=getContentPane();
Font monoFont = new Font("Monospaced", Font.BOLD, 12);
JLabel gameBanner1=new JLabel("Welcome to Tic Tac Toe!");
JLabel gameBanner2=new JLabel("Player is X, Computer is O");
JLabel spacer1=new JLabel(" ");
JLabel spacer2=new JLabel(" ");
JLabel spacer3=new JLabel(" ");
JLabel board1=new JLabel(" 1 | 2 | 3 ");
JLabel board2=new JLabel("---|---|---");
JLabel board3=new JLabel(" 4 | 5 | 6 ");
JLabel board4=new JLabel("---|---|---");
JLabel board5=new JLabel(" 7 | 8 | 9 ");
JLabel announce=new JLabel("");
JLabel prompt=new JLabel("\nPick an available space by entering the number (1-9): ");
JTextField promptField=new JTextField("",4);
JButton submitButton=new JButton("Submit");
JButton restartButton=new JButton("RESTART GAME");
BoxLayout flow=new BoxLayout(con, BoxLayout.Y_AXIS);
public void init() {
// prep the application window
if (TTTapp.checkWin==false) {
con.setLayout(flow);
board1.setFont(monoFont);
board2.setFont(monoFont);
board3.setFont(monoFont);
board4.setFont(monoFont);
board5.setFont(monoFont);
gameBanner1.setAlignmentX(Component.CENTER_ALIGNMENT);
gameBanner2.setAlignmentX(Component.CENTER_ALIGNMENT);
board1.setAlignmentX(Component.CENTER_ALIGNMENT);
board2.setAlignmentX(Component.CENTER_ALIGNMENT);
board3.setAlignmentX(Component.CENTER_ALIGNMENT);
board4.setAlignmentX(Component.CENTER_ALIGNMENT);
board5.setAlignmentX(Component.CENTER_ALIGNMENT);
announce.setAlignmentX(Component.CENTER_ALIGNMENT);
prompt.setAlignmentX(Component.CENTER_ALIGNMENT);
promptField.setAlignmentX(Component.CENTER_ALIGNMENT);
submitButton.setAlignmentX(Component.CENTER_ALIGNMENT);
con.add(gameBanner1);
con.add(gameBanner2);
con.add(spacer1);
con.add(board1);
con.add(board2);
con.add(board3);
con.add(board4);
con.add(board5);
con.add(spacer2);
con.add(announce);
con.add(prompt);
con.add(promptField);
con.add(submitButton);
promptField.requestFocus();
System.out.println("Real INIT");
submitButton.addActionListener(this);
promptField.addActionListener(this);
}
else {
con.setLayout(flow);
board1.setFont(monoFont);
board2.setFont(monoFont);
board3.setFont(monoFont);
board4.setFont(monoFont);
board5.setFont(monoFont);
gameBanner1.setAlignmentX(Component.CENTER_ALIGNMENT);
gameBanner2.setAlignmentX(Component.CENTER_ALIGNMENT);
board1.setAlignmentX(Component.CENTER_ALIGNMENT);
board2.setAlignmentX(Component.CENTER_ALIGNMENT);
board3.setAlignmentX(Component.CENTER_ALIGNMENT);
board4.setAlignmentX(Component.CENTER_ALIGNMENT);
board5.setAlignmentX(Component.CENTER_ALIGNMENT);
announce.setAlignmentX(Component.CENTER_ALIGNMENT);
prompt.setAlignmentX(Component.CENTER_ALIGNMENT);
promptField.setAlignmentX(Component.CENTER_ALIGNMENT);
submitButton.setAlignmentX(Component.CENTER_ALIGNMENT);
restartButton.setAlignmentX(Component.CENTER_ALIGNMENT);
con.add(gameBanner1);
con.add(gameBanner2);
con.add(spacer1);
con.add(board1);
con.add(board2);
con.add(board3);
con.add(board4);
con.add(board5);
con.add(spacer2);
con.add(announce);
con.add(spacer3);
con.add(restartButton);
System.out.println("Else INIT");
restartButton.addActionListener(this);
}
}
public void actionPerformed(ActionEvent e) {
String discard="";
// MAIN GAME LOOP (CHANGED BOTH FORMER WHILE LOOPS TO IF TO ALLOW GETTEXT REFRESH)
// game reset
if (TTTapp.checkWin==true) {
TTTapp.checkWin=false;
TTTapp.boardFull=false;
TTTapp.response="";
TTTapp.board = new char[3][3];
TTTapp.gridPos=(-1);
announce.setText("");
con.remove(announce);
con.remove(restartButton);
con.remove(spacer3);
board1.setText(" 1 | 2 | 3 ");
board2.setText("---|---|---");
board3.setText(" 4 | 5 | 6 ");
board4.setText("---|---|---");
board5.setText(" 7 | 8 | 9 ");
promptField.requestFocus();
init();
TTTapp.response=promptField.getText();
promptField.setText("");
// ### ARGH THIS DOESN'T READ promptField IN INIT() ################
}
if (TTTapp.boardFull==false && TTTapp.checkWin==false) {
// ###############################################################
// System.out.println("boardFull: "+boardFull+" checkWin: "+checkWin);
displayBoard(TTTapp.board);
// player's turn
// System.out.println("Player's turn");
TTTapp.player='X';
TTTapp.gridPos=(-1);
// WAS WHILE
while (TTTapp.gridPos==(-1)) { // this was my last attempt to capture the bad response
if (TTTapp.gridPos==(-1)) {
TTTapp.response=promptField.getText();
promptField.setText("");
// ##################################################
System.out.println("Got here, response is "+response);
TTTapp.gridPos=validateResponse(TTTapp.response, TTTapp.board);
// #######################################
System.out.println("GridPos is "+gridPos);
TTTapp.boardFull=playGridPos(TTTapp.gridPos, TTTapp.board, TTTapp.player);
// ########################################
System.out.println("boardFull: "+boardFull);
displayBoard(TTTapp.board);
}
}
// check for player win
TTTapp.checkWin=scanBoard(TTTapp.board, TTTapp.magicsquare, TTTapp.player);
if (TTTapp.checkWin==true) {
displayBoard(TTTapp.board);
con.remove(prompt);
con.remove(promptField);
con.remove(submitButton);
announce.setText("\nPLAYER WINS");
}
// computer's turn
TTTapp.player='O';
if (TTTapp.checkWin==false) {
if (TTTapp.boardFull==false) {
TTTapp.gridPos=computerPlays(TTTapp.board);
TTTapp.boardFull=playGridPos(TTTapp.gridPos, TTTapp.board, TTTapp.player);
displayBoard(TTTapp.board);
}
//check for computer win here
TTTapp.checkWin=scanBoard(TTTapp.board, TTTapp.magicsquare, TTTapp.player);
if (TTTapp.checkWin==true) {
displayBoard(TTTapp.board);
con.remove(prompt);
con.remove(promptField);
con.remove(submitButton);
announce.setText("\nCOMPUTER WINS");
}
}
}
if (TTTapp.checkWin==false && TTTapp.boardFull==true) {
displayBoard(TTTapp.board);
con.remove(prompt);
con.remove(promptField);
con.remove(submitButton);
announce.setText("\nTIE GAME");
}
if (TTTapp.checkWin==true) {
init();
}
repaint();
}
/**
* Display the TicTacToe board as a grid
* @param board 2D char array representing game board
*/
public void displayBoard(char[][] board) {
int gridPos; // (1-9) 1d rep of position on the board
char[] cell=new char[9]; // array of parsed TTT board elements
char readCell; // single TTT element
int line; // printout line number
String[] boardDisplay = new String[5]; // array for 'console printing' to applet
// System.out.println("");
// stores each element of the displayBoard readout in cell[]
for (int x=0; x<3; x++) {
for (int y=0; y<3; y++) {
gridPos=((x*3)+y);
readCell=parseCell(board[x][y]);
if (readCell==' ') {
cell[gridPos]=(char)(gridPos+49);
}
else {
cell[(x*3)+y]=readCell;
}
}
// constructs tic tac toe grid display line by line
line=x*2;
boardDisplay[line]=(" "+cell[((x*3)+0)]+" | "+cell[((x*3)+1)]+" | "+cell[((x*3)+2)]);
if(x<2) {
line+=1;
boardDisplay[line]=("---|---|---");
}
}
board1.setText(boardDisplay[0]);
board2.setText(boardDisplay[1]);
board3.setText(boardDisplay[2]);
board4.setText(boardDisplay[3]);
board5.setText(boardDisplay[4]);
}
/**
* Checks desired player board position against vacancies on TTT grid
* @param response String of user response
* @param board 2D char array representing game board
*/
public int validateResponse(String response,char[][] board) {
int checkPos=(-1); // desired TTT board position
ArrayList emptyCells=new ArrayList(); // array of available positions
Integer somePos; // candidate TTT board position
int result=(-1); // result returns position or -1 signal to try again
boolean contains=false; // candidate cell matches desired position
// populate emptyCells with (1-9) positions
emptyCells=findEmptyCells(board);
// checks whether there's any empty cells left
if (emptyCells.size() > 0) {
// checks whether the input is at least one char long
if (response.length()>0) {
checkPos=(int)(response.charAt(0)-48);
int i=0;
while (i<emptyCells.size()) {
// #########################################################
// System.out.println("emptyCells("+i+"): "+emptyCells.get(i));
// checks whether requested cell is empty or not
somePos=(Integer)(emptyCells.get(i));
// ############################################################
// System.out.println("somePos: "+somePos+" checkPos: "+checkPos);
if ((somePos.intValue())!=checkPos) {
i++;
}
else {
if ((somePos.intValue())==checkPos) {
contains=true;
i=99;
}
}
}
// if requested cell is empty, validate request
if (contains==true) {
result=checkPos;
}
// otherwise send pick again signal
else {
// #################################################################
// System.out.println("Contains is "+contains+" checkPos is "+checkPos);
result=(-1);
}
}
// if no entry detected, send pick again signal
else {
result=(-1);
}
}
return(result);
}
/**
* Further validates and places player marker on the board
* @param gridPos Int desired position of marker on board
* @param board 2D char array representing game board
* @param player Char marker representing player
*/
public boolean playGridPos(int gridPos, char[][] board, char player) {
int x,y; // cartesian TTT board coordinates
ArrayList emptyCells=new ArrayList(); // array of available positions
boolean boardFull=false; // control variable to detect ties
switch (gridPos) {
// catch invalid entry signal
case(-1):
announce.setText("\nPLEASE ENTER ONE OF THE VISIBLE NUMBERS.");
break;
// place the player marker on the board
default:
gridPos--;
x=gridPos/3;
y=gridPos-(x*3);
board[x][y]=player;
}
emptyCells=findEmptyCells(board);
// check to see if the board is now full
if (emptyCells.size()==0) {
boardFull=true;
}
return(boardFull);
}
/**
* Determines the computer's next move
* @param board 2D char array representing game board
*/
public int computerPlays(char[][] board) {
int gridPos=(-1); // (1-9) 1d rep of position on the board
int temp; // random board position variable
String response=""; // user input
Integer availPos; // Integer rep of board position index
ArrayList emptyCells=new ArrayList(); // array of available positions
boolean vacant=false; // control variable for a position's vacancy
boolean checkWin=false; // control variable to detect a win
char[][] tempBoard = new char[3][3]; // board to test positions for a win
int[][] magicsquare = { {8, 1, 6}, // sekrit weapon
{3, 5, 7},
{4, 9, 2} };
emptyCells=findEmptyCells(board);
// check to see if we have an available win and pick that
for (int i=0; i<emptyCells.size(); i++) {
response=(""+emptyCells.get(i));
temp=validateResponse(response, board);
availPos=(Integer)temp;
temp=availPos.intValue();
// copy board to tempBoard
for(int x=0; x<3; x++) {
for(int y=0; y<3; y++) {
tempBoard[x][y]=board[x][y];
}
}
// if there's two adjacent Os, find the win
if (checkWin==false) {
checkWin=playGridPos(temp,tempBoard,'O');
checkWin=scanBoard(tempBoard,magicsquare,'O');
if (checkWin==true) {
gridPos=temp;
}
}
}
// else pick a random square
if (checkWin==false) {
while (vacant==false) {
temp=(int)(Math.random()*(emptyCells.size()-1));
response=(""+emptyCells.get(temp));
gridPos=validateResponse(response, board);
if (gridPos!=(-2) && gridPos!=(-1)) {
availPos=(Integer)gridPos;
gridPos=availPos.intValue();
vacant=true;
}
}
}
return(gridPos);
}
/**
* Detect vacant elements in TicTacToe matrix
* @param board 2D char array representing game board
*/
public ArrayList findEmptyCells(char[][] board) {
ArrayList emptyCells=new ArrayList(); // array of available positions
char readCell; // contents of a candidate cell
Integer availPos; // integer rep of board position index
for (int x=0; x<3; x++) {
for (int y=0; y<3; y++) {
availPos=Integer.valueOf((x*3)+y+1);
readCell=parseCell(board[x][y]);
if (readCell==' ') {
emptyCells.add(availPos);
}
}
}
return(emptyCells);
}
/**
* Scores the TTT board using a magic square to tally row/col/diag
* @param board 2D char array representing game board
* @param magicsquare 2D matrix to to help score TTT board
* @param player Char marker representing player
*/
public boolean scanBoard(char[][] board, int[][] magicsquare, char player) {
char readCell; // contents of a candidate cell
int[][] keyScores=new int[3][3]; // matrix for magicsquare scoring
boolean checkWin=false; // control variable to detect wins
// mask magicsquare with player's occupied cells
for (int x=0; x<3; x++) {
for (int y=0; y<3; y++) {
readCell=parseCell(board[x][y]);
if (readCell==player) {
keyScores[x][y]=magicsquare[x][y];
}
else {
keyScores[x][y]=0;
}
}
}
// check for a win
// horizontals
for (int x=0; x<3; x++) {
if ((keyScores[x][0]+keyScores[x][1]+keyScores[x][2])==15)
checkWin=true;
}
//verticals
for (int y=0; y<3; y++) {
if ((keyScores[0][y]+keyScores[1][y]+keyScores[2][y])==15)
checkWin=true;
}
//diagonals
if ((keyScores[0][0]+keyScores[1][1]+keyScores[2][2])==15)
checkWin=true;
if ((keyScores[0][2]+keyScores[1][1]+keyScores[2][0])==15)
checkWin=true;
return(checkWin);
}
/**
* Returns a processed representation of one TTT cell element
* @param token Char contents of a single TTT cell
*/
public char parseCell(char token) {
char result=' '; // holds processed contents of cell
switch(token) {
case('\u0000'):
result=(' ');
break;
case('x'):
case('X'):
result=('X');
break;
case('o'):
case('O'):
result=('O');
break;
default:
result=token;
}
return(result);
}
}
Your actionPerformed
logic is wrong.
First, you shouldn't dump all this, unrelated, content into a single actionPerformed
. You should have one actionPerformed
for each separate component (the text field, the buttons).
If you can't then you should check who actually fired the event (either check the ActionEvent.getSource
or ActionEvent.getActionCommand
)
NOW, I did rectify this by changing
if (BadApplet.checkWin == true) {
// Stuff here
}
if (BadApplet.boardFull == false && BadApplet.checkWin == false) {
// more stuff here
}
to
if (BadApplet.checkWin == true) {
// Stuff here
} else if (BadApplet.boardFull == false && BadApplet.checkWin == false) {
// more stuff here
}
I'd highly recommend that you learn how to debug your source ;)