views:

81

answers:

8

I am the beginnings of writing a tic-tac-toe game. I just ran it and got the following stack trace:

 Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
 at java.util.ArrayList.rangeCheck(ArrayList.java:571)
 at java.util.ArrayList.get(ArrayList.java:349)
 at TicTacToe.isMarked(TicTacToe.java:23)
 at TicTacToe.mark(TicTacToe.java:59)
 at TicTacToe.main(TicTacToe.java:7)

I suspect it is a problem with the way I have the ArrayList setup? I read somewhere about nulls causing problems, but this is my first time dealing with arrays, so I'm not to familiar with the subject. Anyhow, here is my code:

    import java.util.*;

    public class TicTacToe {

    public static void main(String[] args) {
    newBoard();
    ******************System.out.println(mark(1));************
    System.out.println(mark(5));
    System.out.println(mark(9));
   }

 // Creates a blank board.
 public static ArrayList<String> newBoard() {
  ArrayList<String> board = new ArrayList<String>(8);
  return board;
 }

 // Returns true if the square has been marked.
 public static boolean isMarked(int numberOfSquare) {
  if (numberOfSquare > 9 || numberOfSquare < 1) {
   throw new IllegalArgumentException("Input a valid square number.");
  }
  ************if (newBoard().get(numberOfSquare - 1) == null) {***********
   return false;
  } else
   return true;
 }

 // Returns the number of moves that have been made.
 public static int moveCount() {
  return countMove();
 }

 // If called, adds 1 to number of moves.
 public static int countMove() {
  int moveNumber = 0;
  moveNumber++;
  return moveNumber;
 }

 // Checks for a win at the specified array location and player (X or O).
 public static boolean checkForWin(int x, int y, int z, int player) {
  if (player == 0) {
   return (newBoard().get(x)).equals("O")
     && (newBoard().get(y)).equals("O")
     && (newBoard().get(y)).equals("O");
  } else {
   return (newBoard().get(x)).equals("O")
     && (newBoard().get(y)).equals("O")
     && (newBoard().get(y)).equals("O");
  }
 }

 // Places an X or O on the specified square.
 public static boolean mark(int markSquareNumber) {
  if (markSquareNumber > 9 || markSquareNumber < 1) {
   throw new IllegalArgumentException("Input a valid square number.");
  }
  ***********if (isMarked(markSquareNumber)) {*******************
   throw new IllegalArgumentException("Square is already marked.");
  }
  if (moveCount() % 2 != 0) {
   newBoard().add(markSquareNumber - 1, "X");
   countMove();
  } else {
   newBoard().add(markSquareNumber - 1, "O");
   countMove();
  }
  if (checkForWin(0, 1, 2, 1) || checkForWin(3, 4, 5, 1)
    || checkForWin(6, 7, 8, 1)) {
   System.out.println("Player-X just won horizontally!");
   return true;
  } else if (checkForWin(0, 3, 6, 1) || checkForWin(1, 4, 7, 1)
    || checkForWin(2, 5, 8, 1)) {
   System.out.println("Player-X just won vertically!");
   return true;
  } else if (checkForWin(0, 4, 5, 1) || checkForWin(2, 4, 6, 1)
    || checkForWin(0, 4, 8, 1)) {
   System.out.println("Player-X just won diagonally!");
   return true;
  }
  if (checkForWin(0, 1, 2, 0) || checkForWin(3, 4, 5, 0)
    || checkForWin(6, 7, 8, 0)) {
   System.out.println("Player-O just won horizontally!");
   return true;
  } else if (checkForWin(0, 3, 6, 0) || checkForWin(1, 4, 7, 0)
    || checkForWin(2, 5, 8, 0)) {
   System.out.println("Player-O just won vertically!");
   return true;
  } else if (checkForWin(0, 4, 5, 0) || checkForWin(2, 4, 6, 0)
    || checkForWin(0, 4, 8, 0)) {
   System.out.println("Player-O just won diagonally!");
   return true;
  } else
   return false;
 }
}

I just put a whole bunch of asterisks by the lines that came up in the stack-trace. If anyone could point out where I am going wrong that would be splendid, thanks!

Ok, here is the solution that I came up with after all your wonderful input: (Please only use this for educational and reference purposes, I don't want to get yelled at by my professor if you are in my CS1410 class and you copy me!!!!)

//Written by JTN for Assignment7.3- CS1410; October 2010. 
import java.util.*;

public class TicTacToe {
private static int moveNumber = 0;
private static ArrayList<String> board = new ArrayList<String>(8);
    public static void main(String[] args) {
        newBoard();
        mark(1);mark(2);
        mark(5);mark(3);
        mark(9);
        boardString();
    }
    // Returns the number of moves that have been made.
    public static int moveCount() {
        return (countMove()-1);
    }

    // If called, adds 1 to number of moves.
    public static int countMove() {
        moveNumber= moveNumber + 1;
        return moveNumber;
    }
    // Creates a blank board.
    public static ArrayList<String> newBoard() {
        for (int i = 0; i <= 8; i++)
            board.add("_");
        return board;
    }

    // Returns true if the square has been marked.
    public static boolean isMarked(int numberOfSquare) {
        if (numberOfSquare > 9 || numberOfSquare < 1) {
            throw new IllegalArgumentException("Input a valid square number.");
        }
        if ((board.get(numberOfSquare - 1)).equals("_")) {
            return false;
        } else
            return true;
    }



    // Checks for a win at the specified array location and player (X or O).
    public static boolean checkForWin(int x, int y, int z, int player) {
        if (player == 0) {
            return     (board.get(x)).equals("O")
                    && (board.get(y)).equals("O")
                    && (board.get(z)).equals("O");
        } 
        else {
            return     (board.get(x)).equals("X")
                    && (board.get(y)).equals("X")
                    && (board.get(z)).equals("X");
        }
    }

    // Places an X or O on the specified square.
    public static boolean mark(int markSquareNumber) {
        if (markSquareNumber > 9 || markSquareNumber < 1) {
            throw new IllegalArgumentException("Input a valid square number.");
        }
        if (isMarked(markSquareNumber)) {
            throw new IllegalArgumentException("Square is already marked.");
        }       
        if ((countMove() % 2) == 0){
            board.set(markSquareNumber - 1, "O");
        }
        else {
            board.set(markSquareNumber - 1, "X");
        } 

        if (checkForWin(0, 1, 2, 1) || checkForWin(3, 4, 5, 1)
                || checkForWin(6, 7, 8, 1)) {
            System.out.println("Player-X just won horizontally!");
            return true;
        } else if (checkForWin(0, 3, 6, 1) || checkForWin(1, 4, 7, 1)
                || checkForWin(2, 5, 8, 1)) {
            System.out.println("Player-X just won vertically!");
            return true;
        } else if (checkForWin(0, 4, 5, 1) || checkForWin(2, 4, 6, 1)
                || checkForWin(0, 4, 8, 1)) {
            System.out.println("Player-X just won diagonally!");
            return true;
        }
         else if (checkForWin(0, 1, 2, 0) || checkForWin(3, 4, 5, 0)
                || checkForWin(6, 7, 8, 0)) {
            System.out.println("Player-O just won horizontally!");
            return true;
        } else if (checkForWin(0, 3, 6, 0) || checkForWin(1, 4, 7, 0)
                || checkForWin(2, 5, 8, 0)) {
            System.out.println("Player-O just won vertically!");
            return true;
        } else if (checkForWin(0, 4, 5, 0) || checkForWin(2, 4, 6, 0)
                || checkForWin(0, 4, 8, 0)) {
            System.out.println("Player-O just won diagonally!");
            return true;
        } else
            return false;
    }

    public static String boardString(){
        String row1 = board.get(0)+"|"+board.get(1)+"|"+board.get(2);
        String row2 = board.get(3)+"|"+board.get(4)+"|"+board.get(5);
        String row3 = board.get(6)+"|"+board.get(7)+"|"+board.get(8);
        System.out.println(row1);
        System.out.println(row2);
        System.out.println(row3);
        return row1+row2+row3;
    }
}
+2  A: 

It looks like there is no initial population of the list. You're trying to access elements that don't exist.

javamonkey79
+8  A: 

This line

ArrayList<String> board = new ArrayList<String>(8);

will not create an array of 8 strings (or 9 strings for that matter, if that was the intention). It will create an ArrayList with initial capacity of 8 elements, but of size 0.

After you've created the ArrayList for the board, you'll have to populate it with elements using the add-method. Try doing something like:

public static ArrayList<String> newBoard() {
    ArrayList<String> board = new ArrayList<String>(8);
    for (int i = 0; i < 9; i++)
        board.add("");
    return board;
}

The message Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 says that you're trying to access the 0th element in a list of length 0 (in a list without elements).

Furthermore, the following line:

newBoard().add(markSquareNumber - 1, "X");

should probably be

newBoard().set(markSquareNumber - 1, "X");
aioobe
Thanks! That seemed to have fixed that problem! :) For the record and for this particular situation, it needs to be i <= 8 or you will still get the same error because 0-8 exclusive is only 8 and there are nine boxes in tic-tac-toe, but just a minor tweak. Thanks again for solving my stupidity!
Mr_CryptoPrime
Thats a good point. I'll update my answer :-)
aioobe
+2  A: 

From the wall of unformatted code, I think I know your problem.

public static ArrayList newBoard() { 
    ArrayList board = new ArrayList(8); 
    return board; 
}

Doesn't initialize anything in the arraylist. It is currently empty with 0 objects. This is what you want.

public static ArrayList newBoard() { 
    ArrayList<String> board = new ArrayList<String>();
    for (int i = 0; i < 9; i++) {
        board.add("");
    }
    return board; 
}

I question why you don't just use a String[], in which case new String[9] will create 9 actual Strings.

And by the way, a tic tac toe board has 9 squares, I don't know what you only created it with 8.

shoebox639
A: 

For a fixed array size like you have here, I'm not sure why you're using an ArrayList, and not just a simple String[] array? If you initialize your array with:

String[] board = new String[9];

You will then actually be able to get the value of any square without having to worry about being out of bounds.

zigdon
A: 

You are calling your newBoard() function every time you try to get an element from your ArrayList<String>. This will return an empty ArrayList<String> every time.

You need to initialize the list and then call the get function on that variable. The other answers explain this.

Austyn Mahoney
Thanks, that helped a lot!
Mr_CryptoPrime
A: 

newBoard().get(numberOfSquare - 1) == null)

everytime you call newBoard() method, you creates a new ArrayList object with no element. on an empty arraylist if you try to get something, you'll get an IndexOutOfBoundsException.

Erhan Bagdemir
A: 

also besides the stated problem.. u seem to be calling a newBoard() method each time.. creating a new ArrayList.. which wouldn't actually have anything as people have stated above.. but it also doesn't make sense to do that.. u want to probably have a single board object to check whether a player has won or not

bala singareddy
+1  A: 

ArrayList is a dinamic structure. Even if you put a "size" when you create it, it will not fill the arraylist. If you want you can fill it by yourself or use a simple Array.

Other thing, your countMove() function is always returning 1. Because you are setting 0 and appling the ++ OP to 0 as well. If you want that behavior just return 1

Hope this helps. Cheers.

RDAM
Yes, I just realized this too...any idea how to get it to increase each time a move is made? I go off the fact that if x always goes first, then they will always be on an odd move 1,3,5,etc...so having it incremented each time a person marks something is imperative.
Mr_CryptoPrime
you can have a global variable and countMove() just for increment. But if you want to have a count per user you can have an Array and each entry of that array is the count for that user.
RDAM
a better and elegant solution is to have a class "Player" and one variable "moves". Each instance will modify its local variable and is more "clean".
RDAM
Thanks! I think I have it all running how I want to now!
Mr_CryptoPrime