(Disclaimer: There are maybe 20 different versions of this question on SO, but a reading through most of them still hasn't solved my issue)
Hello all, (relatively) beginner programmer here. So I've been trying to build a Sudoku backtracker that will fill in an incomplete puzzle. It seems to works perfectly well even when 1-3 rows are completely empty (i.e. filled in with 0's), but when more boxes start emptying (specifically around the 7-8 column in the fourth row, where I stopped writing in numbers) I get a Stack Overflow Error. Here's the code:
import java.util.ArrayList;
import java.util.HashSet;
public class Sudoku
{
public static int[][] puzzle = new int[9][9];
public static int filledIn = 0;
public static ArrayList<Integer> blankBoxes = new ArrayList<Integer>();
public static int currentIndex = 0;
public static int runs = 0;
/**
* Main method.
*/
public static void main(String args[])
{
//Manual input of the numbers
int[] completedNumbers = {0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,3,4,
8,9,1,2,3,4,5,6,7,
3,4,5,6,7,8,9,1,2,
6,7,8,9,1,2,3,4,5,
9,1,2,3,4,5,6,7,8};
//Adds the numbers manually to the puzzle array
ArrayList<Integer> completeArray = new ArrayList<>();
for(Integer number : completedNumbers) {
completeArray.add(number);
}
int counter = 0;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
puzzle[i][j] = completeArray.get(counter);
counter++;
}
}
//Adds all the blank boxes to an ArrayList.
//The index is stored as 10*i + j, which can be retrieved
// via modulo and integer division.
boolean containsEmpty = false;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if(puzzle[i][j] == 0) {
blankBoxes.add(10*i + j);
containsEmpty = true;
}
}
}
filler(blankBoxes.get(currentIndex));
}
/**
* A general method for testing whether an array contains a
* duplicate, via a (relatively inefficient) sort.
* #param testArray The int[] that is being tested for duplicates
* #return True if there are NO duplicate, false if there
* are ANY duplicates.
*/
public static boolean checkDupl(int[] testArray) {
for(int i = 0; i < 8; i++) {
int num = testArray[i];
for(int j = i + 1; j < 9; j++) {
if(num == testArray[j] && num != 0) {
return false;
}
}
}
return true;
}
/**
* If the puzzle is not full, the filler will be run. The filler is my attempt at a backtracker.
* It stores every (i,j) for which puzzle[i][j] == 0. It then adds 1 to it's value. If the value
* is already somewhere else, it adds another 1. If it is 9, and that's already there, it loops to
* 0, and the index beforehand is rechecked.
*/
public static void filler(int indexOfBlank) {
//If the current index is equal to the size of blankBoxes, meaning that we
//went through every index of blankBoxes, meaning the puzzle is full and correct.
runs++;
if(currentIndex == blankBoxes.size()) {
System.out.println("The puzzle is full!" + "\n");
for(int i = 0; i < 9; i++) {
System.out.println();
for(int j = 0; j < 9; j++) {
System.out.print(puzzle[i][j]);
}
}
System.out.println("\n" + "The filler method was run " + runs + " times");
return;
}
//Assuming the puzzle isn't full, find the row/column of the blankBoxes index.
int row = blankBoxes.get(currentIndex) / 10;
int column = blankBoxes.get(currentIndex) % 10;
//Adds one to the value of that box.
puzzle[row][column] = (puzzle[row][column] + 1);
//Just used as a breakpoint for a debugger.
if(row == 4 && column == 4){
int x = 0;
}
//If the value is 10, meaning it went through all the possible values:
if(puzzle[row][column] == 10) {
//Do filler() on the previous box
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);
}
//If the number is 1-9, but there are duplicates:
else if(!(checkSingleRow(row) && checkSingleColumn(column) && checkSingleBox(row, column))) {
//Do filler() on the same box.
filler(currentIndex);
}
//If the number is 1-9, and there is no duplicate:
else {
currentIndex++;
filler(currentIndex);
}
}
/**
* Used to check if a single row has any duplicates or not. This is called by the
* filler method.
* #param row
* #return
*/
public static boolean checkSingleRow(int row) {
return checkDupl(puzzle[row]);
}
/**
* Used to check if a single column has any duplicates or not.
* filler method, as well as the checkColumns of the checker.
* #param column
* #return
*/
public static boolean checkSingleColumn(int column) {
int[] singleColumn = new int[9];
for(int i = 0; i < 9; i++) {
singleColumn[i] = puzzle[i][column];
}
return checkDupl(singleColumn);
}
public static boolean checkSingleBox(int row, int column) {
//Makes row and column be the first row and the first column of the box in which
//this specific cell appears. So, for example, the box at puzzle[3][7] will iterate
//through a box from rows 3-6 and columns 6-9 (exclusive).
row = (row / 3) * 3;
column = (column / 3) * 3;
//Iterates through the box
int[] newBox = new int[9];
int counter = 0;
for(int i = row; i < row + 3; i++) {
for(int j = row; j < row + 3; j++) {
newBox[counter] = puzzle[i][j];
counter++;
}
}
return checkDupl(newBox);
}
}
Why am I calling it a weird error? A few reasons:
The box that the error occurs on changes randomly (give or take a box).
The actual line of code that the error occurs on changes randomly (it seems to usually happen in the filler method, but that's probably just because that's the biggest one.
Different compilers have different errors in different boxes (probably related to 1)
What I assume is that I just wrote inefficient code, so though it's not an actual infinite recursion, it's bad enough to call a Stack Overflow Error. But if anyone that sees a glaring issue, I'd love to hear it. Thanks!
Your code is not backtracking. Backtracking implies return back on failure:
if(puzzle[row][column] == 10) {
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);// but every fail you go deeper
}
There are must be something like:
public boolean backtrack(int currentIndex) {
if (NoBlankBoxes())
return true;
for (int i = 1; i <= 9; ++i) {
if (NoDuplicates()) {
puzzle[row][column] = i;
++currentIndex;
if (backtrack(currentIndex) == true) {
return true;
}
puzzle[row][column] = 0;
}
}
return false;
}
Related
I have a 2D Array map[6][6] and the values are randomly generated in a for loop
This is what the output looks like (In integer values)
There are 5 different brick colors
I want to be able to check for 3-6 of the same brick color in a row (Horizontally and Vertically) when they are randomly generated, and when the user changes a brick value
I am also fairly new to Java (3 Months or so) so I would like a simple way to do this (I do not know things like objects)
Sorry for the delay John. Holiday stuff to do and guests. It's that time of year. :)
In any case. I quickly developed two run-able classes you can use to play with a method you have actually named. It's called the hasThreeInSix() even though it's not limited to any three or six that's the name we're going to give it.
Even though it's specifically designed for 2D integer Arrays it wouldn't take much effort to modify it for whatever.
The first run-able below is for testing only. It simply shows you what is going on via the output console (pane)
The second run-able is is what you may want to apply to carry out your task. Take a close look at your randomBoard() method so that you can see how we've used the hasThreeInSix() method.
Here is the code for the run-able Test Class:
import java.util.ArrayList;
import java.util.Scanner;
public class TESTThreeAndSixMethod {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
String input = "";
while (!input.equals("quit")) {
Echo("\nPress ENTER to generate random number blocks\n"
+ "for 2 dimensional array or enter 'quit' to exit:");
input = userInput.nextLine();
if (input.toLowerCase().equals("quit")) { System.exit(0); }
int[][] x = new int[6][6];
randomBoard(x);
}
userInput.close();
}
public static void randomBoard (int[][] x) {
int[][] map = new int[6][6];
Echo("");
for (int i = 0 ; i < x.length ; i++) {
String arrayString = "";
for (int j = 0 ; j < x[i].length ; j++) {
int tmp = 1 + (int) (Math.random () * 5);
map[i][j] = tmp;
arrayString+= String.valueOf(map[i][j]);
}
// Play with the maxHorizontal, maxVertical, and numberToCheckFor
// parameters. What you see here now will never allow any array
// row of elements contain 3 of any same consecutive single digit
// values either horizontally of vertically. Nowhere should there
// ever be any three of the same digits either vertically or hor-
// zontally. If you just want to deal with a specific number from
// 0 to 9 then change the -1 to that number.
String result = hasThreeInSix(map, i, 3, 3, -1);
if (!"".equals(result)) {
Echo(arrayString + " -- " + result);
//i--;
}
else { Echo(arrayString); }
}
}
/**
* Detect specified same single digit elements either or horizontally and or vertically within
* a 2D Array. This method is to be used during the creation of the 2D Array so as to eliminate
* specified same elements from being placed into the Array.<br><br>
*
* #param array (Integer 2D Array) the array being created.<br>
*
* #param currentIndexRow (Integer) The most currently added ROW index number.<br>
*
* #param maxHorizontal (Integer) The number of same digit elements to detect horizontally. If
* 0 is supplied then no horizontal check is done. 1 can not be supplied. If it is then the method
* simply returns null string which ultimately indicates that the array row is good. 1 is not
* permitted because this will just lead to a infinite loop.<br>
*
* #param maxVertical (Integer) The same number of digit elements to detect vertically. If 0 is
* supplied then no Vertical check is done. 1 can not be supplied. If it is then the method simply
* returns null string which ultimately indicates that the array row is good. 1 is not permitted
* because this will just lead to a infinite loop.<br>
*
* #param numberToCheckFor (Integer) The number to check for to see if there is a repetitive
* sequence horizontally or vertically within the Array. Any single digit number from 0 to 9
* can be suppled. If -1 is supplied then this method will check for repetitive sequences for
* ALL numbers from 0 to 9 both horizontally and vertically.<br>
*
* #return (String) If a null string ("") is returned then this indicates that the Array Row is
* good. If a vertical and or horizontal sequence is detected then a string is returned indicating
* either the row sequence value for horizontal detection or the rows and columns detected for
* vertical detection.
*/
public static String hasThreeInSix(int[][] array, int currentIndexRow, int maxHorizontal,
int maxVertical, int numberToCheckFor) {
String indexString = "";
int n = 1, check = numberToCheckFor;
if (maxHorizontal > 0) {
// Check for horizontal values...
String aStrg = "";
if (maxHorizontal < 2 || maxVertical < 2) {
Echo("\n\u001B[31mmaxHorizontal and or maxVertical Parameter Error!\n"
+ "\u001B[34mNeither of these parameters can be less than 2\n"
+ "otherwise an infinite loop will occure! Exiting Method!\u001B[39;49m");
return "";
}
for (int i = 0; i < array[currentIndexRow].length; i++) {
aStrg+= array[currentIndexRow][i];
}
if (numberToCheckFor == -1) { n = 10; }
for (int c = 0; c < n; c++) {
String valToCheckFor = "";
if (numberToCheckFor == -1) { check = c; }
for (int i = 1; i <= maxHorizontal; i++) {
valToCheckFor+= String.valueOf(check);
}
if (aStrg.contains(valToCheckFor)) { return "Horizontal " + maxHorizontal + " Detected! (" + aStrg + ")"; }
}
}
if (maxVertical > 0) {
// Check for vertical values...
ArrayList<String> listString = new ArrayList<>();
for (int i = 0 ; i < array.length ; i++) {
String aString = "";
for (int j = 0 ; j < array[i].length ; j++) {
aString+= String.valueOf(array[i][j]);
}
if (!"000000".equals(aString)) { listString.add(aString); }
}
n = 1; check = numberToCheckFor;
if (numberToCheckFor == -1) { n = 10; }
for (int c = 0; c < n; c++) {
if (numberToCheckFor == -1) { check = c; }
String itemToLocate = String.valueOf(check);
String foundInfo = "";
for (int i = 0; i < listString.get(0).length(); i++) {
int counter = 0;
for (int j = 0; j < listString.size(); j++) {
if (listString.get(j).substring(i,i+1).equals(itemToLocate)) {
if ("".equals(foundInfo)) { foundInfo = "Vertical " + maxVertical + " Detection in Column " + (i+1) + " On Rows: -> " + (j+1); }
else { foundInfo+= ", " + (j+1); }
counter++;
}
else {counter = 0; foundInfo = ""; }
if (counter == maxVertical) {
indexString = foundInfo;
break;
}
}
if (!"".equals(indexString)) { break; }
}
if (!"".equals(indexString)) { break; }
}
}
return indexString;
}
private static void Echo(Object object) {
System.out.println(object);
}
}
And here is the code for the runable class which contains the workable hasThreeInSix() method:
import java.util.ArrayList;
import java.util.Scanner;
public class ThreeAndSixMethod {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
String input = "";
while (!input.equals("quit")) {
Echo("\nPress ENTER to generate random number blocks\n"
+ "for 2 dimensional array or enter 'quit' to exit:");
input = userInput.nextLine();
if (input.toLowerCase().equals("quit")) { System.exit(0); }
int[][] x = new int[6][6];
randomBoard(x);
}
userInput.close();
}
public static void randomBoard (int[][] x) {
int[][] map = new int[6][6];
Echo("");
for (int i = 0 ; i < x.length ; i++) {
String arrayString = "";
for (int j = 0 ; j < x[i].length ; j++) {
int tmp = 1 + (int) (Math.random () * 5);
map[i][j] = tmp;
// you can remove the line below if you don't want to
// display anything in output.
arrayString+= String.valueOf(map[i][j]);
}
// Play with the maxHorizontal, maxVertical, and numberToCheckFor
// parameters. What you see here now will never allow any array
// row of elements contain 3 of any same consecutive single digit
// values either horizontally of vertically. Nowhere should there
// ever be any three of the same digits either vertically or hor-
// zontally. If you just want to deal with a specific number from
// 0 to 9 then change the -1 to that number.
String result = hasThreeInSix(map, i, 3, 3, -1);
if (!"".equals(result)) { i--; }
// You can remove the else{} line below if you
// don't want to display anything in output.
else { Echo(arrayString); }
}
}
/**
* Detect specified same single digit elements either or horizontally and or vertically within
* a 2D Array. This method is to be used during the creation of the 2D Array so as to eliminate
* specified same elements from being placed into the Array.<br><br>
*
* #param array (Integer 2D Array) the array being created.<br>
*
* #param currentIndexRow (Integer) The most currently added ROW index number.<br>
*
* #param maxHorizontal (Integer) The number of same digit elements to detect horizontally. If
* 0 is supplied then no horizontal check is done. 1 can not be supplied. If it is then the method
* simply returns null string which ultimately indicates that the array row is good. 1 is not
* permitted because this will just lead to a infinite loop.<br>
*
* #param maxVertical (Integer) The same number of digit elements to detect vertically. If 0 is
* supplied then no Vertical check is done. 1 can not be supplied. If it is then the method simply
* returns null string which ultimately indicates that the array row is good. 1 is not permitted
* because this will just lead to a infinite loop.<br>
*
* #param numberToCheckFor (Integer) The number to check for to see if there is a repetitive
* sequence horizontally or vertically within the Array. Any single digit number from 0 to 9
* can be suppled. If -1 is supplied then this method will check for repetitive sequences for
* ALL numbers from 0 to 9 both horizontally and vertically.<br>
*
* #return (String) If a null string ("") is returned then this indicates that the Array Row is
* good. If a vertical and or horizontal sequence is detected then a string is returned indicating
* either the row sequence value for horizontal detection or the rows and columns detected for
* vertical detection.
*/
public static String hasThreeInSix(int[][] array, int currentIndexRow, int maxHorizontal,
int maxVertical, int numberToCheckFor) {
String indexString = "";
int n = 1, check = numberToCheckFor;
if (maxHorizontal > 0) {
// Check for horizontal values...
String aStrg = "";
if (maxHorizontal < 2 || maxVertical < 2) {
Echo("\n\u001B[31mmaxHorizontal and or maxVertical Parameter Error!\n"
+ "\u001B[34mNeither of these parameters can be less than 2\n"
+ "otherwise an infinite loop will occure! Exiting Method!\u001B[39;49m");
return "";
}
for (int i = 0; i < array[currentIndexRow].length; i++) {
aStrg+= array[currentIndexRow][i];
}
if (numberToCheckFor == -1) { n = 10; }
for (int c = 0; c < n; c++) {
String valToCheckFor = "";
if (numberToCheckFor == -1) { check = c; }
for (int i = 1; i <= maxHorizontal; i++) {
valToCheckFor+= String.valueOf(check);
}
if (aStrg.contains(valToCheckFor)) { return "Horizontal " + maxHorizontal + " Detected! (" + aStrg + ")"; }
}
}
if (maxVertical > 0) {
// Check for vertical values...
ArrayList<String> listString = new ArrayList<>();
for (int i = 0 ; i < array.length ; i++) {
String aString = "";
for (int j = 0 ; j < array[i].length ; j++) {
aString+= String.valueOf(array[i][j]);
}
if (!"000000".equals(aString)) { listString.add(aString); }
}
n = 1; check = numberToCheckFor;
if (numberToCheckFor == -1) { n = 10; }
for (int c = 0; c < n; c++) {
if (numberToCheckFor == -1) { check = c; }
String itemToLocate = String.valueOf(check);
String foundInfo = "";
for (int i = 0; i < listString.get(0).length(); i++) {
int counter = 0;
for (int j = 0; j < listString.size(); j++) {
if (listString.get(j).substring(i,i+1).equals(itemToLocate)) {
if ("".equals(foundInfo)) { foundInfo = "Vertical " + maxVertical + " Detection in Column " + (i+1) + " On Rows: -> " + (j+1); }
else { foundInfo+= ", " + (j+1); }
counter++;
}
else {counter = 0; foundInfo = ""; }
if (counter == maxVertical) {
indexString = foundInfo;
break;
}
}
if (!"".equals(indexString)) { break; }
}
if (!"".equals(indexString)) { break; }
}
}
return indexString;
}
private static void Echo(Object object) {
System.out.println(object);
}
}
I hope this helps.
So, I have been making this Tic Tac Toe program for a while now.
It's a basic Tic Tac Toe game but the game board is scalable. The program is almost finished but one little feature is missing.
I have to make the game end if player gets five or more marks in a row when the game board is larger than 4x4.
F.E. If the game board is 9x9 the game has to end when the player or the computer gets five marks in a row.
(Mark = "O" or "X").
The game now ends when someone gets marks in a row equals to the size of the board (if 9x9 you need 9 marks in a row to win).
I have to implement a feature in the playerHasWon and I've been having a lot of trouble finding out how. I think it's a simple to implement thing but I have not found out how to do it.
Hope my explanation is easy enough to understand. Here's the code:
package tictac;
import java.util.Scanner;
import java.util.Random;
public class Tictac {
public static final int DRAW = 0; // game ends as a draw
public static final int COMPUTER = 1; // computer wins
public static final int PLAYER = 2; // player wins
public static final char PLAYER_MARK = 'X'; // The "X"
public static final char COMPUTER_MARK = 'O'; // The "O"
public static int size; // size of the board
public static String[][] board; // the board itself
public static int score = 0; // game win score
public static Scanner scan = new Scanner(System.in); // scanner
/**
* Builds the board with the integer size and user input.
*
* Displays game win message and switches play turns.
*
* #param args the command line parameters. Not used.
*/
public static void main(String[] args) {
while (true) {
System.out.println("Select board size");
System.out.print("[int]: ");
try {
size = Integer.parseInt(scan.nextLine());
} catch (Exception e) {
System.out.println("You can't do that.");
continue; // after message, give player new try
}
break;
}
int[] move = {};
board = new String[size][size];
setupBoard();
int i = 1;
loop: // creates the loop
while (true) {
if (i % 2 == 1) {
displayBoard();
move = getMove();
} else {
computerTurn();
}
switch (isGameFinished(move)) {
case PLAYER:
System.err.println("YOU WIN!");
displayBoard();
break loop;
case COMPUTER:
System.err.println("COMPUTER WINS!");
displayBoard();
break loop;
case DRAW:
System.err.println("IT'S A DRAW");
displayBoard();
break loop;
}
i++;
}
}
/**
* Checks for game finish.
*
* #param args command line parameters. Not used.
*
* #return DRAW the game ends as draw.
* #return COMPUTER the game ends as computer win.
* #return PLAYERE the game ends as player win.
*/
private static int isGameFinished(int[] move) {
if (isDraw()) {
return DRAW;
} else if (playerHasWon(board, move,
Character.toString(COMPUTER_MARK))) {
return COMPUTER;
} else if (playerHasWon(board, move,
Character.toString(PLAYER_MARK))) {
return PLAYER;
}
return -1; // can't be 0 || 1 || 2
}
/**
* Checks for win for every direction on the board.
*
* #param board the game board.
* #param move move on the board.
* #param playerMark mark on the board "X" or "O".
* #return the game is won.
*/
public static boolean playerHasWon(String[][] board, int[] move,
String playerMark) { //playermark x || o
// horizontal check
for (int i = 0; i < size; i++) {
if (board[i][0].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[i][j].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// vertical check
for (int i = 0; i < size; i++) {
if (board[0][i].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[j][i].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// diagonals check
int i;
for (i = 0; i < size; i++) {
if (!board[i][i].equals(playerMark)) {
break;
}
}
if (i == size) {
return true;
}
for (i = 0; i < size; i++) {
if (!board[i][(size - 1) - i].equals(playerMark)) {
break;
}
}
return i == size;
}
/**
* Checks for draws.
*
* #return if this game is a draw.
*/
public static boolean isDraw() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (board[i][j] == " ") {
return false;
}
}
}
return true;
}
/**
* Displays the board.
*
*
*/
public static void displayBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
System.out.printf("[%s]", board[i][j]);
}
System.out.println();
}
}
/**
* Displays the board.
*
*
*/
public static void setupBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
board[i][j] = " ";
}
}
}
/**
* Takes in user input and sends it to isValidPlay.
*
* #return null.
*/
public static int[] getMove() {
Scanner sc = new Scanner(System.in);
System.out.println("Your turn:");
while (true) {
try {
System.out.printf("ROW: [0-%d]: ", size - 1);
int x = Integer.parseInt(sc.nextLine());
System.out.printf("COL: [0-%d]: ", size - 1);
int y = Integer.parseInt(sc.nextLine());
if (isValidPlay(x, y)) {
board[x][y] = "" + PLAYER_MARK;
return new int[]{x, y};
} else { // if input is unallowed
System.out.println("You can't do that");
continue; // after message, give player new try
}
} catch (Exception e) {
System.out.println("You can't do that.");
}
return null;
}
}
/*
* Randomizes computer's turn, where it inputs the mark 'O'.
*
*
*/
public static void computerTurn() {
Random rgen = new Random(); // Random number generator
while (true) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
if (isValidPlay(x, y)) {
board[x][y] = "" + COMPUTER_MARK;
break;
}
}
}
/**
* Checks if a move is possible.
*
* #param inX x-move is out of bounds.
* #param inY y-move is out of bounds.
* #return false
*/
public static boolean isValidPlay(int inX, int inY) {
// Play is out of bounds and thus not valid.
if ((inX >= size) || (inY >= size)) {
return false;
}
// Checks if a play have already been made at the location,
// and the location is thus invalid.
return (board[inX][inY] == " ");
}
}
// End of file
Took a quick look, detected the problem and came up with a quick fix:
public static boolean checkDiagonal(String markToLook) {
// how many marks are we looking for in row?
int sizeToWin = Math.min(size, 5);
// running down and right
// don't need to iterate rows that can't be the starting point
// of a winning diagonal formation, thus can exlude some with
// row < (size - (sizeToWin - 1))
for (int row = 0; row < (size - (sizeToWin - 1)); row++) {
for (int col = 0; col < size; col++) {
int countOfMarks = 0;
// down and right
for (int i = row; i < size; i++) {
if (board[i][i] == null ? markToLook == null :
board[i][i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
countOfMarks = 0;
// down and left
for (int i = row; i < size; i++) {
if (board[i][size - 1 - i] == null ? markToLook == null :
board[i][size - 1 - i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
}
}
return false;
}
And call it from your PlayerHasWon method instead of performign the checks there. Basically we iterate each possible starting square on the board for a diagonal winning formation, and run check down+left and down+right for each of the squares.
I am in awful hurry and did not test it much, but will return in couple of hours to improve this solution. Seems to work.
Edit: My previous solution I found lacking in further tests, I've updated the above code to function as desired.
First, I think playerMark should be a char and not a String. That said, let's go for the answer. The "horizontal" case would be:
// This is the number of marks in a row required to win
// Adjust formula if necessary
final int required = size > 4 ? 5 : 3;
for (int i = 0; i < size; i++) {
int currentScore = 0;
for (j = 0; j < size; j++) {
if (board[i][j].equals(playerMark)) {
currentScore++;
if (currentScore >= required)
return true;
}
else {
currentScore = 0;
}
}
}
}
The vertical case would be analogous. The diagonal one is a bit trickier as now it would require board[i][i+k] for the main diagonal and board[i][k-i] for the secondary; and it may not be obvious which values k and i must traverse. Here's my attempt (variable required as in horizontal case):
Note: everything from here down has been completely rewritten on 2015-12-16. The previous versions didn't work and the algorithm was not explained.
After two failed attempts I decided to do my homework and actually sort things out instead of doing everything in my head thinking I can keep track of all variables. The result is this picture:
Main diagonals are painted in blue, secondary diagonals in green.
Each diagonal is identified by a value of k, with k=0 being always being the longest diagonal of each set. Values of k grow as diagonals move down, so diagonals above the longest one have negative k while diagonals below the longest one have positive k.
Things that hold for both diagonals:
Diagonal contains size-abs(k) elements. Diagonals in which size-abs(k) is less than required need not be searched. This means that, for board size size and required length required, we'll search values of k from required-size to size-required. Notice that these have the same absolute value, with the first being <=0 and the second >=0. These values are both zero only when required==size, i.e. when we need the full diagonal to claim a win, i.e. when we only need to search k=0.
For k<=0, possible values of i (row) go from 0 to size+k. Values greater than or equal to size+k cross the right edge of the board and are thus outside the board.
For k>=0, possible values of i (row) go from k to size. Values below k cross the left edge of the board and are thus outside the board.
Only for main (blue) diagonals:
Value of j (column) is k+i.
Only for secondary (green) diagonals:
Value of j (column) is size-1+k-i. In case this is not obvious, just pick the top right corner (k=0,i=0) and notice j=size-1. Then notice that adding 1 to k (keeping i constant) always moves j by 1 right (it would go out of the board if done from k=0,i=0, just think about the intersection of horizontal line i=0 with diagonal k=1), and adding 1 to i (keeping k constant) always moves j by 1 to the left.
The ressulting code would be:
// Main diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][k+i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
// Secondary diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][size-1+k-i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
At this point, the code is nearly identical in both cases, changing only the j index in board[i][j]. In fact, both loops could be merged, taking care only of keeping two currentScore variables, one for the main (blue) diagonal and the other for the secondary (green) diagonal.
I'm currrently having issues with the neighbors in my project where i have to modify two diffrent methods for a conway game of life. My TA says my code looks like it should work but the neightbor count isn't working. I'v been printing the neightbor code and it works for the first time than it just goes to 0 for the rest of the run. Anyone have an idea where I am messing up?
public static void updateLife(Boolean[][] gameCellAlive) {
int size = gameCellAlive.length;
System.out.println("size of temp--->"+size);
Boolean[][] tempCell = new Boolean [size][size];
int row = 0;
int col = 0;
for (row = 0; row<tempCell.length; row++) {
for(col=0; col<tempCell[0].length; col++) {
tempCell[row][col] = gameCellAlive[row][col];
}
}
for (int i = 0; i<tempCell.length; i++) {
for (int j = 0; j<tempCell[0].length; j++) {
int tempInt = getLifeNeighborCount(gameCellAlive, j, i);
System.out.println("neighbors---->"+tempInt);
if ((tempInt>3) || (tempInt<2)) {
tempCell[i][j] = false;
}
else if(tempInt == 3) {
tempCell[i][j] = true;
}
else if(tempInt==2) {
tempCell[i][j]=true;
}
/*else {
tempCell[row][col]=gameCellAlive[row][col];
}*/
}//2nd for loop
}//for loop
for (int x = 0; x<tempCell.length; x++) {
for(int y=0; y<tempCell[0].length; y++) {
gameCellAlive[x][y] = tempCell[x][y];
}
}
// METHOD STUB - This method needs to be implemented!
//if statemeent for requirements.
} // end method updateLife
/**
*
* #param gameBoard A 2D boolean array containing the current life status of
* each cell at each x,y coordinate on the board. true indicates that the
* cell is alive. false indicates no life in that cell.
* #param colIndex The x position of the cell in the game board whose
* neighbors are to be counted.
* #param rowIndex The y position of the cell in the game board whose
* neighbors are to be counted.
* #return the number of cells adjacent to the cell at the specified row and
* column that contain life. This value ranges between 0 (no adjacent cells
* contain life) and 8 (all adjacent cells contain life).
*
* CS1180 Note: YOU NEED TO IMPLEMENT THIS METHOD
*/
public static int getLifeNeighborCount(Boolean[][] gameBoard, int colIndex, int rowIndex) {
// METHOD STUB - THIS METHOD NEEDS TO BE IMPLEMENTED
int neighborCount = 0;
//check for alive or dead
for (int i = rowIndex-1; i<=rowIndex+1; i++) {
for (int j = colIndex-1; j<=rowIndex+1; j++) {
try {
if (gameBoard[i][j]==true && (i !=rowIndex || j!=colIndex)) {
//System.out.println("hello");
neighborCount++;
}//end if
}//end try
catch (ArrayIndexOutOfBoundsException e){
}//end catch
}//end second foor loop
}//end first foor loop
return neighborCount;
}// end method getLifeNeighborCount
You are using the wrong variable in the condition in this loop:
for (int j = colIndex-1; j<=rowIndex+1; j++) {
It should be:
for (int j = colIndex-1; j<=colIndex+1; j++) {
My issue is the when boolean is false it still returns i and places it in the array lotto. How do I fix it so that when boolean is false it will drop i and run a another random number for that element.
package LottoNumbers;
import java.util.Arrays;
public class LottoNumbers {
//check for duplicates in each array
public static boolean isFound(int[] lotto, int number) {
for (int i = 0; i < lotto.length; i++) {
if (lotto[i] == number ) {
return true;
}
}
return false;
//DO SOMETHING IF FALSE THAT WILL GET RID OF THE NUMBER
}
public static void main(String[] args) {
//specify length of array
int[] lotto = new int[6];
//determine how many arrays
for (int Set = 1; Set <= 5; Set++) {
//assign random numbers to each array element
for (int i = 0; i < lotto.length; i++) {
int number= 0;
isFound(lotto, number = (int) (Math.random() * 50));
lotto[i] = number;
}
Arrays.sort(lotto);
//sort elements in array
//Sort arrays to specified Set numbers
if (Set == 1) {
System.out.printf("LOTTO Numbers for set 1 --> ");
} else if (Set == 2) {
System.out.print("LOTTO Numbers for set 2 --> ");
} else if (Set == 3) {
System.out.print("LOTTO Numbers for set 3 --> ");
} else if (Set == 4) {
System.out.print("LOTTO Numbers for set 4 --> ");
} else if (Set == 5) {
System.out.print("LOTTO Numbers for set 5 --> ");
}
System.out.printf(Arrays.toString(lotto).replace("[", "").replace(",", "").replace("]", "") + "\n");
}
}
}
I'm not entirely sure what you're trying to do - you don't actually do anything at all with the boolean value returned by isFound. But, if you're asking whether you can only assign lotto[i] if isFound evaluates to true, one way you could do that is to change the for loop as follows:
for (int i = 0; i < lotto.length; i++) {
int number;
Boolean wasFound = isFound(lotto, number = (int) (Math.random() * 50));
if (!wasFound)
{
lotto[i] = number;
}
else
{
// perform the previous iteration again
i--;
}
}
Your design is not terrifically efficient or well-designed - you're counting on a random number you generate not having been seen before, which gets significantly worse performance-wise the more numbers you've already created. A better way you could do this would be to just take a single element of a pre-existing collection of non-repeating integers in the range you want:
// define numbersArrayList as { 1, 2, 3, 4, ... n }
ArrayList<int> yourLottoNumbers = new ArrayList<int>()
for (int i = 0; i < yourLimit; i++)
{
int randomIndex = (int)(Math.random() * numbersArrayList.length()) - 1;
int newNumber = numbersArrayList[randomIndex];
yourLottoNumbers.add(newNumber);
yourLottoNumbers.remove(randomIndex);
}
This code executes in an amount of time directly proportional to the number of elements you need to get, rather than in a totally random amount of time, and you don't need to fudge around with repeating for loops.
You don't want your function to loop if it's false, because then the function would never end.
public static boolean IsFound(int number)
{
//You don't need to pass the array, because it's a class level variable
for (int i = 0; i < lotto.length; i++)
{
if (lotto[i] == number)
return true;
return false;
}
}
Once you've done that, you can simply do something like this to add to your array:
for (int i = 0; i < lotto.length; i++)
{
int randomNumber = (Math.random() * 50);
while(IsFound(randomNumber))
{
randomNumber = (Math.random() * 50);
}
lotto[i] = randomNumber;
}
I am doing an algorithm for a hill climbing search, and for some reason, the stack that I'm supposed to have at the end of the loop seems to be overwritten with the last iteration of the state that the loop generated.
Basically, here is a rundown of what this algorithm is doing:
This algorithm is being used to solve the N queens problem. All of the underlying code with the state class works perfectly fine. With this algorithm, it iterates through all possible successor states of the current state. It stores the next successor state within the neighborState variable (as seen in the code below). If a state cost is less than the current cost, it will add the neighborState with that new low cost into a neighborNode and store that into a stack. Any new min values that get detected will wipe the stack and insert the new lowest minimum nodes.
I've done a few tests within the loop to see what the outputs look like from what is being inserted into the stack. All of them seem to be correctly outputting. However, when I am outside the loop and check the stack, all the nodes in the stack have their states replaced to the last generated successor state from the loop. It seems that in every node that has the neighborState stored, each time the neighborState updates, it changes all the node neighborState values as well. I just can't seem to find a way to fix this though.
Some advice as to how I can fix this would be greatly appreciated!
*Note: Disregard the code after the for loop starting at the if statement, as it is still incomplete.
Here is the code:
import java.util.Random;
import java.util.Stack;
public class HillClimber {
private LocalSearchNode _current;
private int _shoulderSearchStepsAllowed;
// may need more instance variables
public HillClimber(LocalSearchNode initial, int searchShoulder) {
_current = initial;
_shoulderSearchStepsAllowed = searchShoulder;
}
public LocalSearchNode findSolution() {
LocalSearchNode neighborNode = null;
//Stack <LocalSearchNode> nodeStack;
State currentState = null;
//State neighborState = null;
Double val = 0.0;
boolean start = true;
while (true) {
currentState = _current.getState();
Stack<LocalSearchNode> nodeStack = new Stack<LocalSearchNode>();
// finds the highest valued successor of current
for (String s : currentState.actions()) {
State neighborState = currentState.successor(s);
Double cost = neighborState.estimatedDistance(neighborState);
// execute this for the first successor found
if (start) {
val = cost;
System.out.println("Started with " + val);
neighborNode = new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
start = false;
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
// resets node array if new min found and adds it to the array
else if (cost < val) {
System.out.println("Reset " + val + " with " + cost);
val = cost;
nodeStack = new Stack<LocalSearchNode>();
neighborNode= new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
// if cost is the same as current min val, add it to the array
else if (cost.equals(val)) {
val = cost;
System.out.println("Added " + val);
neighborNode = new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
}
System.out.println("Final min " + val);
System.out.println(nodeStack.size());
((QState) nodeStack.elementAt(0).getState()).test();
((QState) nodeStack.elementAt(1).getState()).test();
// returns current state if no better state found
if (_current.getValue() < val) {
// System.out.println(val);
// ((QState) _current.getState()).test();
return _current;
} else {
if (nodeStack.size() > 1) {
Random generator = new Random();
int i = generator.nextInt(nodeStack.size());
_current = nodeStack.elementAt(i);
} else {
_current = nodeStack.firstElement();
}
start = true;
}
}
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class QState implements State {
private List<String> _list;
private int[][] _state;
private int[] _qlist;
/**
* Constructor takes in the board and row index value corresponding to the
* queens at their respective column index
*
* #param state
* #param queens
*/
public QState(int[][] state, int[] queens) {
_state = state;
_qlist = queens;
_list = new ArrayList<String>();
// generates a list of all possible actions for this state
for (int i = 0; i < _qlist.length; i++) {
for (int j = 0; j < _qlist.length; j++) {
if (_state[i][j] != -1) {
_list.add("Move queen " + j + " to row " + i);
}
}
}
}
/**
* Returns a list of N * (N - 1) actions
*/
public List<String> actions() {
return _list;
}
/**
* Returns the matrix board configuration of this state
*
* #return
*/
public int[][] getMatrix() {
return _state;
}
/**
* Returns the array of queens row index for the board configuration
*
* #return
*/
public int[] getQList() {
return _qlist;
}
/**
* Parses the action and moves the queen to the new board location
*/
public State successor(String action) {
State temp = null;
int[][] newState = _state;
int[] newQList = _qlist;
String[] vals = action.split("\\s");
int i = Integer.parseInt(vals[5]); // parses the row index
int j = Integer.parseInt(vals[2]); // parses the column index
newState[_qlist[j]][j] = 0; // clears the old queen
newState[i][j] = -1; // sets the new queen
newQList[j] = i; // adds the new queen to the list
temp = new QState(newState, newQList);
return temp;
};
/**
* Returns the default step cost of 1.0
*/
public Double stepCost(String action) {
return 1.0;
}
// overrides the built-in Java equals method
#Override
public boolean equals(Object s) {
if (s == null) {
return false;
}
if (this.getClass() != s.getClass()) {
return false;
}
if (!Arrays.equals(this.getMatrix(), ((QState) s).getMatrix())) {
return false;
}
return true;
}
/**
* Returns the queen conflicts for the particular board
*/
public Double estimatedDistance(State s) {
double conflicts = 0.0;
int col = 0;
int row = 0;
for (int j = 0; j < _qlist.length; j++) {
row = _qlist[j] - 1;
col = j + 1;
// checks the upper right diagonal for queen conflicts
while (row >= 0 && col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
row--;
col++;
}
row = _qlist[j] + 1;
col = j + 1;
// checks the lower right diagonal for queen conflicts
while (row < _qlist.length && col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
row++;
col++;
}
row = _qlist[j];
col = j + 1;
// checks the sideways right for queen conflicts
while (col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
col++;
}
}
// test();
return conflicts;
}
public void test() {
for (int i = 0; i < _qlist.length; i++) {
for (int j = 0; j < _qlist.length; j++) {
if (_state[i][j] == -1) {
System.out.print("Q");
} else {
System.out.print("-");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
If you look at successor, this looks suspicious to me:
int[][] newState = _state;
int[] newQList = _qlist;
Here, it looks like you're sharing these arrays between objects. Without knowing much about what the program is doing, this kind of thing is typically the cause of the "shared update" behavior you've observed.
So updating the array from the returned successor will also change the state of the object that returned it (and so on).
There are a couple of easy ways to copy an array, namely System#arraycopy, Arrays#copyOf and clone. (All arrays are cloneable.) For the 2D array you might want to make a helper method since you'd probably need to make a deep copy. Something like:
static int[][] copyState(int[][] toCopy) {
int[][] copy = new int[toCopy.length][];
for(int i = 0; i < copy.length; i++) {
// or = Arrays.copyOf(toCopy[i], toCopy[i].length);
copy[i] = toCopy[i].clone();
}
return copy;
}
I didn't spend a whole lot of time really parsing the code--there's a lot to go through, sorry--but I don't see you making a copies anywhere, just mutating them, so I'd put my bet on this.