1

I've been learning Java for about 4 months now, it's the first programming language I learn. For school we have to do a project, a console-based game. I chose for Boggle.

I have an ArrayList with dices, each one gets a random 'side up', and then the ArrayList gets shuffled and a two-dimensional array gets filled with the values of each side up. At this moment the Array is filled with Strings, chars may be a better option but something that's fairly easy to change.

The problem I'm facing is that I need to be able to find words in the array. Words in Boggle can go in any direction, each unique block can be used only once per word, but the path can cross, you can search diagonally too. I managed to find if the first letter is present in the array. If not than the search can be aborted, if it is present there needs to start a search that searches for the second character of the word, that has to be around the first character's block.

I did some math and found that it's always for example "i-1 and j-1" as the upper left corner of the surrounding blocks. I worked this out but can't seem to be able to find words... Also, if there are 2 "e"'s surrounding, I have no clue how to search for the word trying each "e". Here is my code so far:

This is my most important class at this moment, the class Gameboard

public class Gameboard {

private List<Dice> dices = new ArrayList<Dice>();
private final int boardSize;
private String[][] board;
private boolean [][] blocksAvailable;
private Random random = new Random();

public GameBoard() {
    // Making the board with a given size (will be changeable later but is "4" for now)
    boardSize = 4;
    board = new String[boardSize][boardSize];
    blocksAvailable = new boolean[boardSize][boardSize];
    for(int i = 0; i < boardSize; i++) {
        for(int j = 0; j < boardSize; j++) {
            blocksAvailable[i][j] = true;
        }
    }
}

public String[][] getBoard() {
    return board;
}

public int getFullSize() {
    return boardSize*boardSize;
}

public void makeBoard() {
    //random "side up" for each dice
    for(int i = 0; i < dices.size(); i++) {
        dices.get(i).setSideUp();
    }

    // Shuffle all dices
    Collections.shuffle(dices);

    // Fill the board with the values of the dices
    int counter = 0;
    for(int i = 0; i < boardSize; i++) {
        for(int j = 0; j < boardSize; j++) {
            board[i][j] = dices.get(counter++).getSideUp();
        }
    }


}

public String showBoard() {
    //Show the board, each block divided by "|"
    String str = "";
    for(int i = 0; i < boardSize; i++) {
        for(int j = 0; j < boardSize; j++) {
            str += String.format("|%s|", board[i][j].toString());
            if(j == 3) {
                str += "\n";
            }
        }
    }
    return str;
}

public void addDices() {
    dices.add(new dice("R", "I", "F", "O", "B", "X"));
    dices.add(new dice("I", "F", "E", "H", "E", "Y"));
    dices.add(new dice("D", "E", "N", "O", "W", "S"));
    dices.add(new dice("U", "T", "O", "K", "N", "D"));
    dices.add(new dice("H", "M", "S", "R", "A", "O"));
    dices.add(new dice("L", "U", "P", "E", "T", "S"));
    dices.add(new dice("A", "C", "I", "T", "O", "A"));
    dices.add(new dice("Y", "L", "G", "K", "U", "E"));
    dices.add(new dice("Q", "B", "M", "J", "O", "A"));
    dices.add(new dice("E", "H", "I", "S", "P", "N"));
    dices.add(new dice("V", "E", "T", "I", "G", "N"));
    dices.add(new dice("B", "A", "L", "I", "Y", "T"));
    dices.add(new dice("E", "Z", "A", "V", "N", "D"));
    dices.add(new dice("R", "A", "L", "E", "S", "C"));
    dices.add(new dice("U", "W", "I", "L", "R", "G"));
    dices.add(new dice("P", "A", "C", "E", "M", "D"));
}

public boolean searchWord(String word) {
    String wordUp = woord.toUpperCase();
    String firstLetter = Character.toString(wordUp.charAt(0));


    for(int i = 0; i < boardSize;i++) {
        for(int j = 0; j < boardSize;j++) {
            if(firstLetter.equals(board[i][j]) == true) {

                int a = i;
                int b = j;
                String theLetter = "";
                // First letter found, continue search
                for(int h = 1; h < hetWoord.length(); h++) {
                    theLetter = Character.toString(wordUp.charAt(h));
                    int[] values = searchLetter(theLetter, a, b);
                    if(values[0] > -1) {
                        a = values[0];
                        b = values[1];
                    } else {
                        return false;
                    }
                }
                return true;

            }
        }
    }
    return false;
}

public int[] searchLetter(String letter, int i, int j) {
    int[] values = new int[2];

    try{if(board[i-1][j-1].equals(letter) && blocksAvailable[i-1][j-1] == true) {
        values[0] = i-1;
        values[1] = j-1;
        blocksAvailable[i-1][j-1] = false;
    }  else if(board[i-1][j].equals(letter) && blocksAvailable[i-1][j] == true) {
        values[0] = i-1;
        values[1] = j;
        blocksAvailable[i-1][j] = false;
    } else if(board[i-1][j+1].equals(letter) && blocksAvailable[i-1][j+1] == true) {
        values[0] = i-1;
        values[1] = j+1;
        blocksAvailable[i-1][j+1] = false;
    } else if(board[i][j-1].equals(letter) && blocksAvailable[i][j-1] == true) {
        values[0] = i;
        values[1] = j-1;
        blocksAvailable[i][j-1] = false;
    } else if(board[i][j+1].equals(letter) && blocksAvailable[i][j+1] == true) {
        values[0] = i;
        values[1] = j+1;
        blocksAvailable[i][j+1] = false;
    } else if(board[i+1][j-1].equals(letter) && blocksAvailable[i+1][j-1] == true) {
        values[0] = i+1;
        values[1] = j-1;
        blocksAvailable[i+1][j+1] = false;
    } else if(board[i+1][j].equals(letter) && blocksAvailable[i+1][j] == true) {
        values[0] = i+1;
        values[1] = j;
        blocksAvailable[i+1][j] = false;
    } else if(board[i+1][j+1].equals(letter) && blocksAvailable[i+1][j+1] == true) {
        values[0] = i+1;
        values[1] = j+1;
        blocksAvailable[i+1][j+1] = false;
    } else {
        values[0] = -1;  // If not found, negative values, easy to check in searchWord if letter was found
        values[1] = -1;
    }}catch (ArrayIndexOutOfBoundsException e) {

    }

    return values;
}
}
E. V. d. B.
  • 785
  • 1
  • 10
  • 29
  • 5
    `catch (ArrayIndexOutOfBoundsException e) {}` just don't... – UmNyobe Jan 21 '13 at 13:02
  • The problem is we didn't really learn about the try - catch yet but I need it to prevent the program form stopping when there's an ArrayIndexOutOfBoundsException... How could I solve this? – E. V. d. B. Jan 21 '13 at 13:06
  • @E.V.d.B. What you are saying is basically : "_Something went wrong but that doesn't matter just do what you are supposed to do._" If there is an exception you are supposed to catch it and react accordingly to what happened. In your case you shouldn't even let the `ArrayIndexOutOfBoundsException` to get thrown. – Fallup Jan 21 '13 at 13:17
  • This sucks because I haven't learnt about try-catch yet, but it's really the only way to do this efficiently right? How should it work in my example then? – E. V. d. B. Jan 21 '13 at 13:20
  • @E.V.d.B. You don't get the point. Such case should never happen. And if it happens, try catch is good to tell us (or user) that something unexpected happened. If you know that there is some _edge_ case in your program then you should consider it while making your program logic. It is like you are producing the cars and you know THAT there is a chance(in fact like 99%) that breaks will fail. You can't sell such cars you should modify the manufacturing process of the breaks first so you will do everything you possibly can to prevent it from breaking. Do you see what I mean ? – Fallup Jan 21 '13 at 13:35
  • Problem is I'm afraid of writing a lot of unnecessary code. If I can't implement to search around the whole block, and if it's out of bounds don't search but search for within bounds in those cases only... then, if I understand what you mean, you are telling me I should code everything manually like for that corner search in those blocks, for the other corner search there, if it's on the upper side search those blocks ... Sounds like a not very efficient program right? It's driving me crazy I'm as stuck as I can be and have no idea how this will ever work :s – E. V. d. B. Jan 21 '13 at 14:02
  • Unnecessary code ? @Dariusz Wawer showed you in his answer that this "unnecessary code" is 4 lines long and handles all of the _edge cases_. I don't know what you mean by _coding manually_. And to the efficiency - I can tell you what is not good and efficient [controlling program flow with exceptions](http://stackoverflow.com/q/729379/1317692) NOTE: When you are reffering to some comment use **@Username** to notify the user who are you replying to. – Fallup Jan 21 '13 at 15:49
  • @Fallup I misunderstood everything and with the help of the answer below I am back on track now ;) There's only one thing I need to solve now and that's searching if there are more of the same letter bounding. – E. V. d. B. Jan 21 '13 at 16:00

5 Answers5

7

This may get deleted, because I am not going to answer your question. But please, pretty please, use:

// instead of: board[i-1][j-1]

public String getBoardValue(int x, int y) {
  if (x<0 || x>=boardSize) return "";
  if (y<0 || y>=boardSize) return "";
  return board[x][y];
}

Using such helper method you will

  • ensure no IndexArrayOutOfBoundsException is thrown
  • always have a non-null board value
Dariusz
  • 19,750
  • 7
  • 65
  • 104
  • So instead of board[i-1][j-1] it would be getBoardValue(i-1, j-1) (with the method declared in the class of course)? Problem is we haven't learnt about try-catch but I think it's the only way to do this efficiently? – E. V. d. B. Jan 21 '13 at 13:18
  • 1
    @EVDB Try/catch is never effective. It is the last resort which usually doesn't help anyway, except to show what went wrong. Since you are a beginner, never even try to catch a RuntimeException - if you are getting it it means you made a mistake somehow somewhere. – Dariusz Jan 21 '13 at 13:20
  • Hmm there is indeed a mistake, it goes to search the letters outside of the array when for example the first letter is in a corner... I tried it was solvable with try-catch, but how can I let the program keep running in that case and search the blocks which are inside the array? – E. V. d. B. Jan 21 '13 at 13:24
  • 1
    @E.V.d.B think on my code for a while. You should come to a solution. Giving it to you would strip you of the pleasure of getting there yourself:) – Dariusz Jan 21 '13 at 13:29
  • @Darius Wawer I am trying to understand but I don't really see why you would return "" and why it would be helpful :s – E. V. d. B. Jan 21 '13 at 13:58
  • @E.V.d.B. Please don't get worried. 1)Return ->"invalid word" 2)no IndexArrayOutOfBoundsException – joey rohan Jan 21 '13 at 14:07
  • @Darius Wawer Oh damn you are my hero :D My main problems are solved now, GREAT!!! Thanks so much :) – E. V. d. B. Jan 21 '13 at 14:23
  • @Joey Rohan Just realised what he meant, but thanks to you too ;) Helped me a lot! – E. V. d. B. Jan 21 '13 at 14:24
  • @E.V.d.B. Don't forget to look at mini projects.They will help you alot – joey rohan Jan 21 '13 at 14:30
0

Well, what you have asked is little complex.You have to look at all the possible directions for the word in your Array.I would suggest you should look at the mini projects and see their logic of evaluation.You can find many projects on google.Example http://1000projects.org/java-projects-gaming.html

I am not asking you to copy paste,but just to get an idea of doing things in a proper way. Few years back i made an X-O game in c/c++, can give you the code(if you want) of checking the combinations of x's and o's(you can make out the code,the syntax in not much different).

joey rohan
  • 3,329
  • 5
  • 27
  • 69
  • Thanks for the tip, I managed to look for every possibility if the first letter is in there multiple times, but I still need to find out if there's a letter with bounding letters that are the same how to solve that, so I'll look at some examples! – E. V. d. B. Jan 21 '13 at 14:44
0

Your method searchLetter can be rewritten to be more understandable.

public boolean isValid(int x, int y) {
   return x>= 0 && x < boardSize && y >=0 && y < boardSize;
}

public int[] searchLetter(String letter, int i, int j) {

   int[] values = new int[2];
  //initialization as not found.
   values[0] = -1;
   values[1] = -1; 
   for(int ix = i-1; ix <= i+1 ; ix++){
      for(int jx = j-1; jx <= j+1 ; jx++){
         if(i == ix && j == jx)
            //skip the cell from were the search is performed
            continue;

         if(isValid(ix,jx)
            && board[ix][jx].equals(letter) 
            && blocksAvailable[ix][jx] == true) {
              values[0] = ix;
              values[1] = jx;
              blocksAvailable[ix][jx] = false;
              //early return 
              return values;
         }
      }
    return values;
}

Even without comments, a reader has the hint that this is some kind of neighboring search.

The fact is your algorithm should return a list of possible next positions.The code above return the first occurrence. If you put each pair of indices in a list rather than returning the first one, you will see all the candidate letters for your word. And while doing so, you will be implementing the getNeighbour or adjancentVertexes of a Breath first search.

UmNyobe
  • 21,341
  • 8
  • 52
  • 85
  • IMO the code is not understandable at all. Such a large method should be split into smaller methods, each doing atomic operations. In other words, this method does too much. Besides, why wont it return a Dimension object? Or some kind of Pair? – Dariusz Jan 21 '13 at 15:46
  • I am not saying the code is correct or well written. I am saying the code is equivalent in its purpose. And the OP seems to not know some classes so this is a first step. – UmNyobe Jan 21 '13 at 15:56
0

One tip: Once you find the initial letter, instead of looking for the next letters one by one, just compute the 8 possible words of same length you can get from that point. You might find less than 8 depending on the position of that letter on the board. Once you are able to figure this out for one direction, it is going to be easier to translate that logic to the rest of possible directions. Then you just need to compare those words and if none of then match, find the initial letter and repeat the same process.

Russell
  • 298
  • 2
  • 10
0

I got asked this on an interview, I didn't have much time left so I explained my interviewer how I would approach this problem. He seemed ok with my answer and didn't even ask me to try to code it, probably because he knew this problem is not as trivial as it might look and we didn't have enough time. I gave it a try at home after my interview finished but my solution had some bugs. Kept thinking about it and came up with the ideas I posted previously, then noticed this post is 3 years old so I decided to code it. This solution works but has room for improvements:

import java.util.LinkedHashSet;
import java.util.Set;


public class StringsArrays {

    public static void main(String[] args) {
        final char[][] matrix = new char[4][4];
        matrix[0] = new char[] { 'G', 'A', 'B', 'C' };
        matrix[1] = new char[] { 'O', 'O', 'E', 'F' };
        matrix[2] = new char[] { 'O', 'H', 'O', 'I' };
        matrix[3] = new char[] { 'J', 'K', 'L', 'D' };


        System.out.println(search("JOOG", matrix)); //N
        System.out.println(search("BEOL", matrix)); //S
        System.out.println(search("AB", matrix));   //E
        System.out.println(search("FE", matrix));   //W
        System.out.println(search("HEC", matrix));  //NE
        System.out.println(search("DOOG", matrix)); //NW
        System.out.println(search("GOOD", matrix)); //SE
        System.out.println(search("FOK", matrix));  //SW        
        System.out.println(search("HO", matrix));

    }

    public static boolean search(final String word, char[][] matrix) {

        final char firstLetter = word.charAt(0);
        for (int y = 0; y < matrix.length; y++) {
            for (int x = 0; x < matrix[y].length; x++) {
                if (matrix[y][x] == firstLetter) {
                    final Set<String> words = readInAllDirections(word.length(), x, y, matrix);
                    if (words.contains(word)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    enum Direction {
        NORTH, SOUTH,
        EAST, WEST,
        NORTH_EAST, NORTH_WEST,
        SOUTH_EAST, SOUTH_WEST
    }

    private static Set<String> readInAllDirections(final int length, final int x, final int y, final char[][] matrix) {
        final Set<String> words = new LinkedHashSet<>();
        for (final Direction direction : Direction.values()) {
            words.add(readWord(length, x, y, matrix, direction));
        }
        return words;
    }

    private static String readWord(final int length, final int xBegin, final int yBegin, final char[][] matrix, final Direction direction) {
        final int xEnd = getXEnd(xBegin, length, direction);
        final int yEnd = getYEnd(yBegin, length, direction);
        int x;
        int y;

        final StringBuilder matrixWord = new StringBuilder();

        if (direction == Direction.SOUTH) {
            if (yEnd > matrix.length-1) {
                return null;
            }
            for (y = yBegin; y <= yEnd; y++) {
                matrixWord.append(matrix[y][xBegin]);
            }
        }
        if (direction == Direction.NORTH) {
            if (yEnd < 0) {
                return null;
            }
            for (y = yBegin; y >= yEnd; y--) {
                matrixWord.append(matrix[y][xBegin]);
            }
        }
        if (direction == Direction.EAST) {
            if (xEnd > matrix[yBegin].length-1) {
                return null;
            }
            for (x = xBegin; x <= xEnd; x++) {
                matrixWord.append(matrix[yBegin][x]);
            }
        }
        if (direction == Direction.WEST) {
            if (xEnd < 0) {
                return null;
            }
            for (x = xBegin; x >= xEnd; x--) {
                matrixWord.append(matrix[yBegin][x]);
            }
        }
        if (direction == Direction.SOUTH_EAST) {
            if (yEnd > matrix.length-1 || xEnd > matrix[yBegin].length-1) {
                return null;
            }
            x = xBegin;
            y = yBegin;
            while (y <= yEnd && x <= xEnd) {
                matrixWord.append(matrix[y][x]);
                y++;
                x++;
            }
        }
        if (direction == Direction.SOUTH_WEST) {
            if (yEnd > matrix.length-1 || xEnd < 0) {
                return null;
            }
            x = xBegin;
            y = yBegin;
            while (y <= yEnd && x >= xEnd) {
                matrixWord.append(matrix[y][x]);
                y++;
                x--;
            }
        }
        if (direction == Direction.NORTH_EAST) {
            if (yEnd < 0 || xEnd > matrix[yBegin].length-1) {
                return null;
            }
            x = xBegin;
            y = yBegin;
            while (y >= yEnd && x <= xEnd) {
                matrixWord.append(matrix[y][x]);
                y--;
                x++;
            }
        }
        if (direction == Direction.NORTH_WEST) {
            if (yEnd < 0 || xEnd < 0) {
                return null;
            }
            x = xBegin;
            y = yBegin;
            while (y >= yEnd && x >= xEnd) {
                matrixWord.append(matrix[y][x]);
                y--;
                x--;
            }
        }

        return matrixWord.toString();
    }

    private static int getYEnd(final int y, final int length, final Direction direction) {
        if (direction == Direction.SOUTH || direction == Direction.SOUTH_EAST || direction == Direction.SOUTH_WEST) {
            // y0 + length + ? = y1
            return y + length - 1;
        }
        if (direction == Direction.NORTH || direction == Direction.NORTH_EAST || direction == Direction.NORTH_WEST) {
            // y0 - length + ? = y1
            return y - length + 1; 
        }

        // direction == Direction.EAST || direction == Direction.WEST)
        return y;
    }

    private static int getXEnd(final int x, final int length, final Direction direction) {
        if (direction == Direction.EAST || direction == Direction.NORTH_EAST || direction == Direction.SOUTH_EAST) {
            // x0 + length + ? = x1
            return x + length - 1;
        }
        if (direction == Direction.WEST || direction == Direction.NORTH_WEST || direction == Direction.SOUTH_WEST) {
            // x0 - length + ? = x1
            return x - length + 1;
        }

        // direction == Direction.NORTH || direction == Direction.SOUTH)
        return x;
    }
}
Russell
  • 298
  • 2
  • 10