Related
I have this block:
public Password2 (int numofSymbols, int numofCap, int numofLow, int numofDigit )throws Exception{
if(numofSymbols<2||numofCap<2||numofLow<2||numofDigit<2){
throw new Exception("Settings supplied not accpeted");
}
this.numofSymbols=numofSymbols;
this.numofCap=numofCap;
this.numofLow=numofLow;
this.numofDigit=numofDigit;
System.out.println("New Settings:"+"\n"+"Symbols: "+numofSymbols+"\n"+"Capitals: "+numofCap+"\n"+"Lowercase: "+numofLow+"\n"+"Digits: "+numofDigit);
}
and later I call it with:
try{
Password2 p1= new Password2();
System.out.print("Default Password:");
System.out.println(p1.getPassword());
p1.setPassword();
System.out.println(p1.getPassword());
}catch(Exception e){
System.out.println(e);
}
and it works fine but the variables are not saved when I use this method:
public ArrayList<String> Generate(){
ArrayList<String> result=new ArrayList<String>();
Random rand=new Random();
int count=0;
while(count<5) {
String placeholder="";
int a;
for(int i=0;i<=numofCap;i++){
a='Z'+1-'A';
a=rand.nextInt(a)+'A';
placeholder+=(char)a;
}
for(int i=0;i<=numofLow;i++){
a='z'+1-'a';
a=rand.nextInt(a)+'a';
placeholder+=(char)a;
}
for(int i=0;i<=numofDigit;i++){
a='9'+1-'0';
a=rand.nextInt(a)+'0';
placeholder+=(char)a;
}
for(int i=0;i<=numofSymbols;i++){
a=48+1-32;
a=rand.nextInt(a)+32;
placeholder+=(char)a;
}
if(Consecutive(placeholder)==true){
continue;
}
count++;
result.add(placeholder);
}
// return the ArrayList
//System.out.println(result);
return result;
}
Ive tried a few solutions but im just lost. Here is all of my code it is for a project and it is very confusing:
import java.util.*;
class Password1{
protected static String defaultPassword = "nDay#159";
protected static int length = 8;
protected static int numofSymbols;
protected static int numofCap;
protected static int numofLow;
protected static int numofDigit;
Password1(){
numofSymbols = 1;
numofCap = 1;
numofLow = 1;
numofDigit = 1;
}
void Password1( int numofSymbols, int numofCap, int numofLow, int numofDigit ){
Password1.numofSymbols = numofSymbols;
Password1.numofCap = numofCap;
Password1.numofLow = numofLow;
Password1.numofDigit = numofDigit;
}
public boolean validLength(String pass){
if(pass.length() >= length){
return true;
}
return false;
}
public boolean validSymbols(String pass){
int count = 0;
boolean result = false;
for(int i=0;i<pass.length();i++){
if(((int)pass.charAt(i) >= 32 && (int)pass.charAt(i) <=47) || ((int)pass.charAt(i) >= 58 && (int)pass.charAt(i) <= 64)){
count++;
}
}if(count >= numofSymbols){
result = true;
return result;
}
return result;
}
public boolean validnumofDigit(String pass){
int count = 0;
boolean result = false;
for(int i=0;i<pass.length();i++){
if(((int)pass.charAt(i) >= 48 && (int)pass.charAt(i) <=57)){
count++;
}
}if(count >= numofDigit){
result = true;
return result;
}
return result;
}
public boolean validCapitals(String pass){
int count = 0;
boolean result = false;
for(int i=0;i<pass.length();i++){
if(((int)pass.charAt(i) >= 65 && (int)pass.charAt(i) <=90)){
count++;
}
}if(count >= numofCap){
result = true;
return result;
}
return result;
}
public boolean validnumofLow(String pass){
int count = 0;
boolean result = false;
for(int i=0;i<pass.length();i++){
if(((int)pass.charAt(i) >= 97 && (int)pass.charAt(i) <=122)){
count++;
}
}
if(count >= numofLow){
result = true;
return result;
}
return result;
}
public void setPassword( ){
String newPassword ;
boolean assume = true;
Scanner s = new Scanner(System.in);
while(assume){
System.out.println("Please enter a valid password : ");
newPassword = s.nextLine();
if(!validLength(newPassword) || !validSymbols(newPassword) || !validCapitals(newPassword) || !validnumofLow(newPassword)|| !validnumofDigit(newPassword) ){
if(!validLength(newPassword)){
System.out.println("Password does not contain atleast 8 letters");
}if(!validSymbols(newPassword)){
System.out.println("Password does not contain atleast " + numofSymbols + " symbols");
}if(!validCapitals(newPassword)){
System.out.println("Password does not contain atleast " + numofCap + " Capitals");
}if(!validnumofLow(newPassword)){
System.out.println("Password does not contain atleast " + numofLow + " Lower Cases");
}if(!validnumofDigit(newPassword)){
System.out.println("Password does not contain atleast " + numofDigit + " numofDigit");
}
}else{
System.out.print("Password Accepted : ");
defaultPassword = newPassword;
assume = false;
}
s.close();
}
}
public String getPassword(){
return defaultPassword;
}
}
public class Password2 extends Password1{
//Data field to represent minimum length of a passord based on the required settings.
private int minSettings;
/** 2pts
* Default constructor that calls the superclass constructor with default
* settings (default settings at least 1 capitals, lowers,numofDigit,....
* initialize the minSettings using the method actualLength
*/
Password2(){
super();
// call the superclass default constructor
minSettings=actualLength();
// set minSettings to the actual min length of a password based on the settings
}
/** 10pts
Overloaded constructor
Make sure to prevent users from supplying settings that are
invalid settings ( for instance , negative values or 0) or
extreme cases like requiring more than 3 of (capitals,numofLow,
symbols,numofDigit) in case, an extreme case supplied,
print a message that the settings supplied is not acceptable and
apply the default setting.
*/
ArrayList<String> result;
public Password2 (int numofSymbols, int numofCap, int numofLow, int numofDigit )throws Exception{
if(numofSymbols<2||numofCap<2||numofLow<2||numofDigit<2){
throw new Exception("Settings supplied not accpeted");
}
this.numofSymbols=numofSymbols;
this.numofCap=numofCap;
this.numofLow=numofLow;
this.numofDigit=numofDigit;
System.out.println("New Settings:"+"\n"+"Symbols: "+numofSymbols+"\n"+"Capitals: "+numofCap+"\n"+"Lowercase: "+numofLow+"\n"+"Digits: "+numofDigit);
}
/** 5pts
* Create a method that checks the actual minimum length based on the
* required settings
* Use ternary operator
* if the addition of settings numbers is less than 8 , then minimum is 8
* for instance ( 1 capital, 1 small , 1 symbol, 1 number) = 4 which is less
* than the default length required, so minimum in this case 8
* if the addition of settings numbers is higher than 8 , then minimum is the
* addition of numbers.
* #return the actual minimum length of the password
*/
public int actualLength() {
int passMin=numofCap+numofLow+numofDigit+numofSymbols;
return(passMin>8)?passMin:8;
}
/** 10pts
* There are four methods that are similar to each other in Password1
* one way to improve in here is to create one method that does their actions
* take the first method that checks how many symbols and name it to checkRange
* the method should look like this:
* public boolean checkRange( String pass,int required, char start,char end)
* the method will take the range of char to be checked against the required
* number of the specific type of numofSymbols.
* This method will replace the 4 methods in Password1
* This will reduce the lines of codes significantly.
*
* #param pass
* #param required
* #param start
* #param end
* #return boolean
*/
public boolean checkRange(String pass,int required,char start,char end){
int count=0;
boolean result2=false;
for(int i=0;i<pass.length();i++){
if(pass.charAt(i)>=start &&pass.charAt(i)<=end){
count++;
}if(count>=required){
result2=true;
return result2;
}
}return result2;
}
public int getValues(){
return numofSymbols;
}
/** 10pts
* Override the setPassword method
* Use the annotation 'override'
* Print out 4 suggested password based on the settings initialized by the constructor
* Promote the user to choose from the suggested generated passwords or supply a
* password that would meet the requirements
* Prevent consecutive numofSymbols (Use the consecutive method, should be created afterward)
* use the checkRange method to check the ranges of capital, small, numofDigit, symbols
* the reset of the method is the same as superclass setPassword
*/
#Override
public void setPassword(){ // Declare a String to hold a password
ArrayList<String> pw2= new Password2().Generate();
System.out.println("Suggested Passwords:"+pw2);
String Password=defaultPassword;
// Decalre a scanner object to recive a password from the keyboard
Scanner scan= new Scanner(System.in);
// Declare a boolean variable to be set whenever a password meets the settings.
boolean flag= true;
// Loop until the user provides a correct password
while(flag){
// Promt the user to enter the password based on the settings
System.out.print("Enter Password based on set settings");
// scan the next line and store in the String holding the password
Password=scan.next();
/* If the password provided is not the actual length required ,
then print out a message and continue */
if(Password.length()<actualLength()){
System.out.println("Password length does not meet setting requrienments");
continue;
}
/* If the password provided does not have the numofCap letters required ,
then print out a message and continue
pass to checkRange the range [65 -90] or[ A -Z] */
if(!checkRange(Password, numofCap,'A','Z')){
System.out.println("Not enough numofCap letters");
continue;
}
/* If the password provided does not have the numofLow letters required ,
then print out a message and continue
pass to checkRange the range [97 -122] or [a - z] */
if(!checkRange(Password, numofLow,'a','z')){
System.out.println("Not enough numofLow letters");
continue;
}
/* If the password provided does not have the symbols required ,
then print out a message and continue
pass to checkRange the range [32 -47] or [ space - /]
( no need for this range [58-64])*/
if(!checkRange(Password, numofSymbols,' ','/')){
System.out.println("Not enough symbols included");
continue;
}
/* If the password provided does not have the numofDigit required ,
then print out a message and continue
pass to checkRange the range [48 -57] or [0 -9] */
if(!checkRange(Password, numofDigit,'0','9')){
System.out.println("Not enough numofDigit included");
continue;
}
/* If the password provided have consecutive charcters,
then print out a message and continue
pass to consecutive the password*/
if(Consecutive(Password)==true){
System.out.println("Consecutive numofSymbols are not allowed");
continue;
}
/* Password provided meets all the settings
set the global variable password to the new qualified password
set the flag to false, to stop iterations
*/
flag=false;
System.out.println("Password Accepted:");
defaultPassword=Password;
// end of while loop
}
}
/**10pts
* Create a new method that suggests few passwords based on
* the settings required , you have to use Random class to generate
* random values of ( numbers, numofSymbols, symbols , numofDigit)
*
* The method should generate based on the data fields settings
* once generated a password, check if there is no consecutive values
* Store the generated password into an arrayList
*
* #return ArrayList encapsulating 4 generated passwords
*/
public ArrayList<String> Generate(){
ArrayList<String> result=new ArrayList<String>();
Random rand=new Random();
int count=0;
while(count<5) {
String placeholder="";
int a;
// Generate random A-Z
for(int i=0;i<=numofCap;i++){
a='Z'+1-'A';
a=rand.nextInt(a)+'A';
placeholder+=(char)a;
}
// Generate from a-z
for(int i=0;i<=numofLow;i++){
a='z'+1-'a';
a=rand.nextInt(a)+'a';
placeholder+=(char)a;
}
// Generate numbers 0-9
for(int i=0;i<=numofDigit;i++){
a='9'+1-'0';
a=rand.nextInt(a)+'0';
placeholder+=(char)a;
}
// Generate symbols , 1st part 32-48
for(int i=0;i<=numofSymbols;i++){
a=48+1-32;
a=rand.nextInt(a)+32;
placeholder+=(char)a;
}
// If no consecutive , add it to the ArrayList
if(Consecutive(placeholder)==true){
continue;
}
count++;
result.add(placeholder);
}
// return the ArrayList
//System.out.println(result);
return result;
}
public boolean Consecutive(String pass){
boolean consecutive=false;
for(int i=1;i<pass.length();i++){
if(pass.charAt(i)==pass.charAt(i-1)){
consecutive=true;
}
} return consecutive;
}
/**public ArrayList getGenerate(){
* Password2 gen=new Generate();
* System.out.println("SuggestedPasswords: "+Generate());
* }
*/
public static void main(String[] args){
try{
Password2 p1= new Password2();
System.out.print("Default Password:");
System.out.println(p1.getPassword());
p1.setPassword();
System.out.println(p1.getPassword());
}catch(Exception e){
System.out.println(e);
}
try{
Password2 p2= new Password2(2,2,2,2);
p2.setPassword();
System.out.println(p2.getPassword());
}catch(Exception e){
System.out.println(e);
}
try{
Password2 p3= new Password2(3,3,3,3);
p3.setPassword();
System.out.println(p3.getPassword());
}catch(Exception e){
System.out.println(e);
}
/*2pts
Declare an instance of password to have these settings
(0 capitals, 10 symbols, -1 numofDigit,100 small letters)
this settings is an extreme and thus the user
should get a warning message and the default settings applied.
*/
try{
Password2 p4= new Password2(10, 0,-1,100);
p4.setPassword();
System.out.println(p4.getPassword());
}catch(Exception e){
System.out.println(e);
}
}
}
I have a project for Hangman game application that reads guesses from the console input and to make unit testing for it, but i don't know how to test it. They told me to make it return true or false on the last method, because my code is untestable and that will help me to test it like i play the game /to win and to lose/. But i don't know how to do it. Can someone help me to test it?
here is my code:
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static Scanner scanner = new Scanner(System.in);
private String secredWord;
private StringBuilder dashes;
private int lives;
private int guesses;
private char[] wrongGuess;
/**
* Generates a string with all letters in the searched word replaced with an
* underline and a space.
*
* #return return the secred word hidden.
*/
public StringBuilder makeDashes() {
dashes = new StringBuilder();
for (int i = 0; i < secredWord.length(); i++) {
dashes.append("_ ");
}
return dashes;
}
/**
* Takes a string as a parameter and set the sacred word to be guessed, and
* reset the lives & guesses that are made.
*
* #param secredWord
* the word that has to be guessed.
*
* #return - returns the method that make the word hidde.
*/
public StringBuilder setGame(String secredWord) {
this.secredWord = secredWord;
lives = 0;
guesses = 0;
wrongGuess = new char[6];
return makeDashes();
}
/**
* Make a check for a repeatable guesses.
*
* #param guess
* Characters to check
* #return - returns true or false. If true it make you guess another
* character.
*/
private boolean repeatGuesses(char guess) {
for (int i = 0; i < dashes.length(); i = i + 2) {
if (dashes.charAt(i) == guess) {
return true;
}
}
for (int i = 0; i < wrongGuess.length; i++) {
if (wrongGuess[i] == guess) {
return true;
}
}
return false;
}
/**
* Reads a letter and check if its been guessed before.
*/
private void guessLetter() {
char guess;
boolean present = false;
LOGGER.info("Make a guess: ");
guess = scanner.next().charAt(0);
while (repeatGuesses(guess)) {
LOGGER.info("The letter has been guessed already!");
LOGGER.info("Make another guess: ");
guess = scanner.next().charAt(0);
}
for (int i = 0; i < secredWord.length(); i++) {
if (secredWord.charAt(i) == guess) {
dashes.setCharAt(i * 2, guess);
present = true;
guesses++;
}
}
if (!present) {
LOGGER.info("Wrong guess");
wrongGuess[lives++] = guess;
}
}
/**
* While you didnt guess the word or you have more lifes left calls the
* motod guessLetter() and you have to make a guess untill you lost your
* lifes or guess the word.
*/
public void playTheGame() {
while (lives < 6 && guesses < secredWord.length()) {
LOGGER.info("Hidden word --> {}", dashes);
LOGGER.info("Lives: ({}/6 wrong letters)", lives);
guessLetter();
}
if (lives == 6) {
LOGGER.info("You lost!The word was --> {}", secredWord);
} else {
LOGGER.info("Congratulations YOU WON!!! The word was --> {}.", secredWord);
}
}
And make one test to see is the word hidden `
public void hiddenWordTest() {
HangmanGame item = new HangmanGame();
String secredWord = "hangman";
String correctHidden = "_ _ _ _ _ _ _ ";
StringBuilder resultHidden = item.setGame(secredWord);
assertEquals(correctHidden, resultHidden.toString());
}`
I can think of two options:
You can pass in LOGGER via contructor and fake it during a test
Extract computation logic into separate class and test that in isolation without any fakes.
Here is the program that I am trying to run:
/**
* Write a description of class mainGame here.
*
* #author Anthony Parsch
* #version 0.1.1
*/
//Import what I need to.
import java.io.*;
public class mainGame
{
/**
* Constructor for objects of class mainGame
*/
public static void main(String[] args)
{
// initialise instance variables
int xCoord = 10; //The map's max x coordinate +1
int yCoord = 10; //The map's max y coordinate +1
int playerX = 0; //The player's current x coordinate
int playerY = 0; //The player's current y coordinate
//Declare the arrays
String[][] map; //[x][y]The map
String[][] direc; //[x][y]Which directions that you can go
String[][] items; //[x][y]Where items are at
String[] inv; // Holds your inventory.
int[][] helpInt; //[x][y] All the other stuff in the
//Initalize the arrays
//---The player arrays
inv = new String[10]; //The player's inventory
inv[0] = "0";
inv = addItem(inv, "Blarg");//GET RID OF THIS LATER
//---The map arrays
map = new String[xCoord][yCoord]; //Descriptions
direc = new String[xCoord][yCoord]; //north y++,west x--,south y--,east x++
items = new String[xCoord][yCoord]; //The items within the world
//Declare the values of map
map[0][0] = "You wake up with the feel of cold metal on your back. The only other thing in this room is the door.";
map[0][1] = "You are now in a hallway with a door behind you and one either side. Forward is a continuation of the hallway.com";
//Declare the values of direc
direc[0][0] = "north";
direc[0][1] = "north, south, east, west";
print(inv[0]); //Check that the functions work
print(findDirec(direc, 0, 0));
}
/**
* Finds and displays the avaliable exits for a coordinate.
*
* #param map[][] The map array from which this method pulls the directions from.
* #param x The x value of the map
* #param y The y value of the map
* #return The string value of which way you can go
*/
static String findDirec(String[][] map, int x, int y){
//Pulls the directions
String match = map[x][y];
//Checks Directions
boolean nor = match.matches("(.*)north(.*)");
boolean sou = match.matches("(.*)south(.*)");
boolean wes = match.matches("(.*)west(.*)");
boolean eas = match.matches("(.*)east(.*)");
//Displays directions
String placeHolder = "You can go ";
if (nor == true){
placeHolder = placeHolder + "north, ";
} else if(sou == true) {
placeHolder = placeHolder + "south, ";
} else if(wes == true) {
placeHolder = placeHolder + "west, ";
} else if(eas == true) {
placeHolder = placeHolder + "east";
}
//---Checks if east is in the string, if not it removes the space and comma
if (eas == false){
StringBuffer soo = new StringBuffer(placeHolder);
soo.delete((placeHolder.length()-3), (placeHolder.length()-1));
placeHolder = soo.toString();
}
//Adds the ending period
placeHolder = placeHolder + ".";
//Returns the string
return placeHolder;
}
//Adds an item to an inventory
static String[] addItem(String inv[], String item){
int i; //Counter for the for loop, and where to add the item at.
boolean stop = false;
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
inv[i] = item;
return inv;
}
static void print(String entry){
System.out.print(entry);
}
}
And when I try and run it through the Command Prompt, I get this error:
Exception in thread "main" java.lang.NullPointerExcpetion
at mainGame.addItem(mainGame.java:113)
at mainGame.main(mainGame.java:38)
When I paste this in to a text editor, line 113 is simply a closing brace }.
However, one line before that is a logic flaw which I presume is really line 113 for you.
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
Each iteration of the loop assigns true to stop and then tests if true equals true, which it does. Your condition to exit the loop is when true equals false, which is never the case, therefore your loop goes forever until an error occurs. Also, don't you want to iterate while stop is false? I think you have it backwards.
The next problem is your if statement, which is probably where your NullPointerException is coming from:
if(inv[i].equals("0"))
{
stop = true;
}
You assume that inv[i] refers to an object. You need a null check.
Three recommendations:
Never use = for a comparison. Use ==. Since this is a boolean, you can even simplify this to stop.
Check the length in your for loop.
Compare "0" to inv[i] instead of the other way around to avoid null pointer dereferencing.
Try this:
boolean stop = false;
for (int i = 0; i < inv.length && !stop; i++)
{
if("0".equals(inv[i])
{
stop = true;
}
}
Another option, and this is a matter of form, is to remove the looping variable and just break out of the loop explicitly.
for (int i = 0; i < inv.length; i++)
{
if("0".equals(inv[i])
{
break;
}
}
inv = new String[10]; //The player's inventory
inv[0] = "0";
inv = addItem(inv, "Blarg");//GET RID OF THIS LATER
So you only initialize one index of your array but here:
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
.. you loop through all of them. just kidding, didn't read the full problem. You should still read the rest of my answer, but the reason why you get the NPE is because your loop condition is broken. (by the way your for loop condition is broken, it should test for equivalence using the == operator, not the assignment = operator.)
So what you actually are doing with that code is this :
inv = new String[10];
At this point you have a new String array of capacity 10, with no values inside, something like this:
[null][null][null][null][null][null][null][null][null][null]
inv[0] = "0";
Now you set [0] to "0", so:
["0"][null][null][null][null][null][null][null][null][null]
Then your loop attempts to access all of those null references, that'll probably be why you have a null reference exception.
To fix it, simply every index position in your array to anything that is not-null:
Arrays.fill(inv, "");
I use Arrays.fill() because it's shorter.
In your for loop condition, which one do you prefer?
stop = true or stop == true
for(i=0; i<inv.length && !stop; i++)
{
if(inv[i]!=null && inv[i].equals("0"))
{
stop = true;
}
}
I'm making a sudoku program, and I 'created' a recursive algorithm to solve a sudoku. The problem is, that sometimes it works perfectly and in an instant, sometimes it gets stuck and works 10s of seconds, and sometimes I just have to quit it.
Any ideas what might be causing this?
I left the code as it is, since I'm not sure how you answer the question(if you copy and try it out or just check the logic). If you want, I can just write out snippets.
Thanks!
import java.util.Random;
/**
* #Program Sudoku
*
* #date November 2013
* #hardware MacBook Pro 17", mid 2010, i7, 8GiB RAM
* #IDE eclipse SDK 4.3.1
* #purpose generates a valid 9x9 sudoku grid
*
*/
public class SudokuSolver //seems to werk !!
{
//create a validitychecker object(will be used as Sudoku.isValid();)
validityChecker Sudoku = new validityChecker();
//Create a 2D array where the full sudoku grid will be stored
private int[][] grid = new int[9][9];
//Creates a 2D array for the playable sudoku grid (with elements removed)
private int[][] playingGrid = new int[9][9];
private Random Ran = new Random();
/**
* #purpose use this construct if you wish to generate a new sudoku
* #param difficultyLevel removes amount of elements from sudoku using the equation elementToRemove=40+15*difficultyLevel
*/
SudokuSolver(int difficultyLevel)
{
//generate an empty grid
generateBaseGrid();
//populate it with a valid sudoku
solveSudoku(0,0);
//store this in a new from which elements shall be removed
for(int i = 0; i < grid.length; i++)
playingGrid[i] = grid[i].clone();
//calculate the amount of elements to be removed
int difficultyMultiplier = 15;
int baseDifficulty = 40;
int difficulty = baseDifficulty+difficultyLevel*difficultyMultiplier;
//and remove them
removeElements(difficulty);
}
/**
* #purpose use this constructor if you just want to use methods and solve
* #param the sudoku you wish to solve. values have to be within the range 1-9(inclusive), and -1 for unknown
* #note to get the solved sudoku use the fullGrid getter.
*/
SudokuSolver(int[][] pg)
{
//lets clone out the arrays - we don't want to just have references ...
for(int i = 0; i < pg.length; i++)
grid[i] = pg[i].clone();
for(int i = 0; i < grid.length; i++)
playingGrid[i] = grid[i].clone();
int coords[] = findOnes(grid);
solveSudoku(coords[0],coords[1]);
System.out.println(coords[0]+" "+coords[1]);
}
/**
* Use this if you only wish to use the internal methods
*/
SudokuSolver()
{}
//this method was implemented later, and I'm too lazy to change methods that use the procedure, but don't call the method. Maybe in next version
/**
* #purpose creates a copy of the passed array
* #param the array you wish to be copied
* #return returns a clone of the passed 2D array
*/
public int[][] cloneBoard(int[][] sudokuArray)
{
int[][] result = new int[9][9];
for(int i = 0; i < sudokuArray.length; i++)
result[i] = sudokuArray[i].clone();
return result;
}
/*
*#purpose fills the grid with -1s; This is for proper functionality during validation
*/
private void generateBaseGrid()
{
//iterates through all the values and stores -1s in it
for(int r=0;r<9;r++)
for(int c=0;c<9;c++)
grid[r][c] = -1;
//System.out.println("Base Grid Created");
}
/**
* #purpose checks if there are -1s in the grid, if so the grid is playable (its not a solution)
* #return true if its playable
*/
public boolean isGridPlayable()
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(grid[i][j]==-1)
return true;
return false;
}
/**
*
* #return the generated grid with all elements (for one without some elements use theGrid()) for generator
* #return the solved grid for solver
*/
public int[][] fullGrid()
{
return grid;
}
/**
* #purpose returns the playing grid
* #return the playable grid
*/
public int[][] theGrid()
{
return playingGrid;
}
/*
* #purpose removes "amnt" of elements from the playingGrid
* #return whether the current method was successful
* #param the amount of elements to be removed
*/
private boolean removeElements(int amnt)
{
if(amnt==0) //yay base case
return true;
for(int i=0; i<20;i++)
{
int r=Ran.nextInt(9);
int c=Ran.nextInt(9);
int element=playingGrid[r][c];
if(element!=-1)
{
playingGrid[r][c]=-1;
if(removeElements(amnt-1))
{return true;}
}else{playingGrid[r][c]=element;//removed as per suggestioni--;}
}
}
return false;
}
//--------------Debugging--------------------------------
public void printUserGrid(int[][] printie)
{
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
int x = printie[i][j];
String bexp = Integer.toString(x);
if(x==-1)
bexp="[]";
else bexp+=" ";
System.out.print(bexp+" ");
if(j==2||j==5)
System.out.print(" ");
}
System.out.println();
if(i==2||i==5)
System.out.println();
}
}
// //----------Main only for debugging-----------------------
public static void main(String[] args)
{
SudokuSolver Generator = new SudokuSolver(2);
int[][] generatedGrid = Generator.theGrid();
int[][] fullGrid = Generator.fullGrid();
Generator.printUserGrid(fullGrid);
// Generator.printUserGrid(generatedGrid);
System.out.println("\n\n");
SudokuSolver Solver = new SudokuSolver(generatedGrid);
Solver.printUserGrid(fullGrid);
}
}
EDIT:
One key thing I forgot to mention, the solveSudoku method, it rearranges some of the values. That means if I'm starting with a **3 it doesn't have a problem returning 312 (this is just an example for illustration). So I'd assume there is some serious logic error somewhere in there.
What you are attempting to solve is an Artificial Intelligence problem. Going by brute-force, or better called plain backtracking would actually mean you possibly have an exponential time complexity.
An exponential solution is expected to take long. In some cases where the order of guesswork matches the actual solution, your solver will return with a result faster.
So what you can do:
Read upon the AI technique called Constraint Satisfaction, and try to implement that.
Read upon more specific AI sudoku solving techniques, maybe some research paper if there is one and try and implement that.
This is my version of a SudokuSolver:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Sudoku
{
//This member has been intentionally left public, as the solved sudoku is
//obtained from here and user will find it much easier to handle this.
//The Sudoku input has to be given through this instance variable only.
//Moreover, many a times, Sudoku inputs as an edit-able 2D array is more
//comfortable than passing a whole 9x9 2D array as a constructor parameter.
public String SUDOKU[][]=new String[9][9];
//This variable records the nature of the Sudoku whether solvable or not
public boolean VALID=true;
public Sudoku()//this constructor can be used to create SUDOKU objects
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
SUDOKU[i][j]="";
}
private Sudoku(String SDK[][])//This is constructor kept private for program use
{
SUDOKU=SDK;
}
//This function checks if a certain digit is possible in a particular cell
private boolean isPossible(int n,int i,int j)
{
for(int a=i/3*3;a<i/3*3+3;a++)
for(int b=j/3*3;b<j/3*3+3;b++)
if(SUDOKU[a][b].length()==1 && n==Integer.parseInt(SUDOKU[a][b]))
return false;
for(int k=0;k<9;k++)
if(SUDOKU[i][k].length()==1 && n==Integer.parseInt(SUDOKU[i][k]) || SUDOKU[k][j].length()==1 && n==Integer.parseInt(SUDOKU[k][j]))
return false;
return true;
}
//The following function is compulsory as it is the only function to place appropriate
//possible digits in the cells of the Sudoku. The easiest Sudokus will get solved here.
private void fillPossibles()
{
boolean newdigit=false;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(SUDOKU[i][j].length()!=1)
{
SUDOKU[i][j]="";
int count=0;
for(int k=1;k<10;k++)
if(isPossible(k,i,j))
{
SUDOKU[i][j]+=k;
count++;
}
if(count==1)
newdigit=true;
}
if(newdigit)
fillPossibles();
}
//This following function is optional, if the Sudoku is known to be genuine. As,
//in that case it only increases the solving speed!! But if the nature is not known,
//this function becomes necessary because the nature of the Sudoku is checked here.
//It returns true if any cell is filled with a digit and false for all other cases
private boolean deepFillPossibles(int ai,int aj,int bi,int bj,boolean first)
{
if(SUDOKU!=null)
for(char c='1';c<='9';c++)
{
int count=0,digit=0,possibility=0,i=0,j=0;
boolean valid=true;
for(int a=ai;a<bi;a++)
for(int b=aj;b<bj;b++)
{
if(SUDOKU[a][b].length()==0)
valid=false;
for(int k=0;k<SUDOKU[a][b].length();k++)
if(SUDOKU[a][b].charAt(k)==c)
{
if(SUDOKU[a][b].length()>1)
{
i=a; j=b; count++;
}
if(SUDOKU[a][b].length()==1)
digit++;
possibility++;
}
}
//the following code is executed only if solution() is called first time
if(first && (digit>1 || valid && possibility==0))
{
SUDOKU=null; return false;
}
if(count==1)
{
SUDOKU[i][j]=String.valueOf(c);
fillPossibles(); return true;
}
}
return false;
}
//This function is also optional if Sudoku is genuine. It only combines the solving
//power of fillPossibles() and deepFillPossibles() to reduce memory consumption
//in the next stages. In many cases the Sudoku gets solved at this stage itself.
private void solution(boolean first)
{
fillPossibles();
for(int i=0;i<9;i++)
if(deepFillPossibles(i,0,i+1,9,first) || deepFillPossibles(0,i,9,i+1,first) ||
deepFillPossibles(i/3*3,i%3*3,i/3*3+3,i%3*3+3,first))
i=-1;
}
//This function is the most challenging. No Sudoku can ever escape solution after
//passing this stage. It uses ECHO TREE logic implementing brute force to check all
//kinds of combinations until solution is obtained. It returns a null for no solution.
private void treeSolution(Tracker track)
{
if(SUDOKU==null)
{
track.TRACK_SUDOKU=null;
return;
}
solution(false);
//For a genuine sudoku the statement could have been replaced by "fillPossibles();"
//(Only it would make solving slower and increase memory consumption!)
//But it is risky if there is a doubt regarding the genuineness of the Sudoku
int a=-1,b=-1;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(SUDOKU[i][j].length()>1 && a==-1)
{
a=i; b=j;
}
else if(SUDOKU[i][j].length()==0)
return;
if(a==-1)//checks if the Sudoku is solved or not setting necessary flags
{
track.TRACK_SOLUTION++;
for(int i=0;i<9;i++)
for(int j=0;j<9;track.TRACK_SUDOKU[i][j]=SUDOKU[i][j],j++);
return;
}
String temp[][]=new String[9][9];
for(int k=0;k<SUDOKU[a][b].length();k++)
{
for(int i=0;i<9;i++)
for(int j=0;j<9;temp[i][j]=SUDOKU[i][j],j++);
temp[a][b]=String.valueOf(SUDOKU[a][b].charAt(k));
new Sudoku(temp).treeSolution(track);
//The following condition stops the braching TREE if the Sudoku is solved by
//echoing back to the root, depending on the flags set on being solved
if(track.TRACK_SOLUTION==2 || track.TRACK_SUDOKU==null)
return;
}
return;
}
//This is the last function which has public access and can be called by the user.
//It sets the Sudoku as null if non-genuine and VALIDity as false if no unique solution
public void solve()
{
try
{
for(int i=0;i<9;SUDOKU[i][8]=SUDOKU[i][8],SUDOKU[8][i]=SUDOKU[8][i],i++);
}
catch(Exception e)
{
SUDOKU=null; VALID=false; return;
}
Tracker track=new Tracker();
solution(true);
treeSolution(track);
SUDOKU=track.TRACK_SOLUTION==0?null:track.TRACK_SUDOKU;
if(track.TRACK_SOLUTION!=1)
VALID=false;
}
}
//the following class is purposely designed to easily track the changes during solution,
//including the nature of the Sudoku and the solution of the Sudoku(if possible)
class Tracker
{
protected int TRACK_SOLUTION=0;
protected String TRACK_SUDOKU[][]=new String[9][9];
}
public class SudokuSolver extends JApplet implements KeyListener, MouseListener
{
private String SUDOKU[][]=new String[9][9];
private Sudoku SDK=new Sudoku();
private Image BOX[][]=new Image[9][9],SELECT_IMG;//BOX is an array of square images
private int SELECT_ROW=4,SELECT_COLUMN=4;//stores the position of the selection box
//this function is used to initialize the coloured square images and fonts
public void init()
{
resize(190,190);
setFont(new Font("Dialog",Font.BOLD,12));
addMouseListener(this);
addKeyListener(this);
Graphics g;
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",j++)
{
BOX[i][j]=createImage(22,22);
g=BOX[i][j].getGraphics();
if((i/3+j/3)%2==0)
g.setColor(Color.yellow);
else
g.setColor(Color.green);
g.fillRect(0,0,21,21);
g.setColor(Color.black);
g.drawRect(0,0,21,21);
}
//the following statements colour the selection box red
SELECT_IMG=createImage(22,22);
g=SELECT_IMG.getGraphics();
g.setColor(Color.red);
g.fillRect(0,0,21,21);
g.setColor(Color.black);
g.drawRect(0,0,21,21);
}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
//the following function handles the mouse click operations
public void mousePressed(MouseEvent e)
{
if(SDK.SUDOKU==null)
{
SDK.SUDOKU=new String[9][9];
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",SDK.SUDOKU[i][j]="",j++);
SDK.VALID=true;
}
if(e.getY()<190 && e.getX()<190 && e.getY()%21!=0 && e.getX()%21!=0)
{
SELECT_ROW=e.getY()/21;
SELECT_COLUMN=e.getX()/21;
}
repaint();
}
public void keyReleased(KeyEvent e){}
//this function manages the operations associated with the various keys
public void keyPressed(KeyEvent e)
{
int code=e.getKeyCode();
if(code==KeyEvent.VK_DELETE || code==KeyEvent.VK_BACK_SPACE || code==KeyEvent.VK_SPACE)
{
SUDOKU[SELECT_ROW][SELECT_COLUMN]=""; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="";
}
switch(code)
{
case KeyEvent.VK_Y : if(SDK.SUDOKU!=null) SDK.VALID=true; break;
case KeyEvent.VK_UP : SELECT_ROW=(SELECT_ROW+8)%9; break;
case KeyEvent.VK_DOWN : SELECT_ROW=(SELECT_ROW+1)%9; break;
case KeyEvent.VK_LEFT : SELECT_COLUMN=(SELECT_COLUMN+8)%9; break;
case KeyEvent.VK_RIGHT : SELECT_COLUMN=(SELECT_COLUMN+1)%9; break;
case KeyEvent.VK_ENTER : SDK.solve(); break;
case KeyEvent.VK_N : if(SDK.SUDOKU!=null){ SDK.VALID=true; code=KeyEvent.VK_ESCAPE;}
case KeyEvent.VK_ESCAPE : if(SDK.SUDOKU==null)
{
SDK.SUDOKU=new String[9][9];
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",SDK.SUDOKU[i][j]="",j++);
SDK.VALID=true;
}
else
for(int i=0;i<9;i++)
for(int j=0;j<9;SDK.SUDOKU[i][j]=SUDOKU[i][j],j++);
}
repaint();
}
//this function is for entering the numbers in the sudoku grid
public void keyTyped(KeyEvent e)
{
char code=e.getKeyChar();
if(Character.isDigit(code))
switch(code)
{
case '1': SUDOKU[SELECT_ROW][SELECT_COLUMN]="1"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="1"; break;
case '2': SUDOKU[SELECT_ROW][SELECT_COLUMN]="2"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="2"; break;
case '3': SUDOKU[SELECT_ROW][SELECT_COLUMN]="3"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="3"; break;
case '4': SUDOKU[SELECT_ROW][SELECT_COLUMN]="4"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="4"; break;
case '5': SUDOKU[SELECT_ROW][SELECT_COLUMN]="5"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="5"; break;
case '6': SUDOKU[SELECT_ROW][SELECT_COLUMN]="6"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="6"; break;
case '7': SUDOKU[SELECT_ROW][SELECT_COLUMN]="7"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="7"; break;
case '8': SUDOKU[SELECT_ROW][SELECT_COLUMN]="8"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="8"; break;
case '9': SUDOKU[SELECT_ROW][SELECT_COLUMN]="9"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="9"; break;
default : SUDOKU[SELECT_ROW][SELECT_COLUMN]=""; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="";
}
}
//the paint() function is designed to print the the sudoku grid and other messages
public void paint(Graphics g)
{
if(!SDK.VALID)
{
g.setColor(Color.white);
g.fillRect(1,1,188,188);
g.setColor(Color.black);
g.drawRect(0,0,189,189);
if(SDK.SUDOKU==null)
{
g.drawString("INVALID SUDOKU!!",45,80);
g.drawString("[PRESS ESCAPE TO RE-ENTER]",10,105);
}
else
{
g.drawString("INCOMPLETE SUDOKU!!",30,60);
g.drawString("Would you like to see a",30,90);
g.drawString("possible solution?",45,105);
g.drawString("Y / N",80,120);
}
}
else
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
{
g.drawImage(BOX[i][j],j*21,i*21,null);
g.drawString(SDK.SUDOKU[i][j],8+j*21,15+i*21);
}
g.drawImage(SELECT_IMG,SELECT_COLUMN*21,SELECT_ROW*21,null);
g.drawString(SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN],8+SELECT_COLUMN*21,15+SELECT_ROW*21);
}
}
}
I have tried to use minimum brute force wherever possible
I have to complete 4 methods but I am having difficulty understanding them. I'm trying to complete whitemove(). I know that I have to iterate through the board, but I cannot totally understand how I'm supposed to do it!
import java.util.List;
import java.util.ArrayList;
public class HexapawnBoard {
private static final int WHITE_PIECE = 1;
private static final int BLACK_PIECE = 2;
private static final int EMPTY = 0;
private static final int BOARD_SIZE = 3;
private int[][] board;
/**
* Create a board position from a string of length BOARD_SIZE*BOARD_SIZE (9).
* The first BOARD_SIZE positions in the string correspond to the black player's home
* position. The last BOARD_SIZE positions in the string correspond ot the white player's
* home position. So, the string "BBB WWW" corresponds to a starting position.
*
* #param pos the string encoding the position of the board
*/
public HexapawnBoard(String pos)
{
if(pos.length() != BOARD_SIZE * BOARD_SIZE)
throw new RuntimeException("HexapawnBoard string must be of length BOARD_SIZExBOARD_SIZE");
board = new int[BOARD_SIZE][BOARD_SIZE];
for(int row=0;row<BOARD_SIZE;row++)
{
for(int col=0;col<BOARD_SIZE;col++)
{
switch(pos.charAt(row*BOARD_SIZE+col))
{
case 'B':
case 'b':
board[row][col] = BLACK_PIECE;
break;
case 'W':
case 'w':
board[row][col] = WHITE_PIECE;
break;
case ' ':
board[row][col] = EMPTY;
break;
default:
throw new RuntimeException("Invalid Hexapawn board pattern " + pos);
}
}
}
}
/**
* A copy constructor of HexapawnBoard
*
* #param other the other instance of HexapawnBoard to copy
*/
public HexapawnBoard(HexapawnBoard other)
{
board = new int[BOARD_SIZE][BOARD_SIZE];
for(int i=0;i<BOARD_SIZE;i++)
for(int j=0;j<BOARD_SIZE;j++)
this.board[i][j] = other.board[i][j];
}
/**
* Return a string version of the board. This uses the same format as the
* constructor (above)
*
* #return a string representation of the board.
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
for(int row=0;row<BOARD_SIZE;row++)
{
for(int col=0;col<BOARD_SIZE;col++)
{
switch(board[row][col])
{
case BLACK_PIECE:
sb.append('B');
break;
case WHITE_PIECE:
sb.append('W');
break;
case EMPTY:
sb.append(' ');
break;
}
}
}
return sb.toString();
}
/**
* Determine if this board position corresponds to a winning state.
*
* #return true iff black has won.
*/
public boolean blackWins()
{
for(int col=0;col<BOARD_SIZE;col++)
if(board[BOARD_SIZE-1][col] == BLACK_PIECE)
return true;
return false;
}
/**
* Determine if this board position corresponds to a winning state
*
* #return true iff white has won
*/
public boolean whiteWins()
{
for(int col=0;col<BOARD_SIZE;col++)
if(board[0][col] == WHITE_PIECE)
return true;
return false;
}
/**
* Determine if black has a move
*
* # return truee iff black has a move
*/
public boolean blackCanMove()
{
return false;
}
/**
* Return a List of valid moves of white. Moves are represented as valid
* Hexapawn board positions. If white cannot move then the list is empty.
*
* #return A list of possible next moves for white
*/
public List<HexapawnBoard> whiteMoves()
{
return null
}
/**
* Determine if two board positions are equal
* #param other the other board position
* #return true iff they are equivalent board positions
*/
public boolean equals(HexapawnBoard other)
{
return true;
}
/**
* Determine if two board positions are reflections of each other
* #param other the other board position
* #return true iff they are equivalent board positions
*/
public boolean equalsReflection(HexapawnBoard other)
{
return true;
}
}
First of all, the code that you have so far seems to be correct.
Now, for the four methods that you have left, I can't really help you with the blackCanMove() or the whiteMoves() methods because I don't know what the rules for moving are in this hexapawn game. If you would explain the rules, I could help with that.
As far as the equals() and equalsReflection() methods go, you basically need to do what you did in the copy constructor, but instead of copying, do a conditional test. If two positions do not match up to what they are supposed to be, return false. Otherwise, once you have checked each square and they are all correct, return true.
As far as the whiteMoves() method, you need to iterate through the board and find all of the white pieces. For each white piece, there may be a few (at most 3) valid moves. Check each of those positions to see if they are possible. The piece may move diagonally if there is a black piece in that spot, or it may move forward if there is no piece at all in that spot. For each valid move, create a new HexapawnBoard representing what the board would look life following that move. At the end of the method, return a list of all of the HexapawnBoards that you created.