Java: search words in two dimensional array - java

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;
}
}

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

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).

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.

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.

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;
}
}

Related

Is there any COMPACT way to check winner in a simple tic-tac-toe game?

I am coding a simple tic-tac-toe for a high-school mini project, but I need it to be within a strict data volume (not more than 112 lines). I thought checking for each row, column and cross would be long, so is there any alternative to do so (You should see a [[[HERE]]] comment)? (Btw, I already know it looks awful) Thanks in advance!
public class TTTGame {
//OPTIONS v
public static final String draw = "DRAW"; // <- Definitions for different states
public static final String circles = "CIRCLES"; // BOT
public static final String crosses = "CROSSES"; // PLAYER
public static final String getCrosses = "X"; //<- Symbols to display
public static final String getCircles = "O";
//OPTIONS ^
//DO NOT MODIFY UNDER THIS LINE (Just kidding, do whatever u want) v
public static int[][] board = {
{0,0,0},
{0,0,0},
{0,0,0},
};
public static final int empty = 0; // Definition of the values
public static final int cross = 1;
public static final int circle = 2;
public static int turns = 0; //Just here to count turns, nothing special
public static void main(String[]args) { //Main process
board[1][1] = circle;
display();
while (true) {
PlayerTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
BotTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
display();
turns += 1;
}
}
private static void GStop() { //Force stop the match function
System.out.println("Winner : " + checkWinner());
System.exit(1);
}
private static boolean checkStop() { //Check if match is already full / completed (Draw)
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
if (board[x][y]==empty) return false;
return true;
}
#Nullable
private static String checkWinner() { //Check Winner
// [[[ HERE ]]] ---------------
return null;
}
private static void PlayerTurn() { //Player turn
int x; Scanner c = new Scanner(System.in);
while (true) {
x = c.nextInt();
x = x-1;
if ((x>=0)&&(x < 9)) {
if (board[x / 3][x % 3] == empty) {
board[x / 3][x % 3] = cross;
break;
} else System.out.println("Already chosen");
} else System.out.println("Invalid");
}
}
private static void BotTurn() { //Bot turn -> (Modify these to change the AI behaviour, here's a very simple one);
boolean choose = true;
for (int y = 0; y < 3 ; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == empty&&choose) {
board[y][x] = circle;
choose = false;
}
}
private static void display() { //Display the board
int nn = 1;
String a = "z";
for (int y = 0; y < 3 ; y++) {
for (int x = 0; x < 3; x++) {
if (board[y][x] == 0) a = "*";
if (board[y][x] == 1) a = getCrosses;
if (board[y][x] == 2) a = getCircles;
System.out.print(a + " ");
}
System.out.print(" "); //Indications
for (int xn = 0; xn < 3; xn++) {
System.out.print(nn);
nn+=1;
System.out.print(" ");
}
System.out.println(" ");
}
}
}
How about this idea: (neither the only nor the best nor the most performant solution... just an idea)
You can use the sum of each row, diagonal and column to determine if the either player one (all 1s) or player two (all 2s) wins. Therefore you only need to set the empty field to be higher than 6.
For example let's say your board looks like this:
7 1 1 -> 7+1+1 = 9 // no one wins
2 2 2 -> 2+2+2 = 6 // player two wins, he has 3 * 2 in a row
1 7 2 -> 1+7+2 =10 // no win here
if all three numbers where 1s (sum == 3) your player one wins.
It is "cumbersome" to implement, but as I said it is just an idea:
// first we check every column
for( int x=0; x<board[y].length; x++){
int sum = 0;
for( int y=0; y<board.length; y++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// then every row
for( int y=0; y<board.length; y++){
int sum = 0;
for( int x=0; x<board[y].length; x++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// and finally the diagonals (if we ever reach that part)
int sum= board[0][0] + board[1][1] + board[2][2];
if(sum == 3 || sum == 6){
return true;
}
sum= board[0][2] + board[1][1] + board[2][0];
if(sum == 3 || sum == 6){
return true;
}
you could also return 1 when the sum == 3 and the first player wins or 2 when player two wins.

search a given word into a letter puzzle diagonally

I have a two-dimensional array filled with random letters. I have words to find in that array.
I have written a toString method that uses:
startX : The start X position of the String to be found
startY : The start Y position of the String to be found
endX : The end X position of the String to be found
endY : The end Y position of the String to be found
The code that I provide works horizontally and vertically but does not work for diagonals. How can I print words which are placed in the array diagonally?
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (startX == endX) {
if (startY < endY) {
for (int i = startY; i <= endY; i++)
sb.append(i).append("x").append(startY).append(" ");
} else {
for (int i = endY; i <= startY; i++)
sb.append(i).append("x").append(startY).append(" ");
}
}
if (startY == endY) {
if (startX < endX) {
for (int i = startX; i <= endX; i++)
sb.append(i).append("x").append(startY).append(" ");
} else
for (int i = endX; i <= startX; i++)
sb.append(i).append("x").append(startY).append(" ");
}
if (startX > endX && startY > endY) {
int i = startX;
int j = startY;
while (i >= endX)
sb.append(i--).append("x").append(j--).append(" ");
} else if (startX > endX && startY < endY) {
int i = startX;
int j = startY;
while (i >= endX)
sb.append(i--).append("x").append(j++).append(" ");
} else if (startX < endX && startY > endY) {
int i = startX;
int j = startY;
while (i >= endX)
sb.append(i++).append("x").append(j--).append(" ");
} else if (startX < endX && startY < endY) {
int i = startX;
int j = startY;
while (i >= endX)
sb.append(i++).append("x").append(j++).append(" ");
}
return sb.toString();
}
I assume that what you are looking for is a way to find a word in a letter puzzle.
In such a case, I suggest you to store the puzzle on a 2D array and the word to find in a String. Then you need to check all the positions of the array that have the same character than the begining character of the String you are looking for (in the code I provide: findWord). Once you find a match, you need to check the rest of the characters of the string (in the code I provide: checkDirections). If the rest of the characters match then you have found the string, otherwise you need to check for the other directions or for the next appearance of the first letter of the string.
Next I provide the code:
package letterPuzzle;
import java.util.Random;
public class LetterPuzzle {
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz";
private static final int[] DIRECTIONS_X = new int[] { 0, 0, 1, -1, 1, 1, -1, -1 };
private static final int[] DIRECTIONS_Y = new int[] { 1, -1, 0, 0, 1, -1, 1, -1 };
private static int N;
private static char[][] puzzle;
private static void initializePuzzle() {
Random r = new Random();
puzzle = new char[N][N];
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
puzzle[i][j] = ALPHABET.charAt(r.nextInt(ALPHABET.length()));
}
}
// Add the JAVA word in a location
if (N < 6) {
System.out.println("[ERRRO] Example needs N >= 6");
System.exit(1);
}
puzzle[2][3] = 'j';
puzzle[3][3] = 'a';
puzzle[4][3] = 'v';
puzzle[5][3] = 'a';
}
private static void printPuzzle() {
System.out.println("[DEBUG] Puzzle");
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
System.out.print(puzzle[i][j] + " ");
}
System.out.println("");
}
System.out.println("[DEBUG] End Puzzle");
}
private static boolean findWord(String word) {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
// We check all the matrix but only try to match the word if the first letter matches
if (puzzle[i][j] == word.charAt(0)) {
if (checkDirections(i, j, word)) {
return true;
}
}
}
}
return false;
}
private static boolean checkDirections(int initX, int initY, String word) {
System.out.println("Searching " + word + " from (" + initX + ", " + initY + ")");
// Checks the different directions from (initX, initY) position
for (int dirIndex = 0; dirIndex < DIRECTIONS_X.length; ++dirIndex) {
System.out.println(" - Searching direction " + dirIndex);
boolean wordMatches = true;
// Checks all the characters in an specific direction
for (int charIndex = 0; charIndex < word.length() && wordMatches; ++charIndex) {
int x = initX + DIRECTIONS_X[dirIndex] * charIndex;
int y = initY + DIRECTIONS_Y[dirIndex] * charIndex;
System.out.println(" -- Checking position (" + x + ", " + y + ")");
if (x < 0 || y < 0 || x >= N || y >= N || puzzle[x][y] != word.charAt(charIndex)) {
System.out.println(" -- Not match");
wordMatches = false;
} else {
System.out.println(" -- Partial match");
}
}
// If the word matches we stop, otherwise we check other directions
if (wordMatches) {
return true;
}
}
return false;
}
public static void main(String[] args) {
// Check args
if (args.length != 2) {
System.err.println("[ERROR] Invalid usage");
System.err.println("[ERROR] main <puzzleSize> <wordToSearch>");
}
// Get args
N = Integer.valueOf(args[0]);
String word = args[1];
// Initialize puzzle (randomly)
initializePuzzle();
printPuzzle();
// Search word
boolean isPresent = findWord(word);
if (isPresent) {
System.out.println("Word found");
} else {
System.out.println("Word NOT found");
}
}
}
Notice that:
The puzzle matrix is randomly initialized and I hardcoded the word 'java' on the 2,3 -> 5,3 positions (this is just for the example but you should initialize the puzzle from the command line or from a file).
The ALPHABET variable is used only for the random generation.
The directions are stored on two 1D arrays to make the 8 directions programatically but you can unroll for the sake of clarity.
It is probably not best efficient code in terms of performance since you will double check lots of positions if the first character of the string appears several times. However it is still a feasible and easy solution.
If :
each word of yours is in a particular row and doesn't overflows to next row and
all words are consecutive
then you can do something like this:
#Override
public String toString()
{
StringBuilder string = new StringBuilder();
for(int c = startY; c<=endY; c++) {
string.append(startX).append("x").append(c).append(", ");
}
return string.toString();
}

moving elements in array to empty location

I want to move false elements in empty space (just like in Schelling's Model of Segregation).
This is the array ..(the elements in the array are placed randomly)
'X*''O'' ''X''O*'
'O''O'' '' ''X'
'O'' ''X''X''O*'
'X'' '' ''X''X'
'X''X''O*''X''O*'
the element at (0,0),(0,4),(2,4),(4,2),(4,4) are false because it does no have similar elements around it.(i have * next to it is easier for you to see). I want to move those false elements to blank location ' ' in the array. It can be moved to any empty location in the array.
' ''O''X''X'' '
'O''O''O'' ''X'
'O''O''X''X'' '
'X''O''O''X''X'
'X''X'' ''X'' '
This is my code.
//isArrGood is a method that checks if the element is false or true
//arr is 2D array that is being passed in the method
//store the false elements
char[] falseEle = new char[arr.length * arr[0].length];
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
if(!isArrGood(arr,i,j)){
falseEle[i] = arr[i][j];
}
}
}
//finds the blank space and replaces it with a false cell and finds a false cell and
//replace it with a blank space
for(int x=0; x<arr.length; x++){
for(int y=0; y<arr[x].length; y++) {
if (arr[x][y] == ' ') {
arr[x][y] = falseEle[x];
}
if(!isArrGood(arr,x,y){
arr[x][y] = ' ';
}
}
}
This is the what i get.
the current array(arr) being send in the method is. The false elements in this array are at
(1,0),(2,2),(3,2)
' ' 'X' 'X' 'X' 'X'
'O' ' ' 'X' 'X' ' '
' ' 'X' 'O' ' ' 'X'
'O' 'O' 'X' ' ' 'O'
'O' 'O' ' ' 'O' ' '
And this is what I get
'' 'X' 'X' 'X' 'X'
'O' 'O' 'X' 'X' 'O'
'O' 'X' 'O' 'O' 'X'
'O' 'O' 'X' 'X' 'O'
'O' 'O' '' 'O' ''
The array on the very top is just a example of what I am trying to do.
Could you post more code i.e. a Minimal, Complete, and Verifiable example of your problem?
For the problem "Schelling's Model of Segregation" you could use the code I wrote to learn from:
import java.util.Random;
public class ShellingSegregationModel
{
public static final int EMPTY = 0;
public static final int BLUE = 1;
public static final int RED = 2;
// number of elements for random
public static final int ELEMENTS = 3;
public static final double THRESHOLD = 0.15;
//size of the field
public int size;
int[][] field;
// temporary field for the iteration
int[][] temporary;
public int iteration;
public ShellingSegregationModel(int size)
{
Random random = new Random();
this.size = size;
field = new int[size][size];
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
field[y][x] = random.nextInt(ELEMENTS);
}
}
}
public int getSize()
{
return size;
}
public void setField(int[][] field)
{
this.field = field;
}
public int[][] getField()
{
return field;
}
public void setTemporary(int[][] temporary)
{
this.temporary = temporary;
}
public int[][] getTemporary()
{
return temporary;
}
public int getIteration()
{
return iteration;
}
public void setIteration(int iteration)
{
this.iteration = iteration;
}
public double getThreshold()
{
return THRESHOLD;
}
//how many neighbors needed for threshold
public double getCalculatedThreshold()
{
return getThreshold()*8;//8 is the neighbors count total possible
}
public static String getSymbolFor(int x)
{
String s = "";
switch (x)
{
case BLUE:
s = "x";
break;
case RED :
s = "o";
break;
case EMPTY:
default:
s = " ";
}
return s;
}
public boolean isEmpty(int x, int y)
{
return get(x,y) == EMPTY;
}
/**
* Prints field
*/
public void print(String message)
{
System.out.println(message);
for (int y = 0; y < getSize(); y++)
{
StringBuilder row = new StringBuilder();
for (int x = 0; x < getSize(); x++)
{
row.append("'").append(getSymbolFor(get(x,y))).append("' ");
}
System.out.println(row.toString());
}
}
public void printSameNeighorsCount(String message)
{
System.out.println(message);
for (int y = 0; y < getSize(); y++)
{
StringBuilder row = new StringBuilder();
for (int x = 0; x < getSize(); x++)
{
row.append("'").append(sameNeighbors(x, y)).append("' ");
}
System.out.println(row.toString());
}
}
public int get(int x, int y)
{
return getField()[y][x];
}
private int add(boolean c)
{
return c ? 1 : 0;
}
public int sameNeighbors(int x, int y)
{
return isEmpty(x,y) ? 0 :
add(isSame(get(x,y),x ,y-1))
+ add(isSame(get(x,y),x-1,y-1))
+ add(isSame(get(x,y),x-1,y ))
+ add(isSame(get(x,y),x-1,y+1))
+ add(isSame(get(x,y),x ,y+1))
+ add(isSame(get(x,y),x+1,y+1))
+ add(isSame(get(x,y),x+1,y ))
+ add(isSame(get(x,y),x+1,y-1));
}
private static void copyArray(int[][] src, int[][] dest)
{
for (int i = 0; i < src.length; i++)
{
dest[i] = new int[src[i].length];
System.arraycopy(src[i], 0, dest[i], 0, src[i].length);
}
}
private void duplicateToTemporary()
{
setTemporary(new int[getField().length][]);
copyArray(getField(),getTemporary());
}
//
private void assignFromTemporary()
{
setField(new int[getField().length][]);
copyArray(getTemporary(), getField());
}
public void iterate(int iterations)
{
for (int i = 0; i < iterations; i++)
{
duplicateToTemporary();
for (int y = 0; y < getSize(); y++)
{
for (int x = 0; x < getSize(); x++)
{
if (!isHappy(x,y))
{
swap(x,y);
}
}
}
assignFromTemporary();
}
setIteration(getIteration()+iterations);
}
//Swaps with empty random from temporary
public void swap(int i, int j)
{
Random random = new Random();
boolean swapped = false;
//skip a random number of empty
int skip = random.nextInt(100);
while (!swapped)
{
for (int y = 0; !swapped && y < getSize(); y++)
{
for (int x = 0; !swapped && x < getSize(); x++)
{
if (getTemporary()[y][x] == EMPTY && 0 >= --skip)
{
getTemporary()[y][x] = getTemporary()[j][i];
getTemporary()[j][i] = EMPTY ;
swapped = true;
}
}
}
}
}
public boolean isHappy(int x, int y)
{
return getCalculatedThreshold() < sameNeighbors(x, y);
}
public boolean isSame(int me, int x, int y)
{
return
//check bounds
x >= 0 && y >= 0 && x < getSize() && y < getSize()
//check element
&& get(x,y) == me;
}
public static void main(String[] args)
{
ShellingSegregationModel ssm = new ShellingSegregationModel(10);
ssm.print("Randomly generated field");
ssm.printSameNeighorsCount("Same neighbors count");
ssm.iterate(5);
ssm.print("Field after 5 iterations");
ssm.printSameNeighorsCount("Same neighbors count");
ssm.iterate(5);
ssm.print("Field after 10 iterations");
ssm.printSameNeighorsCount("Same neighbors count");
ssm.iterate(5);
ssm.print("Field after 15 iterations");
ssm.printSameNeighorsCount("Same neighbors count");
ssm.iterate(50);
ssm.print("Field after 65 iterations");
ssm.printSameNeighorsCount("Same neighbors count");
}
}

Minesweeper program java x and y on 2d array are switched

im creating a code for Minesweeper and trying to implement a GUI. But the problem is that when i run the code and play the game, the position i click on the board reveals the y,x coordinate of that location on the answer board rather than the x, y coordinate. Ive been trying to fix this but i cant see to figure it out. i think it maybe is the way i create the board, but i tried everything i could think of.
class Board{
public MsGUI gui;
private static char[][] userBoard;
private static char[][] solutionBoard;
private static int boundSize = 5;
public Board(){
userBoard = new char[][] {{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'},
{'-','-','-','-','-'}};
solutionBoard = new char[][] {{'0','2','B','2','0'},
{'0','3','B','3','0'},
{'1','3','B','3','1'},
{'B','1','3','B','2'},
{'1','1','2','B','2'}};
return;
}
private static void printBoard(char[][] board){
for (int x = 0; x < boundSize; x++){
for(int y = 0; y < boundSize; y++){
System.out.print(" " + Character.toString(board[x][y]));
}
System.out.println("");
}
System.out.println("");
}
public void flagCell(int xCoordinate, int yCoordinate){
userBoard[xCoordinate][yCoordinate] = 'F';
}
public boolean isFlagged(int xCoordinate,int yCoordinate){
if(userBoard[xCoordinate][yCoordinate] == 'F'){
return true;
}
else{
return false;
}
}
public int getHeight() {
return userBoard.length;
}
public int getWidth(){
return userBoard[0].length;
}
public char getValue(int xCoordinate, int yCoordinate) {
return userBoard[xCoordinate][yCoordinate];
}
private static boolean checkIfAlreadyMarked(int xCoordinate, int yCoordinate)
{
boolean marked = false;
if (Character.toString(userBoard[xCoordinate][yCoordinate]).equals("-") == false)
{
marked = true;
}
return marked;
}
public void revealCell(int xCoordinate, int yCoordinate){
int count = 0;
for(int i = 0;i < userBoard.length;i++){
for(int J = 0;J < userBoard[i].length;J++){
if(userBoard[i][J] != '-'){
count = count + 1;
}
}
if(count == 19){
gui.win("you won");
return;
}
}
if(solutionBoard[xCoordinate][yCoordinate] == 'B'){
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
gui.lose("You lost. Better luck next time!");
return;
}
if(solutionBoard[xCoordinate][yCoordinate] != '0'){
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
}else{
userBoard[xCoordinate][yCoordinate] = solutionBoard[xCoordinate][yCoordinate];
for(int i = 1; i > -2; i--){
if(xCoordinate-i >= solutionBoard.length || xCoordinate-i < 0)
continue;
for(int z = 1; z > -2; z--){
if(yCoordinate-z >= solutionBoard[xCoordinate].length || yCoordinate-z < 0)
continue;
else if(userBoard[xCoordinate-i][yCoordinate-z] == 'F' || userBoard[xCoordinate-i][yCoordinate-z] != '-')
continue;
else{
revealCell(xCoordinate-i, yCoordinate-z);
}
}
}
}
}
public void unflagCell(int xCoordinate, int yCoordinate){
userBoard[xCoordinate][yCoordinate]='-';
}
public static void main(String[] args){
Board b = new Board();
b.gui = new MsGUI(b);
b.gui.setVisible(true);
}
}
The way you are initializing the solutionBoard is not what you expect it to be.
If you get solutionBoard[0], you're not accessing the first column(which would be consistent with what I think is your understanding), but the first row(first item of the two-dimensional array): {'0','2','B','2','0'}
So if you want to have x for a row index and y for a column index and still keep this "readable" initialization, you'll have to swap the indices whenever you access the array.
But this will only help you with one problem - human readable array assignment in the beginning, but I think you'll regret this decision in the future.
EDIT:
You can have the array initialized as you want and still use readable format like this:
String readableBoard =
"0 2 B 2 0;" +
"0 3 B 3 0;" +
"1 3 B 3 1;" +
"B 1 B B 2;" +
"1 1 2 B 2";
char[][] board = initBoard(readableBoard);
....
private char[][] initBoard(String readableBoard){
char[][] board = new char[5][5];
String[] rows = readableBoard.split(";");
String[] fields = null;
for (int y = 0; y<rows.length;y++){
fields = rows[y].split(" ");
for (int x = 0; x<fields.length; x++){
board[x][y]=fields[x].charAt(0);
}
}
return board;
}
Now when you call
board[2][0]
You'll get 'B'
If you look at your nested for loops, you're printing off the columns instead of what I assume to be the desired rows. Try switching your for loops to iterate over y, then x.
for (int y = 0; y < boundSize; y++){
for(int x = 0; x < boundSize; x++){
System.out.print(" " + Character.toString(board[x][y]));
}
System.out.println("");
}
System.out.println("");

Bug in Minimax Algorithm for Tic Tac Toe

I am currently trying to teach myself the Minimax algorithm and I have tried to implement it in java in tic tac toe. There is a bug in my algorithm however and I can't figure out what's causing it.
Below is the complete source code (Sorry for wall of text!):
public class TicTacToe {
private static boolean gameEnded = false;
private static boolean player = true;
private static Scanner in = new Scanner(System.in);
private static Board board = new Board();
public static void main(String[] args){
System.out.println(board);
while(!gameEnded){
Position position = null;
if(player){
position = makeMove();
board = new Board(board, position, PlayerSign.Cross);
}else{
board = findBestMove(board);
}
player = !player;
System.out.println(board);
evaluateGame();
}
}
private static Board findBestMove(Board board) {
ArrayList<Position> positions = board.getFreePositions();
Board bestChild = null;
int previous = Integer.MIN_VALUE;
for(Position p : positions){
Board child = new Board(board, p, PlayerSign.Circle);
int current = max(child);
System.out.println("Child Score: " + current);
if(current > previous){
bestChild = child;
previous = current;
}
}
return bestChild;
}
public static int max(Board board){
GameState gameState = board.getGameState();
if(gameState == GameState.CircleWin)
return 1;
else if(gameState == GameState.CrossWin)
return -1;
else if(gameState == GameState.Draw)
return 0;
ArrayList<Position> positions = board.getFreePositions();
int best = Integer.MIN_VALUE;
for(Position p : positions){
Board b = new Board(board, p, PlayerSign.Cross);
int move = min(b);
if(move > best)
best = move;
}
return best;
}
public static int min(Board board){
GameState gameState = board.getGameState();
if(gameState == GameState.CircleWin)
return 1;
else if(gameState == GameState.CrossWin)
return -1;
else if(gameState == GameState.Draw)
return 0;
ArrayList<Position> positions = board.getFreePositions();
int best = Integer.MAX_VALUE;
for(Position p : positions){
Board b = new Board(board, p, PlayerSign.Circle);
int move = max(b);
if(move < best)
best = move;
}
return best;
}
public static void evaluateGame(){
GameState gameState = board.getGameState();
gameEnded = true;
switch(gameState){
case CrossWin :
System.out.println("Game Over! Cross Won!");
break;
case CircleWin :
System.out.println("Game Over! Circle Won!");
break;
case Draw :
System.out.println("Game Over! Game Drawn!");
break;
default : gameEnded = false;
break;
}
}
public static Position makeMove(){
Position position = null;
while(true){
System.out.print("Select column(x-axis). 0, 1 or 2: ");
int column = getColOrRow();
System.out.print("Select row(y-axis). 0, 1 or 2: ");
int row = getColOrRow();
position = new Position(column, row);
if(board.isMarked(position))
System.out.println("Position already marked!");
else break;
}
return position;
}
private static int getColOrRow(){
int ret = -1;
while(true){
try{
ret = Integer.parseInt(in.nextLine());
} catch (NumberFormatException e){}
if(ret < 0 | ret > 2)
System.out.print("\nIllegal input... please re-enter: ");
else break;
}
return ret;
}
}
public enum PlayerSign{
Cross, Circle
}
public enum GameState {
Incomplete, CrossWin, CircleWin, Draw
}
public final class Position {
public final int column;
public final int row;
public Position(int column, int row){
this.column = column;
this.row = row;
}
}
public class Board {
private char[][] board; //e = empty, x = cross, o = circle.
public Board(){
board = new char[3][3];
for(int y = 0; y < 3; y++)
for(int x = 0; x < 3; x++)
board[x][y] = 'e'; //Board initially empty
}
public Board(Board from, Position position, PlayerSign sign){
board = new char[3][3];
for(int y = 0; y < 3; y++)
for(int x = 0; x < 3; x++)
board[x][y] = from.board[x][y];
board[position.column][position.row] = sign==PlayerSign.Cross ? 'x':'o';
}
public ArrayList<Position> getFreePositions(){
ArrayList<Position> retArr = new ArrayList<Position>();
for(int y = 0; y < 3; y++)
for(int x = 0; x < 3; x++)
if(board[x][y] == 'e')
retArr.add(new Position(x, y));
return retArr;
}
public GameState getGameState(){
if(hasWon('x'))
return GameState.CrossWin;
else if(hasWon('o'))
return GameState.CircleWin;
else if(getFreePositions().size() == 0)
return GameState.Draw;
else return GameState.Incomplete;
}
private boolean hasWon(char sign){ //8 ways to win.
if(board[1][1] == sign){
if(board[0][0] == sign && board[2][2] == sign)
return true;
if(board[0][2] == sign && board[2][0] == sign)
return true;
if(board[1][0] == sign && board[1][2] == sign)
return true;
if(board[0][1] == sign && board[2][1] == sign)
return true;
}
if(board[0][0] == sign){
if(board[0][1] == sign && board[0][2] == sign)
return true;
if(board[1][0] == sign && board[2][0] == sign)
return true;
}
if(board[2][2] == sign){
if(board[1][2] == sign && board[0][2] == sign)
return true;
if( board[2][1] == sign && board[2][0] == sign)
return true;
}
return false;
}
public boolean isMarked(Position position){
if(board[position.column][position.row] != 'e')
return true;
return false;
}
public String toString(){
String retString = "\n";
for(int y = 0; y < 3; y++){
for(int x = 0; x < 3; x++){
if(board[x][y] == 'x' || board[x][y] == 'o')
retString += "["+board[x][y]+"]";
else
retString += "[ ]";
}
retString += "\n";
}
return retString;
}
}
Here is the output when I run the program (Computer is circle):
[ ][ ][ ]
[ ][ ][ ]
[ ][ ][ ]
Select column(x-axis). 0, 1 or 2: 1
Select row(y-axis). 0, 1 or 2: 1
[ ][ ][ ]
[ ][x][ ]
[ ][ ][ ]
Child Score: 0
Child Score: 0
Child Score: 0
Child Score: 0
Child Score: 0
Child Score: 0
Child Score: 0
Child Score: 0
[o][ ][ ]
[ ][x][ ]
[ ][ ][ ]
Select column(x-axis). 0, 1 or 2: 0
Select row(y-axis). 0, 1 or 2: 1
[o][ ][ ]
[x][x][ ]
[ ][ ][ ]
Child Score: -1
Child Score: 0
Child Score: 0
Child Score: -1
Child Score: -1
Child Score: -1
[o][ ][o]
[x][x][ ]
[ ][ ][ ]
Select column(x-axis). 0, 1 or 2:
As you can see after the first move, the computer thinks that no matter what move it makes it can get a draw (Score = 0).
On the second move I put a cross on column 0, row 1. For some reason, the computer then thinks that there are two possible moves to reach a draw (Score = 0) and only four moves to lose (Score = -1). It then makes an incorrect move thinking it will get a draw.
I first thought that there was something wrong with the hasWon method, but I have tested all 8 ways of getting three in a row and they all return true.
I suspect that the problem exists somewhere in the findBestMove, max or min methods, but I haven't been able to figure out exactly what is causing it.
I would really appreciate it if someone could tell what is causing the bug or give any suggestions on how to better debug my recursive algorithm.
Looks to me like you mixed up parts of min and max. Currently, your max returns the value of the worst possible move (for him) the human could take, instead of the optimal move the computer could take. Likewise, min returns the value of the worst move the computer could take, instead of the optimal move for the opponent.
Fix this by switching the PlayerSigns in min and max, and findBestMove should call min, not max.

Categories