Why is method not working as I thought it would? - java

I am attempting to create a hangman game. I have everything working as I want so far with the exception of one method. This method is called processGuess and takes a String letter and two String arrays as parameters. The first array is an array called spaceArray and contains Underscores that match the length of the word being guessed (example: hello produces [ _ , _ , _ , _ , _ ]). The second array is called wordArray and contains the word the user is trying to guess.
The way I envision my method working is as follows:
Create an array that stores all guessed letters (guessArray)
Create a counter that keeps track of the number of guesses (guessCounter)
Use a for loop to iterate through each letter of the word and compare it with the letter the user guessed.
a. If the letter is in the word, add the letter to the correct index of the spaceArray
Compare spaceArray to wordArray
a. If equal, print something saying they won in x number of guesses
b. If not equal.
Print spaceArray
Call the method that asks user to guess the next letter
Call this method so that the new guessed letter is processed.
The problem is that when I recall this method, it does not contain the new letter but still contains the old letter. I am unsure of what I am doing wrong. This is the first time that I have tried using methods within a method.
Here is my method:
public static void main(String[] args) throws FileNotFoundException {
Scanner file = new Scanner(
new File("C:/FilesForJava/ScrabbleDictionary.txt"));
instructions();
String[] dictionary = createDictonaryArray(file);
String[] randomWord = getRandomWord(dictionary);
String[] underscoreArray = showSpaces(randomWord);
String letter = getGuesses();
processGuess(letter, underscoreArray, randomWord);
}
public static void instructions() {
System.out.println("Let's play hangman!");
System.out.println();
}
public static String[] createDictonaryArray(Scanner inputFile)
throws FileNotFoundException {
int wordCount = 0;
while(inputFile.hasNext()) {
String word = inputFile.next();
wordCount++;
}
String[] scrabbleDictionary = new String[wordCount];
Scanner file = new Scanner(
new File("C:/FilesForJava/ScrabbleDictionary.txt"));
while(file.hasNext()) {
for(int i = 0; i < wordCount; i++) {
scrabbleDictionary[i] = file.next();
}
}
file.close();
return scrabbleDictionary;
}
public static String[] getRandomWord(String[] dict) {
String word = dict[(int)(Math.random() * dict.length)];
String[] wordArray = new String[word.length()];
for(int i = 0; i < wordArray.length; i++) {
wordArray[i] = word.trim().substring(0, 1);
word = word.trim().substring(1);
}
return wordArray;
}
public static String[] showSpaces(String[] word) {
String[] spaceArray = new String[word.length];
for(int i = 0; i < word.length; i++) {
spaceArray[i] = "_";
}
System.out.println(Arrays.toString(spaceArray));
System.out.println();
return spaceArray;
}
public static String getGuesses() {
Scanner guess = new Scanner(System.in);
System.out.println("Guess a letter: ");
String letter = guess.next();
System.out.println();
//guess.close();
return letter;
}
public static void processGuess(String letter, String[] spaceArray,
String[] wordArray) {
int guessCounter = 0;
String[] guessArray = new String[spaceArray.length];
for(int i = 0; i < spaceArray.length; i++) {
guessCounter++;
guessArray[i] = letter;
String indexLetter = wordArray[i];
if(indexLetter.equalsIgnoreCase(letter)) {
spaceArray[i] = letter;
}
}
if(spaceArray.equals(wordArray)) {
System.out.println("Yes! You won in " + guessCounter + "guesses!");
}else {
System.out.println(Arrays.toString(spaceArray));
getGuesses();
processGuess(letter, spaceArray, wordArray);
}
}

You need to pass the new guess into your processGuess method. Try something like this:
else {
System.out.println(Arrays.toString(spaceArray));
String newLetter = getGuesses();
processGuess(newLetter, spaceArray, wordArray);
}

I think this method is trying to do too much. It's strange for it to read new input and call itself recursively -- I would have expected its caller to use a loop to solicit guesses from the player and call this method (which would not recurse) instead. The method might indicate by a return value whether the user had won.
Additionally, the code seems overly complex. For instance, what's the point of guessArray, which you instantiate and initialize but never use for anything?
Furthermore, it's strange that you use arrays of Strings instead of arrays of chars, since all your Strings seem to contain a single character each. (That might actually be appropriate if you are looking to accommodate surrogate pairs, but such a consideration seems a little out of character for the level of the task.)
In any event, the reason the recursive calls to your method see only the first letter guessed is that that's what you pass to them. The getGuesses() method does nothing to modify the local letter variable (nor can it do), and the method itself just passes along whatever was passed to it.

Well, it looks like you might have a couple of problems.
First, recursion is a very poor choice for this method, I think what you're looking for is a while loop where the condition changes when the strings are equal. Using recursion here needlessly increases the size of the stack as you call more and more methods, but never return from them.
Now as to your question, in the code you gave us, the variable letter never gets changed. I assume that get guesses returns a string? If thats true then you need to set letter equal to it.
I would also like to suggest that you use a char instead of a string.
public static void processGuess(String letter, String[] spaceArray,
String[] wordArray) {
while(true) {
int guessCounter = 0;
String[] guessArray = new String[spaceArray.length];
for (int i = 0; i < spaceArray.length; i++) {
guessCounter++;
guessArray[i] = letter;
String indexLetter = wordArray[i];
if (indexLetter.equalsIgnoreCase(letter)) {
spaceArray[i] = letter;
}
}
if (spaceArray.equals(wordArray)) {
System.out.println("Yes! You won in " + guessCounter + "guesses!");
break;
} else {
System.out.println(Arrays.toString(spaceArray));
letter = getGuesses();
}
}
}

You've written the method as a recursive method (probably not the best way to do it). The issue is that when a recursive method declares a local variable, each invocation of the recursive method has its own copy of the local variables.
Thus you call processGuess, which creates a guessArray. Then processGuess calls itself again, which has its own guessArray, and after this happens a few times, you'll have a stack that looks something like:
+--------------------------------------------------------+
+ processGuess#1 +
+ local variables: guessCounter#1, guessArray#1, i#1 +
+--------------------------------------------------------+ --> calls:
+ processGuess#2 +
+ local variables: guessCounter#2, guessArray#2, i#2 +
+--------------------------------------------------------+ --> which calls:
+ processGuess#3 +
+ local variables: guessCounter#3, guessArray#3, i#3 +
+--------------------------------------------------------+ --> which calls:
+ processGuess#4 +
+ local variables: guessCounter#4, guessArray#4, i#4 +
+--------------------------------------------------------+
When processGuess#4 modifies guessArray, it changes guessArray#4. But that has no effect on guessArray#3, guessArray#2, or guessArray#1. All of these are separate local variables, and they are references that refer to four different objects. Thus, when processGuess#4, processGuess#3, and processGuess#2 all return, the changes they've made to their own guessArray's are lost, and processGuess#1 will see only the changes that it, itself, has made to its own guessArray.
As I said, I wouldn't use recursion for this particular problem. But it's definitely a problem in other cases where recursion is the right way to do things. The solutions are: (1) declare the variable or object outside the recursive method, as an instance field in the object--then they will all be accessing the same variable; or (2) add a parameter to the recursive method so that the recursive invocations can share a reference to the same object.
[Note: The #1, #2 numbers I added are just to help explain things; they aren't part of any language syntax or anything like that.]

Related

How to Find the word that comes after a specified word in java String

My program has a String inputted Eg. hello i am john who are you oh so i see you are also john i am happy
my program then has a keyword inputted Eg. i (the program doesn't like capitals or punctuation yet)
then it reads the initial String and finds all the times it mentions the keyword + the word after the keyword, Eg. i am, i see, i am.
with this is finds the most common occurrence and outputs that second word as the new keyword and repeats. this will produce
i am john/happy (when it comes to an equal occurrence of a second word it stops (it is meant to))
What i want to know is how i find the word after the keyword.
package main;
import java.util.Scanner;
public class DeepWriterMain {
public static void main(String[] args) {
String next;
Scanner scanner = new Scanner(System.in);
System.out.println("text:");
String input = scanner.nextLine();
System.out.println("starting word:");
String start = scanner.nextLine();
input.toLowerCase();
start.toLowerCase();
if (input.contains(start)) {
System.out.println("Loading... (this is where i find the most used word after the 'start' variable)");
next = input.substring(5, 8);
System.out.println(next);
}else {
System.out.println("System has run into a problem");
}
}
}
If you use split to split all your words into an array, you can iterate through the array looking for the keyword, and if it is not the last in the array, you can print the next word
String arr [] = line.split(" ");
for (int i = 0; i < arr.length -1; i++) {
if (arr[i].equalsIgnoreCase(keyword)) {
sop(arr[i] + " " arr[i + 1]);
}
if it is not the last in the array, iterate only to length - 1
The String class includes a method called public int indexOf(String str). You could use this as follows:
int nIndex = input.indexOf(start) + start.length()
You then only need to check if nIndex == -1 in the case that start is not in the input string. Otherwise, it gets you the position of the first character of the word that follows. Using the same indexOf method to find the next space provides the end index.
This would allow you to avoid a linear search through the input, although the indexOf method probably does one anyway.

Get Data by Given Index

While coding a program for employee-management, I need a method which spits out the corresponding data to a specific index (the number given by the user).
Already tried a lot of methods to make this happen, for example the one below, with no success:
public static void ausgabeIndex(int index, String[] nN, String[] vN, String[] adres) {
Scanner sc = new Scanner(System.in);
System.out.println("Please insert the number of the registered employees: ");
index = sc.nextInt();
for (int i = 0; i < nN.length; i++) {
if (nN[i].equals(index)) {
System.out.println(nN[index]);
System.out.println(vN[index]);
System.out.println(adres[index]);
}
}
}
It's unclear if there is more than one issue here, since you didn't say what the actual problem was, other than it doesn't work.
Also, your data here is a bit strange. This would be better suited as a class to encapsulate all the data instead of three string arrays.
I suspect, however, that this is part of your problem: nN[i].equals(index)
nN is an array of String, and index is an int. These will never be equal. They aren't the same data type.
To properly compare a String and an integer, you need to either convert the String to an int or the int to a String. Converting an int to a String is the safer option, so you could do this:
nN[i].equals(String.valueOf(index))
To do the opposite, converting a String to an int, you can use Integer.parseInt(String)
Integer.parseInt(nN[i]) == index
One more note:
public static void ausgabeIndex(int index
You are passing in index but immediately overwriting it with your Scanner.nextInt. You don't need it as a method argument.
You need to address the following things in your code:
Whenver you try to access an element from an array, make sure you check the bounds to avoid ArrayIndexOutOfBoundsException e.g. you have accessed vN[index] without checking whether index is less than vN.length. adres[index] also has the same problem.
nN[i] is a String while i is an int and therefore they can not be compared unless you change the one into the other. However, I'm sure you didn't mean to compare nN[i] with index. Looking at your program, it seems you want to compare nN[i] with nN[index].
Since you are already passing index as a parameter to the method, the following lines do not make sense unless you want to override the value of index. If you do not want to override the value of parameter, index, remove these lines.
Scanner sc = new Scanner(System.in);
System.out.println("Geben Sie bitte die Nummer des/der regestrierten Mitarbeiters/Mitarbeiterin ein: ");
index = sc.nextInt();
Alternatively, if you want to keep these lines in the method, remove the parameter, index. As I've mentioned above, keeping both of these makes sense only if you want to override the value of parameter, index.
Given below is the code incorporating these comments:
public static void ausgabeIndex(int index, String[] nN, String[] vN, String[] adres) {
if(index < nN.length) {
for (int i = 0; i < nN.length; i++) {
if (nN[i].equals(nN[index])) {
System.out.println(nN[index]);
if (index < vN.length) {
System.out.println(vN[index]);
}
if (index < adres.length) {
System.out.println(adres[index]);
}
}
}
} else {
System.out.printnl("The index, " + index + " is out of the bounds.");
}
}

Stuck on making an if statement for my game

Trying to make an if statement for a game where I give the user a scrambled word and they have to input a word made up of those scrambled letters. I have hardcoded three answers they can use. I'm stuck on trying to make an IF else statement to tell the user if they got the answer correct or to try again.
import java.util.*;
public class Scramble {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String[] wordbank = new String [10];
int answer;
wordbank[0] = "GALEE";
wordbank[1] = "OLWBE";
wordbank[2] = "RHCIA";
wordbank[3] = "RWSIT";
wordbank[4] = "NTARI";
wordbank[5] = "ETLBA";
wordbank[6] = "TIRSH";
wordbank[7] = "TLUFE";
wordbank[8] = "MIGEIN";
wordbank[9] = "RAEHT";
String[] GALEE = {"Eagle", "Gale", "Leg"};
String[] OLWBE = {"Elbow", "Below", "Lobe"};
String[] RHCIA = {"Chair", "Hair", "Air"};
String[] RWSIT = {"Wrist", "Wit", "It"};
String[] NTARI = {"Train", "Rant", "Art"};
String[] ETBLA = {"Table", "Late", "Bet"};
String[] TIRSH = {"Shirt", "Sir", "Sh"};
String[] TLUFE = {"Flute", "Felt", "let"};
String[] MIGEIN = {"Gemini", "Mini", "Gem"};
String[] RAEHT = {"Heart", "Hate", "Ear"};
System.out.println("Create a five letter word out of " + wordbank[0] + ".");
answer = s.nextInt();
if (answer) {
}
As noted in the comment by Arnaud, you should probably use next(), at least check that the value of answer is really the user input.
Once this is out of the way, you will want to check that answer is one of the three acceptable solutions.
You can use this in java 8:
boolean resultOk = Arrays.asList(GALEE).contains(answer);
if (resultOK) {
// do something
}
This will check that answer belongs to your GALEE array (so it has to be equal to either eagle, gale or leg.
You can simply iterate through GALEE to see if what they have entered is equal to one of the words in GALEE.
This is what your code should look like:
String answer = scanner.nextLine();
boolean matches = false;
for(String combination: GALEE){
if(answer.equals(combination)){
matches = true;
break
}
}
if(matches){
System.out.println("You got it correct!");
} else{
System.out.println("You got it wrong!");
}
I'd also like to note that you asked them to
Create a five letter word
but some of the words in GALEE are less than 5 letters long.
Assuming that the user types the word, you need to read it with next() instead of nextInt().
The if statement reads a boolean, that boolean in this case should come from a comparison (for example, equals as a simple solution).
if(answer.equals("Eagle")){
//do something
}
See How do I compare strings in Java? for more info
Or, for a better solution you should try to check in only one step if the answer given by the user matches some element of the array
if(Arrays.asList(GALEE).contains(answer)){
//do something
}
For this you can check How do I determine whether an array contains a particular value in Java?

Avoid 26 if/elseif/else statements when checking for a letter in user input

I was just wondering if there was a simple way to check for the value of a letter from user input. For example,
Scanner scanner = new Scanner(System.in);
String message = scanner.nextLine();
String letter;
int length = message.length();
for (int i=0; i < length; i++) {
letter = message.substring(i, i+1);
if (letter.equalsIgnoreCase("a")) {
System.out.println("letter a");
// Of course the real program won't just print out "letter a" but this is
// to show that all actions are different
} else if(letter.equalsIgnoreCase("b") {
System.out.println("letter b");
} // etc... for all of the possible values of "letter"
}
Is there any way to write the above statement without having to make 26+ if/else statements? (I know there's switch-case but you'd still have to write 26+ of them). Any help is greatly appreciated!
Well, according to the comments you are just printing different text depending on the letter. In that case, Tim's idea of using a Map is pretty good. I will show you a basic example:
In your class, create a HashMap. I'm guessing that a final static one would be fine:
private static final HashMap<String, String> messages = new HashMap<String, String>();
static {
messages.put("A", "Hello!");
messages.put("B", "Again!");
messages.put("C", "Etc etc!");
}
Notice how the keys are all uppercase. So when you get the letter you should convert it to uppercase too:
letter = message.substring(i,i+1).toUpperCase();
So now you can get the message to print doing this:
String toPrint = messages.get(letter);
And you can just print that without making 26 switches.
The above solution assumes you need to print some sort of customized text. Otherwise the others' solutions are fine: just print the letter.
Without knowing what you want to do in the if parts, this is the most generic approach to this problem:
final Map<String, Consumer<String>> actions = new HashMap<>();
actions.put("a", this::performActionA);
actions.put("b", this::performActionB);
final Scanner scanner = new Scanner(System.in);
final String message = scanner.nextLine();
String letter;
final int length = message.length();
for (int i = 0; i < length; i++) {
letter = message.substring(i, i + 1);
actions.get(letter).accept(message);
}
And then you need to have the methods like this:
private void performActionA(final String message) {
System.out.println("Test.performActionA()");
}
private void performActionB(final String message) {
System.out.println("Test.performActionB()");
}
Of course in production you might want to check if there even is an action, but I leave that to you.
you can convert the letter into char value and then directly check for the ASCII equivalent. Then it will be numerical and easier too. This way whatever be your operations convert it into ASCII format and it will be done easily.

Why doesn't my string change?

public static void read(String a[], double b[], String c) throws IOException {
Scanner in = new Scanner(new File("data.txt"));
while (in.hasNext()) {
int i = 0;
String id = in.next();
String name = in.next();
String lastname = in.next();
double grade = in.nextDouble();
if (name.substring(0, 2).equalsIgnoreCase(c)) {
a[i] = id + "\t" + name + "\t" + lastname + "\t" + grade;
b[i] = grade;
}
i++;
}
}
When I use this method with
String men[] = new String[501];
double menGrade[] = new double[501];
read(men, menGrade, "MR");
My men[0] is assigned a String but men[1] to men [500] are all null ...
You need to declare your variable i outside of the while loop to keep it incremented.
Right now you are
declaring it with value 0
assign the values to the 1st array position
increment i, and then
declare it again with value 0 at the next loop iteration.
SO, just change your lines:
while (in.hasNext()) {
int i = 0;
to
int i = 0;
while (in.hasNext()) {
Your code has also other issues which you should adress in some way.
I do not know why you initialize your array with a fixed size of 500 and also check some conditions before you add your men and grades to those Arrays. This will however lead to a few problems if you are not careful.
Right now you would have holes in your array whenever the if condition does not evaluate to true.
Also your program would crash if there is more than 500 entries in your file.
A rather good solution when dealing with dynamic data structures (so, when you do not know beforehand how many records you will have exactly), is to use a dynamic data structure.
In java you can have a look at java.util.List interface and probably java.util.ArrayList as a good implementation.
Here is also the java doc of that class: Java Doc
Here you find more on the collections api which are a good thing for dynamic data structures: Collections - List tutorial
while (in.hasNext()) {
int i = 0;
...
This will RESET i each time you start the while loop and you always overwrite a[0] and b[0].
swap these two lines! (so the int i = 0; comes before the loop:
int i = 0;
while (in.hasNext()) {
...
You should increment i in your if statement and not always like you do now. You don't want holes in your men array.
This simply means, either your loop is executing only once.
Or, if block in loop is exceuting only once.
Dependecy is on the content of file you are importing and your if condition.
I'm pretty sure that something is wrong with your "data.txt" that caused the while loop to execute only once. Otherwise, I don't see any mistake in the code.
Why don't you check the value of i during the execution of the program?
If your data.txt file contains One single line then the corresponding while will be running for once populating the first element of the array i.e men in your case
The reason is this:
while (in.hasNext()) {
int i = 0;
...
i++;
}
you are destroying and creating i variable each time loop is executed effectively reseting it to 0 each time. Asides from notes from other answers you can simply move i outside the loop:
int i = 0;
while (in.hasNext()) {
...
i++;
}
Now I can't run in myself, but I see
while (in.hasNext()) {
int i = 0;
//other
a[i] = id + "\t" + name + "\t" + lastname + "\t" + grade;
b[i] = grade;
}
i++;
If you use a counter i over an array/Collection, generally you have to give a greater scope to counter.
if the counter is inside the while, at every iteration you recreate the counter and you point always at the same element of array
The/one solution can be:
int i=0;
while (in.hasNext()) {
//etc

Categories