Function not ending correctly after recursive loop - java

I am trying to build a copy cat of the game risk. I have a while loop which says while the attack isn't finished do something. Then I ask the user to type in either 'end turn' to end turn or 'continue' to recursively call the attack function again. The problem is after the user types in attack a few times and then 'end turn' the turn doesn't end rather it starts from the beginning or the function again. I would greatly appreciate an expert eye to look at my code and see what I am missing, thanks in advance.
public void attackOrSkip(Player player,Player[] playerArray, int playerId) {
boolean attackFinished = false;
int numUnitsAttackWith = 0;
int defenceArmiesNumber =0;
displayString(makeLongName(player) + ": Type 'attack' to attack or 'skip' to skip your turn...");
String command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("skip") ||command.equals("skip ") ||command.equals("s")) {
return;
}else if (command.equals("attack") ||command.equals("attack ")){
displayString(PROMPT + command);
//while the attack isn't finished
while(attackFinished == false) {
//get the country the user is attacking
int countryAttackingFrom=countryFromCheck(playerId,player);
//get the country to attack
int countryToAttack = countryToCheck(player);
//get the player who we are attacking
int occupierPlayer =board.getOccupier(countryToAttack);
if ((board.getNumUnits(countryAttackingFrom)) < 2) {
displayString("You dont have enough units on this country to make an attack!");
attackOrSkip(player, playerArray, playerId);
break;
}
//if the country is adjacent to another one then you can attack else no
else if(isAdjacent(countryAttackingFrom,countryToAttack)) {
//check the number of unit to attack with
numUnitsAttackWith =numUnitsCheckerAttack(player,countryAttackingFrom);
//check the number of unit to defend with
defenceArmiesNumber = numUnitsCheckerDefence(player,countryToAttack);
//roll the dice
player.rollDice(numUnitsAttackWith);
playerArray[occupierPlayer].rollDice(defenceArmiesNumber);
//display the roll results
displayString(makeLongName(player) + "Rolled: "+printDie(player));
displayString(makeLongName(playerArray[occupierPlayer]) + "Rolled: "+printDie(playerArray[occupierPlayer]));
}
displayString(makeLongName(player) + ": 'end turn' or 'continue'");
command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("end turn")||command.equals("end turn ") ||command.equals("endturn")||command.equals("endturn ") ||command.equals("end")) {
attackFinished = true;
return;
}else if(command.equals("attack") ||command.equals("attack ")){
// break;
}else if(command.equals("continue") ||command.equals("continue ") ||command.equals("con")){
attackOrSkip(player,playerArray,playerId);
}else {
return;
}
}else {
displayString(makeLongName(player) + ": ERROR, not adjacent countries");
}
}
}
}

Okay - as currently written - every time you call attackOrSkip within the method - you end up 1 level lower in the stack - with a new set of variables -
attackFinished, numUnitsAttackWith, defenceArmiesNumber
When you leave the recursion (i.e. via a return) you end up with simple variables as they were before you enter the recursive call - remember Java is Call By Value (even though you can pass in references to objects, you get a value (the current value of the variable reference when called ... and changing the reference to point at a different object doesn't change the callers reference).
SO, without looking to see whether you have done the correct algorithm - I would guess that if you made the method return type a boolean and returned the status instead of nothing, you could update attackFinished and the right thing might happen..
e.g. change all the
return;
to
return attackFinished;
AND change all the places where you call
attackOrSkip(....)
to set attack finished based on what the method return
attackFinished = attackOrSkip(....)
OR - you can pass in an extra parameter - attackFinished - in a Holder (an example of the concept) object - again the reference can't change, but you can go attackFinished.value = true (and it will then be the same the whole way out the stack as you drop out of the recursion).

Related

Improving methods in Java

I have recently started a course where the main language we are learning at the moment is Java.
I have been tasked with creating a program that allows people to vote on two candidates - the program then counts the votes, and depending on how man votes have been made depends on what is displayed.
Here is the part I am concerned with at the moment:
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0)
{
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
}
else if(this.completed == false)
{
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
else if(this.completed == true)
{
System.out.println ("Voting has finished, no more votes will be allowed.");
return "Voting has finished, no more votes will be allowed";
}
{
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
}
Originally I used void in this method, but part of our task was to then change it to a string value. This is where I am struggling - once I set completed to true, it is still allowing me to cast votes. I know that this code is incomplete but I can't finish it as I am unsure what to do! These were the next parts to the questions.
Modify your printResults method so that it applies the first two rules. Note that the value of the completed field indicates whether or not voting is complete. The method should be modified to return a String which indicates whether printing has been successful.
Modify your vote method to apply the third rule.
Test your methods by creating an instance and doing the following – before
doing each test note the result you expect to get, and compare this with what you actually get:
• Try to print results immediately
• Cast votes for both candidates and try to print results
• Set the completed field to true by calling setCompleted
• Try to cast a vote for a candidate
• Print the results
I am new to this (this is my first year) and have managed to do okay in my books to get this far, however any help on this next issue would be greatly appreciated!
First of your code is unnecessary complicated, which makes it hard to read/enhance. It can easily simplified, like
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0) {
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
} // you returned ... NO need for ELSE!
if(this.completed == false) {
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
// it is very clear here that completed must be true!
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
Probably that easier-to-read code is all that you need to get you going!
Looking at the code the last block will never be reached because either you have no votes or you have votes and in that case completed will be either true or false and will thus reach always one of the else ifs and they all return a string. So I wonder why how you can cast any votes at all.
You could also post the code where you call printResults and setCompleted to see where the problem lies.
Some more hints for improving your code:
Sometimes you have the opening bracket on the same line and sometimes on the next. You should probably choose one style
It is not necessary to surround the last code block with brackets
if (this.completed == true) and else if (this.completed == false) is a bit redundant and can be written like: if (this.completed) and if (!this.completed). Also you can write
if (this.completed) {
...
} else {
....
}
because if completed is not true it can only be false.
Instead of writing every String two times and having to edit it two times in case you want to change something you could also do the following:
String msg = "Voting has not finished"
System.out.println(msg);
return msg;

Java Recursion game theory get best move

I have been tasked to write a function that will find the best move for the computer to make for part of a backtracking algorithm. My solution finds a winnable answer but not a best answer. I am having trouble figuring out a way to keep a value assigned to the different options that won't get reset during the next recursive call. So if it goes through moves 1,2,3,4 and 2 and 3 both lead to a winnable solution it will take 3 and not 2 even if 2 would be the better choice. I can see why in my code this happens but I can't seem to think through how to fix it. I tried with the wins and total wins variable but this doesn't seem to be working. So once again the function works to find a winnable avenue but won't always pick the best of the winnable moves. Any help would be much appreciated
Move bestMove = null;
int totalwins= 0;
public Move findbest(Game g) throws GameException {
int wins = 0;
PlayerNumber side = g.SECOND_PLAYER;
PlayerNumber opp = g.FIRST_PLAYER;
Iterator<Move> moves = g.getMoves();
while(moves.hasNext()){
Move m = moves.next();
//System.out.println(m + " Totalwins " + totalwins);
Game g1 = g.copy();
g1.make(m);
//System.out.println("Turn: " + g.whoseTurn());
if(!g1.isGameOver()){
bestMove = findbest(g1);
}else{
if(g1.winner() == side ){
bestMove = m;
wins++;
}else if(g1.winner() == opp){
wins--;
}
if(wins > totalwins){
totalwins += wins;
bestMove = m;
}
}
if(bestMove == null){//saftey so it won't return a null if there is no winnable move.
bestMove = m;
}
}
//System.out.println("Totalwins = " + totalwins);
return bestMove;
}
As stated in the comments, you need to have some sort of rating system to determine which move really is the best.
Then, make a global variable Move bestMove and, instead of having findBest return the "best move", simply have it check to see if the current move is a winning one and, if so, also check to see if its rating is better than that of the current bestMove. If both of these conditions are true, then assign the current move to bestMove.

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

Calculate Dice Roll from Text Field

QUESTION:
How can I read the string "d6+2-d4" so that each d# will randomly generate a number within the parameter of the dice roll?
CLARIFIER:
I want to read a string and have it so when a d# appears, it will randomly generate a number such as to simulate a dice roll. Then, add up all the rolls and numbers to get a total. Much like how Roll20 does with their /roll command for an example. If !clarifying {lstThen.add("look at the Roll20 and play with the /roll command to understand it")} else if !understandStill {lstThen.add("I do not know what to say, someone else could try explaining it better...")}
Info:
I was making a Java program for Dungeons and Dragons, only to find that I have come across a problem in figuring out how to calculate the user input: I do not know how to evaluate a string such as this.
I theorize that I may need Java's eval at the end. I do know what I want to happen/have a theory on how to execute (this is more so PseudoCode than Java):
Random rand = new Random();
int i = 0;
String toEval;
String char;
String roll = txtField.getText();
while (i<roll.length) {
check if character at i position is a d, then highlight the numbers
after d until it comes to a special character/!aNumber
// so if d was found before 100, it will then highlight 100 and stop
// if the character is a symbol or the end of the string
if d appears {
char = rand.nextInt(#);
i + #'s of places;
// so when i++ occurs, it will move past whatever d# was in case
// d# was something like d100, d12, or d5291
} else {
char = roll.length[i];
}
toEval = toEval + char;
i++;
}
perform evaluation method on toEval to get a resulting number
list.add(roll + " = " + evaluated toEval);
EDIT:
With weston's help, I have honed in on what is likely needed, using a splitter with an array, it can detect certain symbols and add it into a list. However, it is my fault for not clarifying on what else was needed. The pseudocode above doesn't helpfully so this is what else I need to figure out.
roll.split("(+-/*^)");
As this part is what is also tripping me up. Should I make splits where there are numbers too? So an equation like:
String[] numbers = roll.split("(+-/*^)");
String[] symbols = roll.split("1234567890d")
// Rough idea for long way
loop statement {
loop to check for parentheses {
set operation to be done first
}
if symbol {
loop for symbol check {
perform operations
}}} // ending this since it looks like a bad way to do it...
// Better idea, originally thought up today (5/11/15)
int val[];
int re = 1;
loop {
if (list[i].containsIgnoreCase(d)) {
val[]=list[i].splitIgnoreCase("d");
list[i] = 0;
while (re <= val[0]) {
list[i] = list[i] + (rand.nextInt(val[1]) + 1);
re++;
}
}
}
// then create a string out of list[]/numbers[] and put together with
// symbols[] and use Java's evaluator for the String
wenton had it, it just seemed like it wasn't doing it for me (until I realised I wasn't specific on what I wanted) so basically to update, the string I want evaluated is (I know it's a little unorthodox, but it's to make a point; I also hope this clarifies even further of what is needed to make it work):
(3d12^d2-2)+d4(2*d4/d2)
From reading this, you may see the spots that I do not know how to perform very well... But that is why I am asking all you lovely, smart programmers out there! I hope I asked this clearly enough and thank you for your time :3
The trick with any programming problem is to break it up and write a method for each part, so below I have a method for rolling one dice, which is called by the one for rolling many.
private Random rand = new Random();
/**
* #param roll can be a multipart roll which is run and added up. e.g. d6+2-d4
*/
public int multiPartRoll(String roll) {
String[] parts = roll.split("(?=[+-])"); //split by +-, keeping them
int total = 0;
for (String partOfRoll : parts) { //roll each dice specified
total += singleRoll(partOfRoll);
}
return total;
}
/**
* #param roll can be fixed value, examples -1, +2, 15 or a dice to roll
* d6, +d20 -d100
*/
public int singleRoll(String roll) {
int di = roll.indexOf('d');
if (di == -1) //case where has no 'd'
return Integer.parseInt(roll);
int diceSize = Integer.parseInt(roll.substring(di + 1)); //value of string after 'd'
int result = rand.nextInt(diceSize) + 1; //roll the dice
if (roll.startsWith("-")) //negate if nessasary
result = -result;
return result;
}

Java Error When attempting to use the return from a method as an item in an if statement

I keep getting the following errors:
Cannot find symbol
Variable find
cannot find symbol
method getdata(int)
I am sure I am making this way more difficult than it is, but I am not sure how make this work so that the return from searching through the array, can be seen and evaluated by the if statement.
//assigns manager identification
manID = keyboard.nextInt();
//Fibonacci binary array for passwords
int[] passWArray = {00000000,00000001,00000001,00000010,00000011,00000101,00001000,00001101};
//item = find.getdata(manID);
if (getdata(manID) != -1)
{
//Do work here
dblPayRate = 10.85;
dblGrossPay = (intHours * dblPayRate) + (15.00);
dblTaxes = dblGrossPay * 0.19;
dblGrossPay -= dblTaxes;
//Print information to user
System.out.print("\n\n$" + df2.format(dblTaxes) +
" was withheld from this paycheck in taxes after working "+ intHours + " hours.\n\n");
System.out.print("The amount \"Employer Here\" owes you is $" + df2.format(dblGrossPay) + "\n");
}
else
{
// Dialog box for incorrect password
JOptionPane.showMessageDialog(null, "Invalid Entry! Contact the BOFH!");
//exits program (Note: needed for any JOptionPane programs)
System.exit(0);
}
}// end of long if statement for >50 hours
}//end of main method
public int find(int[] passWArray, int manID)
{
//search for manID in passWArray array
for (int index = 0; index < passWArray.length; index++)
if ( passWArray[index] == manID )
return manID;
//-1 indicates the value was not found
return -1;
}// end of find method
Change
if (getdata(manID) != -1)
into
if (find(passWArray , manID) != -1)
BTW those numbers don't magically become binary because they only contain 0's and 1's. Here's a hint:
int thirteen = Integer.parseInt("00001101", 2)
EDIT: in response to your next error
For now make the method static:
public static int find(int[] passWArray, int manID)
Eventually you might want to think about your 'Object-Oriented design' and just use the main() method as an entry point. Within main you create an instance of a class and let it do its work. In this way you can use the powers of O-O like encapsulation and inheritance and don't have to make everything static.
EDIT2: Afterthought
Your program seems to have the following 'actions':
user interaction
authentication
calculation
And there seem to be the following 'things' in your domain:
user
password
keyboard
display (command line and screen)
calculation
A good rule of thumb for an O-O design is to convert some of the 'things' and 'actions' already present in your domain into classes. A good class has a single responsibility and shares as little as possible of its data and methods with other classes (this is called information hiding).
Here's a class diagram that comes to mind:
User (represents a user, contains a single field 'password')
Authenticator (authenticates a user, contains the list of allowed passwords)
Console (all user interaction, either use System.out/in or Swing, but don't mix them)
Calculator (it calculates shit)

Categories

Resources