Random number guessing game with limitations after each guess - java

I am making a number guessing game:
The computer generates a number inside an interval
I try to guess it and receive a reply whether it's higher/lower than my guess or equals to my guess and I've won
There is an interval in which I can guess, as well as a guess attempt limit
The trick is, however, that I need to implement another condition: each guess should "shrink" the interval in which I'm able to guess. For example: computer generates 50, I guess 25, computer replies "The random number is larger.". Now knowing that, I should not guess anything lower than 25 again, it's unreasonable. In case I guess i.e. 15, the computer should reply "The guess doesn't make sense.". I understand that I somehow need to save each guess value to a new variable, but nothing seems to work. I'm a beginner, please bear with the following code, I've tried a lot of things:
public String guess(int guess)
{
int lowerBound = 0;
int upperBound = 99;
Set<Integer> lowerGuesses = new TreeSet<>();
Set<Integer> higherGuesses = new TreeSet<>();
if (gameOver) {
return "The game is over.";
}
if (guess < 0 || guess > 99) {
return "The guess is out of bounds.";
}
if (guessCount < maxGuessCount) {
if (guess < secretNumber) {
if (lowerGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
else {
guessCount++;
lowerBound = guess;
lowerGuesses.add(guess);
return "The random number is larger.";
}
}
if (guess > secretNumber) {
if (higherGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
else {
guessCount++;
upperBound = guess;
higherGuesses.add(guess);
return "The random number is smaller.";
}
}
if (lowerGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
if (higherGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
}
if (guess < lowerBound || guess > upperBound) {
return "The guess doesn't make sense.";
}
if (guessCount == maxGuessCount) {
gameOver = true;
victorious = false;
return "Ran out of guess attempts.";
}
guessCount++;
gameOver = true;
victorious = true;
return "You won.";
}
Thank you in advance!

First, to avoid confusion, let's rename the method in order to make sure that its name is not an exact match with its parameter, so this is how it should look like:
public String makeGuess(int guess)
avoid naming different entities in the same name space with the exact same name (local variables being present in different methods or parameters having similar names with data members for the purpose of initialization are an exception). From now on, you will call the method as makeGuess(25), for example.
Now, to the actual problem. You have an incorrect assumption. You assume that you need to keep track of past intervals. That's not the case. You can just change the edges of the intervals. Also, your code is superfluous, I advise you to refactor it. Finally, you always initialize upper bounds, local bounds and higher and lower guesses as local variables, so they will never be kept track of. Instead of this, you need to perform the following simple measures in order to make this work:
Define the bounds and limit as data members
protected int lowerBound = 0;
protected int higherBound = 99;
protected int lb = 0;
protected int hb = 99;
protected int limit = 5;
protected int guessCount = 0;
protected int randomizedNumber; //Initialize this somewhere
Note that I have hard-coded some values. You might want to make this dynamic with initialization and stuff like that, but that's outside the scope of the answer. lowerBound, higherBound, limit are game settings. while lb, hb, guessCount represent the game state. You could separate this logic into another class, but for the sake of simplicity, even though I would program differently, I will leave them here in this case.
Have a method that initializes the game
public void initialize() {
lb = lowerBound;
hb = higherBound;
guessCount = 0;
}
So you separate your concern of game initialization from the outer logic of starting and maintaining a game.
Implement makeGuess in a simplistic way
public String makeGuess(int guess) {
if (++guessCount >= limit) return "The game is over.";
else if ((lb > guess) || (hb < guess)) return "The guess doesn't make sense";
else if (randomizedNumber == guess) return "You won.";
else if (guess < randomizedNumber) {
hb = guess;
return "The random number is smaller.";
} else {
lb = guess;
return "The random number is larger.";
}
}
NOTE: I dislike mixing up the logic with the output layer, the reason I did it in the method above was that you have mentioned you are a beginner and my intention is to make this answer understandable for the person who just begun programming and is very confused. For the purpose of actual solutions, you should return a state and in a different layer process that state and perform the console/UI operations you need. I will not go through the details now, as it would also be outside of scope, but for now, please have some success with the solution above, but THEN you should DEFINITELY look into how you need to code, because that is almost as important as making your code work.

Related

How can I change how an objects field is PRESENTED while still retaining the original value of said field?

I'm working on a project that involves creating a Spring Boot REST Application using JDBC Template to access a database of my own creation (MySQL). I'm using Postman to verify endpoints and entering data using JSON through postman. I'm currently tasked with creating a guessing game that generates a random 4 digit number, easy enough. My issue is this; " Returns a specific game based on ID. Be sure in-progress games do not display their answer" I've created a "starter" method that fulfills the requirement, but only at it's most basic level. I cannot operate on an object once it's field's value is changed to "Hidden" as it is no longer a 4 digit number. I'd like to hide the answer while still being able to operate in a functional manner on the randomly generate number. Here is my base method for this return:
#Override
public Game gameById(int id) {
Game game = gameDao.getGameById(id);
if (game.getFinished() == false) { // "Hides" the answer
game.setGameAnswer("Hidden");
}
return game;
}
Here is my method that uses the above code in my application:
#PostMapping("/guess")
public ResponseEntity<Game> play( int id, String guess) {
Game game = service.gameById(id); // Get method
if (game == null) {
return new ResponseEntity(null, HttpStatus.NOT_FOUND);
}
Round round = new Round();
round.setGuess(guess);
service.guess(round, game);
return ResponseEntity.ok(game);
}
Any ideas?
EDIT:
Game Object:
public class Game {
int gameId;
String gameAnswer;
Boolean finished;
List<Round> Rounds = new ArrayList<>();
I will also include the database game table which i am storing said fields into:
CREATE TABLE game(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
gameAnswer CHAR(4) NOT NULL,
finished BOOLEAN DEFAULT false
);
To clarify my question, when running the game in postman, the game object and it's fields are all displayed as well as the games answer. My goal is to hide this answer as long as the games status reads "false". Once the number has been correctly guessed the status of the game will automatically be set to true.
Here is an example from the JSON output of postman:
{
"gameId": 1,
"gameAnswer": "2651",
"finished": false,
"rounds": [
{
"roundId": 1,
"guess": "1234",
"timeOfGuess": "2021-06-18 09:28:29",
"exactMatch": 0,
"partialMatch": 0,
"gameId": 1
}
]
}
While the "Finished" is set to false, I want the game answer to read "Hidden", but still have the numerical value be present to operate on.
Guess Method:
#Override
public void guess(Round round, Game game) {
// Takes the games generated answer and the user's guess and breaks them down into character array's
round.setGameId(game.getGameId());
char[] gameAnswer = characterBreakDown(game.getGameAnswer());
char[] roundGuess = characterBreakDown(round.getGuess());
// used for comparing the array elements one by one
int min = 0;
int max = 1;
int exact = 0;
boolean run = true;
while (run) {
for (int i = 0; i <= 3; i++) {
if (Arrays.equals(roundGuess, gameAnswer)) {
round.setExactMatch(4);
game.setFinished(true);
gameDao.updateGameById(game);
run = false;
} else if (Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
exact++;
round.setExactMatch(exact);
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
} else if (!Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
}
}
}
game.getRounds().add(round);
roundDao.createRound(round);
}
If I understand correctly, your issue is that in one use case (when you are responding to an API request), you want to retrieve a Game and respond to the API request with the Game's "gameAnswer" field hidden, but for other, purely background operations, you want to retrieve the Game with the "gameAnswer" field intact.
Assuming that's the case, I think that your only real issue is where you "blank-out" the "gameAnswer" field.
It looks like you are currently doing that in the "gameById" method of the repository. That results in the gameAnswer being hidden in all cases, right?
So, the solution would be to blank-out the gameAnswer in the controller method instead.
If I have this wrong / backwards I apologize, but the way that you posted your code, in pieces, makes it hard to follow. But anyway, I believe that the solution to your problem is to only blank-out the field where it actually needs to be blanked out. Or if you are having some other issue with it being blanked out / not blanked out because you are doing so in some shared method, perhaps create a new method for the use-case that needs it the other way.

Issue converting double to boolean

I am working on a project in my Java class that is using multiple classes as well as GUI (not sure if that info is relevant). My group partner and I have come across an issue though. We have a Validator class, that should validate a "SSN" but we are continuously given the error:
java:146: error: incompatible types: double cannot be converted to boolean
if(Validator.isValidSSN(jTextFieldEmpSSN)){
Now obviously java:146 is the line. the code we have for each class is:
employeeUI class (the one showing the error):
private void jButtonEnterActionPerformed(java.awt.event.ActionEvent evt)
{
Employee e=new Employee();
if(Validator.isValidName(jTextFieldEmpFirst)){
if(Validator.isValidName(jTextFieldEmpLast)){
if(Validator.isValidEmail(jTextFieldEmpEmail)){
if(Validator.isValidSSN(jTextFieldEmpSSN)){
e.setFirstName(jTextFieldEmpFirst.getText());
e.setLastName(jTextFieldEmpLast.getText());
e.setEmailAdd(jTextFieldEmpEmail.getText());
e.setSSN(Integer.parseInt(jTextFieldEmpSSN.getText()));
}}}}
and the Validator class for isValidSSN is:
public static double isValidSSN(JTextField textfield)
{
double number = 0;
boolean inRange = false;
while(!inRange)
{
number = Double.parseDouble(textfield.getText());
if (number >= 100000000 && number <= 999999999)
{
inRange = true;
} else {}
}
return number;
}
We have been beating our head on how to fix this for quite some time, but are coming up at a loss. Are we missing something? we would greatly appreciate any help with this.
If I ask, "Is 123-45-6789" a valid SSN?" you wouldn't reply "123456789.0", would you? You'd give me a yes or a no. By returning double your method is doing the former. It's responding with a number instead of an answer to the question.
A good rule of thumb is that methods starting with is or has should return booleans. "Is this a valid SSN?" is a yes/no question, so isValidSSN should return the programming equivalent of yes/no.
public static boolean isValidSSN(JTextField textfield)
There are a couple of other design points here:
The loop isn't necessary. The SSN is either valid or it isn't.
A text field is not itself an SSN. It holds some text, and that text is the SSN. Rather than taking a text field and looking up the text in the field with getText(), it'd be better to have isValidSSN take the text directly. Let the caller extract the text from the text field.
In broader terms this is known as the single responsibility principle. Every method should ideally do just one thing.
Result:
public static boolean isValidSSN(String ssn) {
double number = Double.parseDouble(ssn);
if (number >= 100000000 && number <= 999999999) {
return true;
}
else {
return false;
}
}
P.S. If I don't mention it someone will surely comment that the if and else blocks aren't necessary; one can return the if result directly. They would be right, though I consider it a bit of an advanced trick. It would look like so:
public static boolean isValidSSN(String ssn) {
double number = Double.parseDouble(ssn);
return number >= 100000000 && number <= 999999999;
}

Why do I get no output out of println in this case?

I tried this a lot, and debugged it a few times, everything seems to be working and largest prime does indeed become the largest prime even if it takes rather long.
I can't get the printed value from System.out.println. I could find it through the debugger but the value is too high to find fast just holding down step over.
It compiles as well so I am stumped about what's the issue here. I would be very happy to know what I did wrong.
Edit: The reason why I wrote this code in the first place is because in the site project euler it asked for the largest prime value that when divided with the value of primer gave a whole number.
Is there a way at least that would allow me to make it faster with the same value? this seems rather impractical.
package unit5;
public class Primefinder { public static void main(String[] args)
{
double primer = 600851475143d;
double largestprime = 0;
Boolean ifprime = false;
for(double x = 2d; x < primer; x++)
{
for(double z = 2d; z<x; z++)
{
if( (x%z == 0) && (z != x) )
{
ifprime = false;
break;
}
else {
ifprime = true;
}
}
if((ifprime != false) && (x > largestprime))
{
largestprime = x;
}
ifprime = false;
}
System.out.print(largestprime);
}
}
for other questions you might ask everywhere, please tell us that what is the purpose of your code. this way it is easier to get the fault.
the code you have written above runs completely but the numbers you have used are too big so you need to wait a lot, so that compiler be able to reach to this line:
System.out.print(largestprime);
use lower numbers (at least for test) or wait properly.
Your 'primer' Value is very big.
So, loop is taking very much time to reach at '600851475143' value.
Wait Sometime and it with show largest prime number

Health counter for game

I am having an issue with a health counter in my battleship game. What I need is that when the method is called, it will take 1 off of health. So let's say that the health is at 3, the method is called when the players ship takes a hit. Then I need it to go health-1, and keep that value. Then when the health=0, the game will end.
Any questions and improvements to this code is welcome, as well as criticism.
UPDATED:
public static void enemyShoot (int row, int col)
{
int shot1;
int shot2;
int health = 3;
Random enemyshot = new Random();
shot1 = enemyshot.nextInt(5)+1;
shot2 = enemyshot.nextInt(5)+1;
if (shot1 == row && shot2 == col)
{
System.out.println("You Have Been Hit");
health = Health(health);
}
}
public static int Health (int health)
{
if (health == 0){
System.out.println("You dead");
System.exit(0);
}
health = health-1;
return health;
}
You are initializing health to 0
int health = 0;
Then when your shot hits the target you
Health(health);
Which subtracts 1 and then tests for 0
health = health-1;
if (health == 0)
your health is now at -1 so it never == 0
You should set health to some positive value and or change your test to
if (health <= 0)
When you pass health as a parameter to Health method, it makes a copy to use in this method. So, when you decrement and analyse it, the copy gets decremented to 2 (if you initially passed 3), but the original variable is still equal to 3. So, in fact, the health counter is never decremented.
It's called "passing argument by value", you can check out this question: What's the difference between passing by reference vs. passing by value?
The right thing to do is to decrement the value inside your method and then return the result. You should also call your method like this:
health = Health(health);
Also, arsendavtyan91 is right, you start with health = 0... you might want to pass the actual value to the method enemyShoot
You were close with your Health method, what you would want is something like this:
public static void hit() {
this.health--;
if (health <= 0) {
System.out.println("You died.");
System.exit(0);
}
}
Java is a pass-by-reference so you need to update the health of the object that just got hit. In this case I am assuming your Health() is inside of that object.
Otherwise you start with your health = 3; and every time it gets hit it will become health = 2; but the object that is being hit will always stay at 3 health. Again, without seeing any more of your code I can't tell exactly the best way to do this, so I had to assume a few things.
It should be noted that this will exit the program very quickly and you won't even see the You died message.
You need not subtract if health is equal to zero.
The game will continue since you always initialize health on the line int health = 3; in the enemyShoot() method, right before going to call the Health() method.
So I suggest you declare health inside the class (like a global variable) and initialise it by passing it to the a constructor like this:
int health;
public ClassName(int health){//this is a constructor with the `health` argument
this.health = health;
}
public static void enemyShoot (int row, int col)
{
int shot1;
int shot2;
Random enemyshot = new Random();
shot1 = enemyshot.nextInt(5)+1;
shot2 = enemyshot.nextInt(5)+1;
if (shot1 == row && shot2 == col)
{
System.out.println("You Have Been Hit");
health = Health(health);
}
}
public static int Health (int health)
{
if (health == 0){
System.out.println("You dead");
System.exit(0);
}
health = health-1;
return health;
}
First, you should really read more about the basics of object oriented programming. My answer is based on that little snippet of code you provided, so I'm not sure if there already is a proper implementation...
However, I think what you want is to create a Battleship Object, which has initial health as a member variable. You could define coordinates, orientation etc with parameters, but I'll leave them out from this example. After you have managed to create this object, use enemyShoot method to calculate if battleship has been hit and decrease health that is the case. Which comes out something like this:
public class Battleship {
int m_health;
public BattleShip() {
m_health = 3;
}
public enemyShoot(int x, int y) {
// TODO: calculate if hit
if (hit == true) {
m_health--;
if (m_health == 0)
System.out.println("You dead");
System.exit(0);
}
}
}
So to use the code above I would put it in a file called Battleship.java and create a main method which would initialize the objects I wish to use
public static void main(String ... args) {
Battleship bs = new Battleship(); // <-- Creates a new Battleship object
bs.enemyShoot(0,0); // <-- shoot the battleship
bs.enemyShoot(0,0);
bs.enemyShoot(0,0); // <--At this point battleship would be destroyed, if hit
}
The problem with the Health method you provide is that is takes primitive data type int as parameter (initially set as 0) which gets assigned -1 every time Health is called. Callign this method does not have affect on health value inside enemyShoot method, since this is NOT an Object but a inner method variable of primitive data type.
Hope this helps you to get on with your assignment. :)

How would I ask java to stop until a variable is changed?

so I am trying to design a GUI with the program BlueJ, that sends data from a jtextfield box into a variable (already done), and using that variable to be able to update another variable, but for java to "stop running" until a specific variable is updated. So something along the lines of...
string bacon = "";
int agility = 1;
int dexterity = 2;
int strength = 3;
int intelligence = 4;
int charisma = 5;
//my variables.
if (bacon = "agility")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
agility= agility+bacon
}
else if (bacon = "dexterity")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
dexterity = dexterity+bacon
}
else if (bacon = "strength")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
strength = strength+bacon
}
else if (bacon = "intelligence")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
intelligence = intelligence+bacon
}
else if (bacon = "charisma")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
charisma = charisma+bacon
}
Thank you very much to anybody who can help me figure this out. I would also like it to have something so that if bacon is stated as a non-integer (32.7 or "hello"), it would simply ask you to input a proper integer.
Not quite sure what you are asking in the first part of the question, but for the second part to it check if it is a non integer you can do something like this....
boolean isValidInput = true;
for(int i=0;i<bacon.length();i++) {
char charAt = bacon.charAt(i);
if(!Character.isDigit(charAt)) {
isValidInput = false;
break;
}
}
if(!isValidInput)
System.out.println("Invalid Input!");
Also, = is used for assignment in java, ex a = 3;, however if you are trying to check if something is equal to something else, you should use the == operator. ex. if(x==2)
But in your case, since you are comparing Strings, you should use if(x.equals("hello"))
Another tip, instead of saying charisma = charisma + bacon; you can just say charisma += bacon; as a shorthand ;)
Hope this helps,
Saashin

Categories

Resources