Trying to reduce/optimize if/else block to make more readable - java

I have the following block of code. Basically it determines in baseball if a player gets a number of bases, what happens based on if someone is on 1st/2nd/3rd. I did this out brute force but is there a way to rewrite this to drastically reduce the line count? I haven't been able to come up with a good way to do it.
int runsScored = 0;
switch (action) {
case 0:
break;
case 1:
if (third) {
if (second) {
if (first) {
runsScored = 2;
second = false;
} else {
runsScored = 2;
first = true;
second = false;
third = false;
}
} else {
if (first) {
runsScored = 1;
second = true;
third = false;
} else {
runsScored = 1;
first = true;
third = false;
}
}
} else {
if (second) {
if (first) {
third = true;
} else {
first = true;
}
} else {
if (first) {
second = true;
} else {
first = true;
}
}
}
break;
case 2:
if (third) {
if (second) {
if (first) {
runsScored = 2;
first = false;
} else {
runsScored = 2;
third = false;
}
} else {
if (first) {
runsScored = 1;
second = true;
} else {
runsScored = 1;
second = true;
third = false;
}
}
} else {
if (second) {
if (first) {
runsScored = 1;
third = true;
second = true;
first = false;
} else {
runsScored = 1;
}
} else {
if (first) {
first = false;
second = true;
third = true;
} else {
second = true;
}
}
}
break;
case 3:
if (third) {
if (second) {
if (first) {
runsScored = 3;
first = false;
second = false;
third = true;
} else {
runsScored = 2;
second = false;
}
} else {
if (first) {
runsScored = 2;
first = false;
} else {
runsScored = 1;
}
}
} else {
if (second) {
if (first) {
runsScored = 2;
third = true;
second = false;
first = false;
} else {
runsScored = 1;
second = false;
third = true;
}
} else {
if (first) {
runsScored = 1;
first = false;
second = false;
third = true;
} else {
third = true;
}
}
}
break;
case 4:
if (third) {
if (second) {
if (first) {
runsScored = 4;
first = false;
second = false;
third = false;
} else {
runsScored = 3;
second = false;
third = false;
}
} else {
if (first) {
runsScored = 3;
first = false;
third = false;
} else {
runsScored = 2;
third = false;
}
}
} else {
if (second) {
if (first) {
runsScored = 3;
third = false;
second = false;
first = false;
} else {
runsScored = 2;
second = false;
third = false;
}
} else {
if (first) {
runsScored = 2;
first = false;
second = false;
third = false;
} else {
runsScored = 1;
}
}
}
break;
default:
throw new AssertionError();
}
return runsScored;

Well I'd be describing this with class and letting it manage itself, but a good first step.
if (third) {
if (second) {
if (first) {
runsScored = 2;
second = false;
} else {
runsScored = 2;
first = true;
second = false;
third = false;
}
if (third) {
if (second) {
runsScored = 2;
second = false;
if (!first) {
first = true;
third = false;
}
i.e. factor out the same code on either side of the else. Once you've cleared out some of the undergrowth, something simpler may become visible.

I'd do it with bitwise operators (shifts and bitwise and)
(see http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html)
int runsScored = 0;
int action=1;
boolean first = false;
boolean second = true;
boolean third = true;
// Encode the bases in a bitstring.
int onbases = 0;
onbases += third ? 1 : 0;
onbases <<= 1;
onbases += second ? 1 : 0;
onbases <<= 1;
onbases += first ? 1 : 0;
onbases <<= 1;
// Bitmasks for use in the loop.
int homeplate = 16;
int basesloaded = 2+4+8;
onbases += 1; // Represent the batter who hasn't yet left the plate.
for (int i=0; i<action; i++) {
onbases <<= 1;
if ((onbases & homeplate) > 0) {
runsScored += 1;
}
}
// Remove the "players" who crossed home plate.
onbases &= basesloaded;
// onbases now reflects who's on base *after* the run.
// Decode the bitstring.
first = (onbases & 2) > 0;
second = (onbases & 4) > 0;
third = (onbases & 8) > 0;
Integer.toBinaryString(int) is useful for debugging, and if you use these operators, I find it helpful to go heavy on parentheses, because order-of-operations sometimes doesn't fall out intuitively.
Edit: What I posted earlier forgot to put the batter on base. I believe it's fixed.

Assuming your action variable is what base the hitter will end up on, and first, second, third are booleans for batters on base:
Instead of checking each boolean, perhaps transform them into a single array. For example, a runner on first and third looks like [1,0,1]. A runner on second and third looks like [0,1,1]. Then you can write a function that takes in your action variable and the array of bases and returns the number of runs and the new bases array.
This could certainly remove the nested statements from your case statement, but maybe you can think of a way to make this work better than nested ifs?

A shorter version for the booleans (only):
boolean new_first = (action == 1) || (action == 2) && (first &!second & third);
boolean new_second = (action == 2) || (action == 1
&& (first && !(second && third))
|| (!first && second && !third));
boolean new_third = (action == 3)
|| (action == 2 && first)
|| (action == 1 && (first && second));
The runsScored part is harder.

Related

Experiencing issues with overwriting previous instance of ArrayList object initialization

EDIT: I may have fixed it by making the instance variable static? If so, why does this fix it? This was something my prof glossed over in my intro to OOP class, so I never really learned about it.
so, I'm not sure if I'm using terrible coding practice and just don't realize it, but I'm having a single crippling issue with my program, despite this one issue, everything appears to be running fine.
This program takes strings from an argument file and executes the commands based on the instructions. However, when I issue the command "x = x + 5" I experience a crippling issue.
My class titled Work creates another instance of my other class Read. When it does this, the ArrayList that I define at the top of Read is overwritten with a new instance, and thus, deletes the whole list of variables. I'm unable to find a way to make the initialization execute only once. Where am I going wrong?
Work:
import java.util.*;
public class Work {
static int Precedence(char ch){
switch (ch){
case '+':
case '-':
return 1;
case '*':
case '/':
case '%':
return 2;
case '^':
return 3;
}
return -1;
}
String infixConverter(String infix){
Variable tvar = new Variable(); //consider removing these~
Read reader = new Read();
String pfix = new String("");
Stack<Character> stack = new Stack<>();
for (int i=0; i<infix.length(); ++i) {
char curr = infix.charAt(i);
// if (Character.isLetterOrDigit(curr)||curr==' ') {
if (Character.isDigit(curr)||curr==' ') {
pfix += curr;
}
else if (Character.isLetter(curr)){
// Read reader = new Read();
if (reader.varExists(curr)){
// Variable tvar = new Variable();
int n = reader.getIndex(curr);
tvar = reader.vars.get(n);
pfix += tvar.getValue();
}
else{
//error
}
//implement this below
//check for letter within the string, if the string is
}
else if (curr == '('){
stack.push(curr);
}
else if (curr == ')'){
while (!stack.isEmpty() && stack.peek() != '(') {
pfix += stack.pop();
}
if (!stack.isEmpty() && stack.peek() != '(') {
return "Invalid Expression"; // invalid expression
}
else {
stack.pop();
}
}
else {
while (!stack.isEmpty() && Precedence(curr) <=
Precedence(stack.peek())) {
pfix += stack.pop();
}
stack.push(curr);
}
}
while (!stack.isEmpty()) {
pfix += stack.pop();
}
int sol = postfixEvaluation(pfix);
pfix = Integer.toString(sol);
return pfix;
}
Integer postfixEvaluation(String pfix){
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < pfix.length(); i++){
char curr = pfix.charAt(i);
if(curr == ' '){
continue;
}
else if(Character.isDigit(curr)){
int num = 0;
while(Character.isDigit(curr)){
num = num*10 + (int)(curr-'0');
i++;
if (i==pfix.length()){break;}
curr = pfix.charAt(i);
}
i--;
stack.push(num);
//include variables and be able to insert them into here
}
else{
int val1 = stack.pop();
int val2 = stack.pop();
switch(curr){
case '+':
stack.push(val2+val1);
break;
case '-':
stack.push(val2-val1);
break;
case '/':
stack.push(val2/val1);
break;
case '%':
stack.push(val2%val1);
break;
case '*':
stack.push(val2*val1);
break;
}
}
}
return stack.pop();
}
}
Read:
import java.util.*;
public class Read {
ArrayList<Variable> vars = new ArrayList<Variable>();//FIX THIS SHIT AHHHHHHHHH
Variable tempvar = new Variable();
Work eval = new Work();
private int lnNum = 1;
private String arg="";
public void thing() {
String thing,thing2;
if (arg.contains("print")){
String real="";
int i = arg.indexOf('t');
if (i!=4){}//error
thing = arg.substring(0,i);
if (thing!="print"){}//error
thing2 = arg.substring(i+2,arg.length());
real = eval.infixConverter(thing2);
System.out.println(real);
}
else if (arg.contains("read")){
Scanner in = new Scanner(System.in);
int i = arg.indexOf('d');
if (i!=3){}//error
thing = arg.substring(0,i+1);
if (thing!="read"){}//error
thing2 = arg.substring(i+2,arg.length());
char c2 = thing2.charAt(0);
if (thing2.length()>1){}//error
System.out.println("Enter a value for "+thing2+": ");
String vv = in.nextLine();
vv = eval.infixConverter(vv);
int vvv = Integer.parseInt(vv);
tempvar.setValue(c2, vvv);
vars.add(tempvar);
}
else if (arg.contains("=")){
int i = arg.indexOf('=');
int tempval;
thing = arg.substring(0,i-1);
thing2 = arg.substring(i+2,arg.length());
if(thing.length()>1){}//error
thing2=eval.infixConverter(thing2);
char c = thing.charAt(0);
if (vars.contains(thing)){
tempval = Integer.parseInt(thing2);
tempvar.setValue(c,tempval);
int f = vars.indexOf(thing);
}
else if(Character.isLetter(c)){
tempval = Integer.parseInt(thing2);
tempvar.setValue(c,tempval);
vars.add(tempvar);
}
else{
//error
}
}
else{
}
}
boolean varExists(char c){
for (int i = 0; i<vars.size(); i++){
tempvar = vars.get(i);
if (tempvar.getTitle()==c){
return true;
}
}
return false;
}
int getIndex(char c) {
for (int i = 0; i <= vars.size(); i++) {
tempvar = vars.get(i);
if (tempvar.getTitle() == c) {
return i;
}
}
return -1;
}
int inLineNum(){
lnNum++;
return lnNum;
}
void setArg(String c){
arg=c;
}
}

Connect 4 right to left diagonal check

There's probably a very simple solution to this but I can't figure out where I'm doing something wrong. I'm making a small game with android studio that is connect 4. There is a 5x7 matrix of single cells, and 5 imageviews above that when clicked on put a fiche in the right place. Up to this point it's all working fine. When I however have to check if someone won, I thought I could break up the process in 4 main functions: one that check horizontally, one vertically, one diagonally left to right and one diagonally right to left. Now they all work perfectly except the right to left one. I'll post the code below:
private void checkRightLeftDiagonally() {
int winCondition = 0;
boolean goingRight = true;
int y = 1;
int i = 4;
int j = 0;
while (y < 6 && won == false) {
while (i > 0 && j < 7 && won == false) {
if (cells[j][i].getFull() == true && players[playerTurn].getFicheColor() == cells[j][i].getFicheColor()) {
winCondition++;
winningCells.add(cells[j][i]);
} else {
winCondition = 0;
winningCells.clear();
}
if (winCondition == 4) {
won = true;
for (int x = 0; x < 4; x++) {
winningCells.get(x).won();
}
}
i--;
j++;
}
if(goingRight == true)
{
if(y<=4)
{
i=4-y;
j=0;
y++;
}
else
{
goingRight = false;
y=0;
i=0;
j=0+y;
}
}
if(goingRight == false)
{
i=0;
j=0+y;
y++;
}
if(won == false)
{
winCondition = 0;
winningCells.clear();
}
}
if(won == false) {
winCondition = 0;
winningCells.clear();
}
}
And here is one of the arrow imageview code:
imgArrows[0].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(cells[0][0].getFull() == false && won == false)
{
int i = 0;
while(cells[i][0].getFull() == false)
{
i++;
if(i>6) break;
}
i--;
cells[i][0].ficheDown(players[playerTurn]);
checkVertically();
checkHorizantally();
checkLeftRightDiagonally();
checkRightLeftDiagonally();
playerTurn++;
if(playerTurn==2)
{
playerTurn = 0;
}
}
}
});
I've also made the cell class, which is here if it could help you
public class Cell {
private boolean full;
private Player.FicheColor ficheColor;
private ImageView fiche;
public Cell(Player currentPlayer, ImageView img)
{
full = false;
ficheColor = currentPlayer.getFicheColor();
fiche = img;
img.setAlpha(0f);
}
public void ficheDown(Player currentPlayer)
{
full = true;
ficheColor = currentPlayer.getFicheColor();
switch(ficheColor)
{
case red:
fiche.setImageResource(R.drawable.redfiche);
break;
case blue:
fiche.setImageResource(R.drawable.bluefiche);
break;
case green:
fiche.setImageResource(R.drawable.greenfiche);
break;
case white:
fiche.setImageResource(R.drawable.whitefiche);
break;
case black:
fiche.setImageResource(R.drawable.whitefiche);
break;
}
fiche.setAlpha(1f);
}
public Player.FicheColor getFicheColor()
{
return ficheColor;
}
public boolean getFull()
{
return full;
}
public void won(){
fiche.setColorFilter(Color.GREEN);
}
public void reset()
{
fiche.clearColorFilter();
}
}
Thank a lot, even just for reading
In the end the problem was that in the first paragraph of code the int i needed to be set to 4 and that solved it. Thanks to everyone that tried to help me

What's wrong with my simple Java program?

It's just supposed to print the prime numbers below 100 but it only gets the number '3' as an output. I'm only just starting to learn Java so it all looks right to me.
public class ClassesAndObjects {
public static void main(String[] args) {
Prime n = new Prime();
for (int i = 3; i < 100; i++){
n.Number = i;
n.factors();
}
}
}
class Prime{
long Number;
long fact;
boolean state = true;
void factors(){
for (fact = 2; fact < Number; fact++){
if (fact != Number){
if (Number % fact == 0){
state = false;
break;
}
}
}
if (state == true){
System.out.println(Number);
}
}
}
You have to reset the boolean state to true at the beginning of each call, otherwise it's always false except for the first call (when i =3 )
void factors(){
state = true;
for (fact = 2; fact < Number; fact++){
if (fact != Number){
if (Number % fact == 0){
state = false;
break;
}
}
}
if (state == true){
System.out.println(Number);
}
}
Add a statement like this:
if (state == true){
System.out.println(Number);
}
state = true; //reset the state variable
Here we are resetting the state variable to true for next iteration.
Well, you dont have state = true as default in your factor() method. So when it runs state = false for the first time (happens when Number = 4), then it is always false. Just add the bolded line in your code and you are good to go.
void factors(){
**boolean state = true;**
for (fact = 2; fact < Number; fact++){
if (fact != Number){
if (Number % fact == 0){
state = false;
break;
}
}
}
if (state == true){
System.out.println(Number);
}
}

binary search between two user defined points, returning all names inbetween those two entered

In java, I have to do a binary search, asking the user to select two names that exist in my list and then print all the people between those two, starting at the first name they enter and ending at the second name they enter. I start by calling the method searchTwoPeople. It's returning nothing.... Here's my code:
public int binarySearch(String searchItem)
{
int first = 0;
int last = nMembers - 1;
int mid = 0;
boolean found = false;
while (first <= last && !found)
{
mid = (first + last) / 2;
if (bookMembers[mid].lastName.compareTo(searchItem) == 0)
found = true;
else if (bookMembers[mid].lastName.compareTo(searchItem) > 0)
last = mid - 1;
else
first = mid + 1;
}
if (!found)
mid = -1; //it is an unsuccessful search
return mid;
}//end binarySearch
public int findFirstPosition(int position)
{
int newPosition = position;
if (position == 0)
{
return position;
}
while (true)
{
if (bookMembers[newPosition-1].lastName.compareTo(bookMembers
[position].lastName) != 0)
{
return newPosition;
}
else
{
--newPosition;
if (newPosition == 0)
{
return newPosition; // no more people in list
}
}
// end sequential search
}
}
public void searchTwoPeople()
{
String lastName = new String();
String lastName2 = new String();
int position, nextPosition;
int tryCount = 0;
boolean more = true;
do
{
lastName = inputFirstLastName();//this is the
method where I have user input a name where to start search and it's passed in here
lastName2 = inputSecondLastName(); //user puts
name where to end search
position = binarySearch(lastName);
if (position == -1)
{
System.out.println ("\n You have no
contacts with the name " + lastName);
++ tryCount;
}
}
while (position == -1 && tryCount < 3);// only let them try
3 times....
if (tryCount == 3)
{
System.out.println("Only allowed 3 attempts -
select a new menu option");
return;
}
nextPosition = findFirstPosition(position);
while (more)
{
System.out.println(bookMembers
[nextPosition].toString());
}
++ nextPosition; // check next person
if (nextPosition == nMembers) // last person in
the list
{
more = false;
}
else
{
if (bookMembers
[nextPosition].lastName.compareTo(lastName2) == 0)
{
more = false;
}
}
}

Why do I get "Cannot be Resolved" errors in this code?

I'm trying to check if a move is legal for the game Othello. I'm using a for each loop and once (only once) when I use the variable that it's checking for, it gives me a 'cannot be resolved error'. I have bolded where this occurs. Can anyone help? (Yes I know that the code isn't finished yet, I'm trying to get rid of this error first.)
public boolean isLegal(Location loc1)
{
String currentColor = currentPlayer.getColor();
boolean isLegal = false;
int row = loc1.getRow();
int col = loc1.getCol();
if(board.isValid(loc1))
{
if(board.get(loc1) == null)
{
for(Location tempLoc : board.getValidAdjacentLocations(loc1))
{
if(!board.get(tempLoc).equals(currentColor))
{
int tempRow = tempLoc.getRow();
if((row != tempLoc.getRow()) && (col == tempLoc.getCol()))
{
//count up column
if(**tempLoc.getRow()** < row)
{
for(int i = row; i > 1;)
{
Location tempLoc2 = new Location(i-2, col);
if(!board.get(tempLoc2).equals(currentColor))
{
i--;
}
else
{
i=-1;
isLegal = true;
}
}
}
//count down column
else
{
for(int i = row; i < 6;)
{
Location tempLoc2 = new Location(i+2, lcol);
if(!board.get(tempLoc2).equals(currentColor))
{
i++;
}
else
i=9;
isLegal = true;
}
}
}
else if(col != tempLoc.getCol() && row == tempLoc.getRow())
{
//count left/right row
if(col > tempLoc.getCol())
{
}
else
}
else
{ //count up/right & down/left diag
if(1!=0)
{
}
//count up/left & down/right diag
else
}
}
}
}
}
return isLegal;
}
The "else" statements without a body at the bottom of your code are confusing the compiler about what counts as inside the loops. If you fix that error, the other one will go away.

Categories

Resources