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.
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);
}
}
}
Earlier i asked a question specifically for the animate method in my word collider program. The purpose of this program is that when two words collide the words will explode and each individual character will be scattered randomly on the screen. Below is the code for my program;
/**
* Write a description of class WordCollider here.
*
* #author
* #version
*/
public class WordCollider
{
// instance variables - replace the example below with your own
private Text word1;
private Text word2;
// the characters contained in word1
private Text[] charWord1;
// the characters contained in word2
private Text[] charWord2;
/**
* Constructor for objects of class WordCollider
*/
public WordCollider(String w1, String w2)
{
// initialise instance variables
word1 = new Text(w1);
word1.randomizePosition();
word1.changeColor("green");
word1.changeSize(48);
word1.makeVisible();
word2 = new Text(w2);
word2.randomizePosition();
word2.changeColor("orange");
word2.changeSize(48);
word2.makeVisible();
charWord1 = new Text[w1.length()];
charWord2 = new Text[w2.length()];
fillChars(charWord1, w1);
fillChars(charWord2, w2);
}
private void fillChars(Text[] a, String w) {
char[] cs = w.toCharArray();
for (int i=0; i<a.length; i++) {
a[i] = new Text(""+cs[i]);
a[i].changeSize(48);
a[i].changeColor("red");
}
}
/**
* Randomize the position of the two words repeatedly and stop
* when the bounding box of the two words overlaps.
*/
public void animate()
{
while(checkOverlap() == false){
word1.randomizePosition();
word2.randomizePosition();
}
while(checkOverlap() == true){
word1.makeInvisible();
word2.makeInvisible();
}
for(int i = 0; i < charWord1.length; i++){
word1.makeVisible();
word1.randomizePosition();
}
for(int i = 0; i < charWord2.length; i++){
word2.makeVisible();
word2.randomizePosition();
}
}
/**
* erase the words and any other characters on the display
*/
public void clearDisplay() {
word1.makeInvisible();
word2.makeInvisible();
}
/**
* check if the bounding box of the two words overlaps.
* #return true when the words overlap and false otherwise.
*/
private Boolean checkOverlap() {
if (word2.getXPosition() < word1.getXPosition() + word1.getTextWidth()){
return true;
}
if (word1.getXPosition() > word2.getXPosition() + word2.getTextWidth()){
return true;
}
if (word2.getYPosition() < word1.getYPosition() - word1.getTextHeight()){
return true;
}
if (word1.getYPosition() > word2.getYPosition() - word2.getTextHeight()){
return true;
}
if (word2.getXPosition() < word1.getXPosition() + word1.getTextWidth() && word1.getXPosition() > word2.getXPosition() + word2.getTextWidth()){
return true;
}
if (word2.getYPosition() < word1.getYPosition() - word1.getTextHeight() && word1.getYPosition() > word2.getYPosition() - word2.getTextHeight()){
return true;
}
return false;
}
}
The purpose of the animate method is if checkOverlap is false the word will be randomized on the screen whereas if checkOverlap is true then i want to make the word invisible and scatter the letters on the screen
The purpose of the clear display method is just to make the words invisible on screen.
The purpose of the checkOverlap method is to check whether or not the two words overlap, if they do overlap then the return statement will be true and if they don't overlap then it will return false
How can i go round fixing this so my program works?
The assignment consists in decompress a string. In particular, the code has to work for 3 samples as illustrated in the picture.
My code here works in the first 2 of the samples. However, I am not able to come up with the 3rd sample. Probably I did not understand probably the concept of recursion. Can you help me?
import java.util.Scanner;
public class Compression4 {
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
String input=in.next();
System.out.println(uncompress(input));
}
public static boolean flag = true;
public static String uncompress(String compressedText)
{
return uncompress(compressedText, "", "");
}
public static String getMultiple(String x, int N) {
if (N == 0) return "";
return ""+x+getMultiple(x,N-1);
}
public static String uncompress(String text, String count, String output)
{
if (text.equals(""))
{
return output;
}
if(text.charAt(0) == '(')
{
int FirstIndex = text.indexOf("(")+1;
String inner = text.substring(FirstIndex, text.lastIndexOf(")"));
//System.out.println(inner);
flag = false;
return uncompress (inner, count, output);
}
else if (Character.isLetter(text.charAt(0)))
{
//letter case - need to take the count we have accrued, parse it into an integer and add to output
if (flag==true)
{
//System.out.println(count);// * text.charAt(0);
String s = String.valueOf(text.charAt(0));
output += getMultiple(s,Integer.parseInt(count));
count ="1";
}
else
{
//System.out.println(count);// * text.charAt(0);
output += getMultiple(text,Integer.parseInt(count));
//System.out.println("output: "+output);
count="0";
}
}
else if(Character.isDigit(text.charAt(0)))
{
//digit case - need to add to the count but keep as a string because must be parsed later
if(flag)
count += (""+text.charAt(0));
else
{
count = "0";
count += (""+text.charAt(0));
}
}
//parse the *remainder* of the string, one character at a time, so pass in the substring(1)
return uncompress(text.substring(1), count, output);
}
}
Sorry for the long code but it's more easy to explain with code than with words.
Premise:
I think to the problem as an interpreter of a language to render a string
the language is simple and functional so recursive interpretation is possible
Algorithm phases:
First: tokenize the expression (to work at an higher level of abstraction)
Second: parse the expression just tokenized
Recursion: the logic is based on the syntax of the language. Key concepts of a recursion:
the base cases and the recursive cases
the state necessary to a single recursion (local variables of recursion, those passed as parameters to the recursive method)
the state for the all recursion (global variables of recursion, those read/write in some specific recursion)
I've made many comments to explain what the algorithm is doing. If it's not clear I can explain it better.
import java.util.ArrayList;
import java.util.List;
public class TestStringDecompression {
// simpleExpr examples: a | b | 123a | 123b | 123(a) | 123(ab) | 123(ba) | (ab) | (ba)
// 11ab = aaaaaaaaaaab = = expression = simpleExpr simpleExpr = 11a b
// 4(ab) = abababab = expression = simpleExpr = 4(ab)
// 2(3b3(ab)) = bbbabababbbbababab = expression = compositeExpr = 2 ( simpleExpr simpleExpr ) = 2 ( 3b 3(ab) )
public static void main(String[] args) {
System.out.println(new StringInflater().inflate("11ab"));
System.out.println(new StringInflater().inflate("4(ab)"));
System.out.println(new StringInflater().inflate("2(3b3(ab))"));
}
public static class StringInflater {
// This store the position of the last parsed token
private int posLastParsedToken = 0;
public String inflate(String expression) {
return parse(tokenize(expression), 0, false);
}
/**
* Language tokens:
* <ul>
* <li>literals:
* <ul>
* <li>intLiteral = [0-9]*</li>
* <li>charLiteral = [ab]</li>
* </ul>
* </li>
* <li>separators:
* <ul>
* <li>leftParen = '('</li>
* <li>rightParen = ')'</li>
* </ul>
* </li>
* </ul>
*/
private Object[] tokenize(String expression) {
List<Object> tokens = new ArrayList<Object>();
int i = 0;
while (i < expression.length()) {
if ('0' <= expression.charAt(i) && expression.charAt(i) <= '9') {
String number = "";
while ('0' <= expression.charAt(i) && expression.charAt(i) <= '9' && i < expression.length()) {
number += expression.charAt(i++);
}
tokens.add(Integer.valueOf(number));
} else {
tokens.add(expression.charAt(i++));
}
}
return tokens.toArray(new Object[tokens.size()]);
}
/**
* Language syntax:
* <ul>
* <li>simpleExpr = [intLiteral] charLiteral | [intLiteral] leftParen charLiteral+ rightParen</li>
* <li>compositeExpr = [intLiteral] leftParen (simpleExpr | compositeExpr)+ rightParen</li>
* <li>expression = (simpleExpr | compositeExpr)+</li>
* </ul>
*/
private String parse(Object[] tokens, int pos, boolean nested) {
posLastParsedToken = pos;
String result = "";
if (tokens[pos] instanceof Integer) {
/** it's a intLiteral */
// get quantifier value
int repetition = (int) tokens[pos];
// lookahead for (
if (tokens[pos + 1].equals("(")) {
// composite repetition, it could be:
// simpleExpr: "[intLiteral] leftParen charLiteral+ rightParen"
// compositeExpr: "[intLiteral] leftParen (simpleExpr | compositeExpr)+ rightParen"
result = parse(tokens, pos + 1, true);
} else {
// simple repetition, it could be:
// simpleExpr: [intLiteral] charLiteral
result = parse(tokens, pos + 1, false);
}
result = repeat(result, repetition);
// evaluate the rest of the expression because syntax allows it
if (posLastParsedToken + 1 == tokens.length) {
// end of the expression
return result;
} else {
// there are other simpleExpr or compositeExpr to parse
return result + parse(tokens, posLastParsedToken + 1, false);
}
} else if (tokens[pos].equals('(')) {
/** it's a leftParen */
// an open paren means what follow this token is considered nested (useful for string to treat as char sequence)
return parse(tokens, pos + 1, true);
} else if (tokens[pos].equals(')')) {
/** it's a rightParen */
// a closed paren, nothing to render
return "";
} else {
/** it's a charLiteral */
if (nested) {
// it's nested between paren, so more parsing is requested to consume next charLiteral or next simpleExpr or compositeExpr
return tokens[pos] + parse(tokens, pos + 1, nested);
} else {
// it's not nested between paren, return charLiteral as is
return "" + tokens[pos];
}
}
}
private String repeat(String s, int repetition) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < repetition; i++) {
result.append(s);
}
return result.toString();
}
}
}
I have downloaded a java file needed for a coursework at college. However I find it impossible to run it. Eclipse won't give me the chance to even run it (only ant build), and if I use netbeans I get this exception :
Exception in thread "main"
java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Uncompilable source code - class Hangman is public, should be declared in a file named Hangman.java
at Hangman. < clinit > (hangman(Case Conflict).java: 20)
Java Result: 1
If someone is kind enough to read through the code, I really do not know what to do next. I figure there has to be something wrong with the main class. Thanks!
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
class Hangman {
Scanner userInput;
private Set < Character > wrongGuesses;
private String[] answers = {
"leverets", "hatchlings", "puppies",
"kittens", "pullets", "goslings"
};
private String answer;
private String guessed;
private int maxTurns;
private int currentTurns;
private boolean inProgress;
private char nextGuess;
private boolean gameWin;
public Hangman() {
userInput = new Scanner(System.in);
wrongGuesses = new HashSet < Character > ();
inProgress = false;
gameWin = false;
maxTurns = 14;
currentTurns = 0;
// set answer somehow
answer = answers[0];
// set guessed to the correct number of dashes
StringBuilder sb = new StringBuilder();
for (int i = 0; i < answer.length(); i++) {
sb.append('-');
}
guessed = sb.toString();
}
/* start a new game */
public void startGame() {
inProgress = true;
startGameLoop();
}
/* the game loop. this method is the heart of the game */
private void startGameLoop() {
printInstructions();
while (inProgress) {
printStatus();
acceptGuess();
checkStatus();
}
printWinOrLose();
}
private void printInstructions() {
System.out
.println("Guess the word one letter at a time until you win or run out of turns. Good luck!");
}
private void printWinOrLose() {
if (gameWin) {
System.out.println("You win! The answer was " + answer);
} else {
System.out.println("You lose.");
}
}
private void printStatus() {
System.out.println("Guesses left: " + (maxTurns - currentTurns));
System.out.println("Current status: " + guessed);
System.out.println("Wrong guesses: " + getWrongAnswers());
}
/* get the next character from the player */
private void acceptGuess() {
System.out.println("Next guess: ");
String temp = userInput.next();
nextGuess = temp.charAt(0);
}
/* check what state the game is in */
private void checkStatus() {
// if already guessed, say already guessed.
if (wrongGuesses.contains(nextGuess)) {
System.out.println("You already guessed that!");
return;
}
// if guess is not in answer, update number of turns played and add
// guess to wrong guesses
// otherwise update the guessed variable
if (answer.indexOf(nextGuess) < 0) {
++currentTurns;
wrongGuesses.add(nextGuess);
} else {
updateGuessStatus();
}
// check to see if the player has won or lost
if (answer.equals(guessed)) {
gameWin = true;
inProgress = false;
}
if (currentTurns == maxTurns) {
inProgress = false;
}
}
/* update the guessed variable when there is a correct guess made */
private void updateGuessStatus() {
// replace - with nextGuess where appropriate
int index = answer.indexOf(nextGuess);
int lastIndex = answer.lastIndexOf(nextGuess);
StringBuilder sb = new StringBuilder(guessed);
if (index != lastIndex) { // more than one instance of the guess in the
// answer
// swap out in a loop
while (index != -1) {
sb.setCharAt(index, nextGuess);
int i = answer.indexOf(nextGuess, (index + 1));
index = i;
}
} else { // letter only appears once
// swap out just that one
sb.setCharAt(index, nextGuess);
}
guessed = sb.toString();
}
/* build a text representation of all the incorrect guesses */
private String getWrongAnswers() {
if (wrongGuesses.size() > 0) {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (Character c: wrongGuesses) {
sb.append(c + ",");
}
sb.deleteCharAt(sb.length() - 1); // delete trailing comma
sb.append(')');
return sb.toString();
} else {
return "<none>";
}
}
public static void main(String[] args) {
Hangman h = new Hangman();
h.startGame();
}
}
The exception says everything you need to know. Rename the class FILE to Hangman.java.
Caused by: java.lang.RuntimeException: Uncompilable source code - class Hangman is public, should be declared in a file named Hangman.java
You should save your downloaded file in Hangman.java and not hangman.java (see it needs 'H' in caps same as your class name).
Change the class to public class Hangman. It allows outside methods to access it.
EDIT: I downloaded the file, changing the class to public worked. I also found an issue in the code itself, the word is always "Leverets".
To change this, edit the getAnswer() method and change it to
private int getAnswer() {
int i = (int) (Math.random() * 6) + 0;
return i;
}
I've posted my program for review on code review (stackexchange).
Everything worked fine, After I came home I was told to use a IDE.
I opened my source with Eclipse IDE, and then I started getting (both on the IDE, or without) this error upon run:
Exception in thread "main" java.lang.NullPointerException
at games.Spin.rand(Spin.java:68)
at games.Spin.<init>(Spin.java:10)
at games.GameHandler.<init>(GameHandler.java:8)
at Mains.startGame(Mains.java:16)
at Mains.main(Mains.java:9)
Why is it doing that? My mate has reviewed my code, and could not find anything wrong with it?.
I am very new to java, tried at attempting going deeper in OO.
My code is located at code review thread (3 classes):
https://codereview.stackexchange.com/questions/28197/improving-my-java-object-oriented-review
What is wrong with it? Why is it giving me that exception?
Line 68: return r.nextInt(x);
public int rand(int x) {
return r.nextInt(x);
}
That's how I create r object:
/**
* Creating new Random object.
**/
private Random r = new Random();
Mains.java:
import games.GameHandler;
import java.util.Scanner;
import java.io.*;
public class Mains {
public static void main (String[] args) {
//Start the game
startGame();
}
private static void startGame() {
//Declares
GameHandler handler = new GameHandler();
Scanner console = new Scanner(System.in);
boolean game = true;
String input = "";
//Print program welcome text
handler.printStart();
//While in game...
while (game) {
//Getting input ready for new commands from the player
input = console.nextLine();
//Checking if input was set.
if (input != null) {
//Selecting the game you want to play.
handler.selectGame(input);
//If game was selected.. then.. let's start playing.
while (handler.inGame) {
//Use will say something.
input = console.nextLine();
//If it was "exit", it will go back and select another game.
if (input.equals("exit")) {
handler.exitGame();
} else {
//Play again.
handler.continueGame(input);
}
}
}
}
}
}
GameHandler.java:
package games;
import java.io.*;
public class GameHandler {
private String[] games = {"Spin", "Tof"};
private String[] navigation = {"Back", "Start"};
private Spin spin = new Spin();
private boolean spinGame = false;
private boolean tofGame = false;
public boolean inGame = false;
/**
* Method printStart
*
* Will welcome the player to the program.
*/
public void printStart() {
this.print(0, "Welcome to the program!");
this.print(0, "Please select a game: " + this.availableGames());
}
/**
* Method available games
*
* This will print all the games that are located in the games array in one row.
**/
private String availableGames() {
String names = "";
for (int i = 0; i < games.length; i++) {
names = (names + games[i]);
if (i < games.length -1) {
names = (names + ", ");
}
}
return names;
}
/**
* Method selectGame
*
* This will select the given game.
* #param command The entered command.
**/
public void selectGame(String command) {
if (this.inArray(command))
{
if (command.equalsIgnoreCase("spin")) {
this.startGame("spin");
} else if (command.equalsIgnoreCase("tof")) {
this.startGame("tof");
}
} else {
this.print(0, "Could not find game!");
}
}
/**
* Method inArray
*
* This will check if the entered game name is exisiting in the games array.
* If yes, will return a boolean true, else false.
*
* #param value The entered game name.
* #return boolean true/false.
**/
private boolean inArray(String value) {
int returning = 0;
for (String s : games) {
if (value.equalsIgnoreCase(s)) {
returning = 1;
}
}
if (returning == 1) {
return true;
} else {
return false;
}
}
/**
* Method startGame
*
* Will start the game, and print instructions.
* will set the game boolean to true.
**/
private void startGame(String game) {
switch (game) {
case "spin":
this.print(0, "Welcome to spin game!");
this.print(0, "Please click on any key to spin!");
spinGame = true;
break;
case "tof":
break;
}
inGame = true;
}
/**
* Method continueGame
*
* Will continue the game, either spin again, or print new question or even answer.
* #param command The entered command.
**/
public void continueGame(String command) {
while (inGame) {
if (spinGame) {
this.spinWheel();
// Break out of the loop.
break;
}
}
}
/**
* Method exitGame
*
* Exit the game..
**/
public void exitGame() {
spinGame = false;
tofGame = false;
this.printStart();
}
/**
* Method spinWheel
*
* This will spin the wheel.
**/
private void spinWheel() {
this.print(0, spin.spinWheel());
}
/**
* Method print
*
* Prints text using System.out
* #param type printing type (Println/print).
* #param message The message
**/
private void print(int type, String message) {
switch (type) {
case 0:
System.out.println(message);
break;
case 1:
System.out.print(message);
break;
}
}
}
Spin.java:
package games;
import java.util.Random;
public class Spin {
/**
* The base auth we are going to work with..
**/
private int auth = this.rand(1000) / 5;
/**
* Creating new Random object.
**/
private Random r = new Random();
/**
* Method spinWheel
*
* Spins the damn wheel..
* #return spinned value + if you won or not.
**/
public String spinWheel() {
return this.spinWheel(this.rand(100));
}
/**
* spinWheel
*
* Returning results.
**/
private String spinWheel(int number) {
int result = this.Calculate(this.rand(number));
if (result < 101) {
return "You have won the game!" + result;
} else {
return "You've lost the game!" + result;
}
}
/**
* Method calculate
*
* Calculates the spin.
* #return the spinned number.
**/
private int Calculate(int Number) {
int var = this.rand(101);
int holder = (var * Number) / 2;
return holder + this.auth;
}
/**
* Shortcut for nextInt of Random
**/
public int rand(int x) {
return r.nextInt(x);
}
}
rand is invoked before the Random instance r is initialised. Switch the order or these 2 statements
private int auth = this.rand(1000) / 5;
private Random r = new Random();
should be
private Random r = new Random();
private int auth = this.rand(1000) / 5;
Make the assignment of r the first thing in your Spinwheel class definition, i.e. put it before it is used in this.rand(1000):
public class Spin {
/**
* Creating new Random object.
**/
private Random r = new Random();
/**
* The base auth we are going to work with..
**/
private int auth = this.rand(1000) / 5;
r is null, so you can't call any instance method on r. Make sure you intialize r before using it.
More specifically, in this line:
private int auth = this.rand(1000) / 5;
you're calling the rand() method before r has been initialized (it's initialized right after).
This is the line causing the NullPointerException:
private int auth = this.rand(1000) / 5;
Since this line comes before the initialization for r, you are invoking rand before r was initialized. In that case r is null in rand and that's your exception.
This is obvious from your stack trace:
at games.Spin.rand(Spin.java:68)
at games.Spin.<init>(Spin.java:10)
Note that the exception is happening in the initializer. From there, it's easy to back out what is going on.
You need to initialize r first, that is, move the initialization line for r before the initialization line for auth. Thus:
private Random r = new Random();
private int auth = this.rand(1000) / 5;
This is because r is being used before it is instantiated within statement private int auth = this.rand(1000) / 5; . So , JVM is seeing r as null , which is leading to NPE. To get rid of this problem within Spin class declare the fields as follows:
private Random r = new Random();
private int auth = this.rand(1000) / 5;