Is there a way to refactor these if statements? - java

just wondering if there's a way to refactor the below code? I'm new to Java and trying to have DRY code - the below I've written but seems like a lot of conditionals to check
void printDirection() {
if (yDirection > 0) {
if (xDirection < 0) {
println("Travelling South-West");
} else {
println("Travelling South-East");
}
} else if (yDirection < 0) {
if (xDirection <0) {
println("Travelling North-West");
} else {
println("Travelling North-East");
}
}
}
Thanks in advance for any help!

You can evaluate the north/south and the east/west conditions individually, and glue the directions into your message.
System.out.printf("Travelling %s-%s%n", (yDirection < 0 ? "North" : "South"),
(xDirection < 0 ? "West" : "East"));
I assume from the code in your question that you're only concerned about those four complementary directions (not due north, due east, stationary etc.).

If you really want to make it DRY, it can be done using the operator ? but It's neither easy to read nor recommanded. It's used in programming contest where the goal is to go as fast as possible.
It follows the scheme :
(Condition?WhatHappenIfConditionIsTrue:WhatHappenIfConditionIsFalse);
You can use it in assignment :
int i = (a>0)?a:0;
in that case, if a>0 then i=a, else a=0
In your case, I would do it like that
void printDirection()
{
System.out.println("Travelling " + (yDirection > 0?"South":"North") + "-" + (xDirection>0?"East":"West"));
}

Some suggestiones:
1. Due to x,y combination; there are five states; you can use enum type to define these status;
2. If you want to reduce if...else statementes in your code, please refer to Status Machine Design Pattern; but i think, under your case, the status is so simple, do not need to make it too complicated
public class Status {
public enum Direction {
SOUTH_WEST((x, y) -> y > 0 && x < 0, "Travelling South-West")
, SOUTH_EAST((x, y) -> y >0 && x > 0, "Travelling South-East")
, NORTH_EAST((x, y) -> x > 0 && y < 0, "Travelling North-East")
, NORTH_WEST((x,y) -> x < 0 && y < 0, "Travelling North-West"), CENTER((x,y) -> x == 0 && y == 0, "");
BiPredicate<Integer, Integer> bp;
String desc;
public BiPredicate<Integer, Integer> getBp() {
return bp;
}
public void setBp(BiPredicate<Integer, Integer> bp) {
this.bp = bp;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
private Direction(BiPredicate<Integer, Integer> bp, String desc) {
this.bp = bp;
this.desc = desc;
}
public static Direction getDirection(int x, int y) {
for (Direction direction : Direction.values()) {
if(direction.getBp().test(x, y)) {
return direction;
}
}
return null;
}
}
public static void main(String[] args) {
Direction d = Direction.getDirection(3, 4);
System.out.println(d.getDesc());
/* if(d == Direction.SOUTH_WEST){
System.out.println("do some thing");
} else if(d == Direction.SOUTH_EAST){
System.out.println("do some thing");
} else if(d == Direction.NORTH_EAST){
System.out.println("do some thing");
} else if(d == Direction.NORTH_WEST){
System.out.println("do some thing");
}*/
}
}

Related

A* Pathfinding really slow

I have an A* pathfinding algorithm that I've used for a Spigot plugin which worked fine. I then added a requirements system so it won't try and pathfind through places it shouldn't. Now it seems REALLY slow, and it looks like it has nothing to do with the requirements code itself, and more to do with the algorithm having many more incorrect paths when calculating. I seem to be getting 1500ms+ on this, which is definitely not good xD
Here is the pathfinder code:
public Path calculate(PathfinderGoal goal, PathScorer scorer, List<PathRequirement> requirements, int maxNodes) {
PathNode start = toNode(npc.getLocation());
PathNode end = toNode(goal.getLocation());
List<PathNode> open = new ArrayList<>() {{ add(start); }};
List<PathNode> navigated = new ArrayList<>();
start.setF(scorer.computeCost(start, end));
Timer timer = new Timer().start();
while (!open.isEmpty()) {
PathNode current = null;
for (PathNode node : open) {
if (current == null || node.getH() < current.getH()) {
current = node;
}
}
if (scorer.computeCost(current, end) < 1 || (navigated.size() >= maxNodes && maxNodes != -1)) {
navigated.add(navigated.size() < maxNodes ? end : current);
return reconstruct(navigated, navigated.size() - 1);
}
open.remove(current);
current.close();
for (PathNode node : current.getNeighbors()) {
if (node.isClosed()) {
continue;
}
double tentG = current.getG() + scorer.computeCost(current, node);
if (!open.contains(node) || tentG < node.getG()) {
boolean requirementsMet = true;
for (PathRequirement requirement : requirements) {
requirement.setNavigated(navigated);
if (!navigated.isEmpty() && !requirement.canMoveToNewNode(navigated.get(navigated.size() - 1), node)) {
requirementsMet = false;
break;
}
}
if (!navigated.contains(current)) {
navigated.add(current);
}
node.setG(tentG);
node.setH(scorer.computeCost(node, end));
node.setF(tentG + node.getH());
if (!open.contains(node) && requirementsMet) {
open.add(node);
}
}
}
Bukkit.broadcastMessage("Open Set Size: " + open.size());
Bukkit.broadcastMessage(timer.stop() + "ms");
}
return null;
}
private Path reconstruct(List<PathNode> navigated, int index) {
final PathNode current = navigated.get(index);
Path withCurrent = new Path(new ArrayList<>() {{ add(current); }});
if (index > 0 && navigated.contains(current)) {
return reconstruct(navigated, index - 1).append(withCurrent);
}
return withCurrent;
}
And here is the PathNode class:
public PathNode(Pathfinder pathfinder, int x, int y, int z) {
this.pathfinder = pathfinder;
this.x = x;
this.y = y;
this.z = z;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof PathNode otherNode)) {
return false;
}
return otherNode.x == x && otherNode.y == y && otherNode.z == z;
}
public List<PathNode> getNeighbors() {
return new ArrayList<>() {
{
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
add(new PathNode(pathfinder, PathNode.this.x + x, PathNode.this.y + y, PathNode.this.z + z));
}
}
}
}
};
}
public Location getLocation() {
return new Location(pathfinder.getNPC().getLocation().getWorld(), x, y, z);
}
public double getF() {
return F;
}
public void setF(double f) {
this.F = f;
}
public double getG() {
return G;
}
public void setG(double g) {
this.G = g;
}
public double getH() {
return H;
}
public void setH(double h) {
this.H = h;
}
public boolean isClosed() {
return closed;
}
public void close() {
this.closed = true;
}
Valid Requirements Class:
public class ValidPathRequirement extends PathRequirement {
#Override
public boolean canMoveToNewNode(PathNode from, PathNode to) {
Block fromBlock = from.getLocation().getBlock();
Block toBlock = to.getLocation().getBlock();
boolean validHeight = toBlock.getType().isAir() && toBlock.getRelative(BlockFace.UP).getType().isAir(); // checks if is player height
boolean validGround = toBlock.getRelative(BlockFace.DOWN).getType().isSolid(); // is there a block underneath that they can stand on?
boolean validFromPrev = toBlock.getLocation().subtract(fromBlock.getLocation()).getY() <= 1; // is it max one block higher than the last one?
// is this one causing issues?
Location fromLocDist = from.getLocation().clone();
Location toLocDist = to.getLocation().clone();
toLocDist.setY(fromLocDist.getY());
boolean validDistance = fromLocDist.distance(toLocDist) <= 1;
return validHeight && validGround && validFromPrev;
}
}
Without looking at the rest of the algorithm, the first thing that stands out is that your data structures are incorrect. The "open" list needs to be a Priority Queue, and "closed" (or "navigated") should be a set.

BukkitRunnable gives me a NPE

Basically, this code works as I need it to, perfectly fine, however, If I move even the slightest bit or get out of the water, my console gets SPAMMED with NPE's... What have I done wrong with my code? What I'm trying to do is, if a player is in a specific location, inside of water, it will start a countdown, in this case, of 5 seconds. And after the 5 seconds, it's meant to say "Testing", etc. That part works, just not when I move or step out of the water. And yes, I'm aware that the code is sloppy and a bunch of junk but I don't want to create a whole class for cuboid for just one event.
private HashMap<UUID, Integer> afkCountdown;
private HashMap<UUID, BukkitRunnable> afkCountdownTask;
#EventHandler
public void onPlayerMoveEvent(PlayerMoveEvent e) {
afkCountdown = new HashMap<UUID, Integer>();
afkCountdownTask = new HashMap<UUID, BukkitRunnable>();
Player p = e.getPlayer();
int x = 52;
int y = 80;
int z = 255;
int x1 = 72;
int y1 = 100;
int z1 = 275;
Location l1 = new Location(Bukkit.getWorld("Void"), x, y, z);
Location l2 = new Location(Bukkit.getWorld("Void"), x1, y1, z1);
Location loc = new Location(Bukkit.getWorld("Void"), p.getLocation().getBlockX(), p.getLocation().getBlockY(), p.getLocation().getBlockZ());
if (p.getWorld().equals(loc.getWorld())) {
if (Objects.requireNonNull(e.getTo()).getBlock().isLiquid()) {
if (loc.getX() > l1.getX() && loc.getX() < l2.getX()) {
if (loc.getY() > l1.getY() && loc.getY() < l2.getY()) {
if (loc.getZ() > l1.getZ() && loc.getZ() < l2.getZ()) {
if (!afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.put(p.getUniqueId(), 5);
afkCountdownTask.put(p.getUniqueId(), new BukkitRunnable() {
#Override
public void run() {
afkCountdown.put(p.getUniqueId(), afkCountdown.get(p.getUniqueId()) - 1);
if (afkCountdown.get(p.getUniqueId()) == 0) {
p.sendMessage("Testing");
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
afkCountdown.put(p.getUniqueId(), 5);
} else if (!afkCountdown.containsKey(p.getUniqueId())) {
cancel();
}
}
});
afkCountdownTask.get(p.getUniqueId()).runTaskTimer(plugin, 20, 20);
} else {
return;
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
} else {
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
afkCountdownTask.remove(p.getUniqueId());
} else {
return;
}
}
}
I have multiple things to say about this code :
The value of e.getTo() will never be null. So the Objects.requireNonNull code is a useless import.
You get multiple time the same world instance with Bukkit.getWorld("Void"), but only one time is better.
Instead of create Location's object, you can check directly the x/y/z values that are given just before.
For the issue :
When you left liquid, all task will remove the value linked with the player's uuid. So, you will not be able to get id with afkCountdown.get() or afkCountdownTask.get(). To fix it, you should use afkCountdown.getOrDefault(p.getUniqueId(), 0).
For timers, you never stop already started timer. So, your server with crash because of overflow. When you do afkCountdownTask.remove() you should do :
BukkitRunnable task = afkCountdownTask.remove(p.getUniqueId()); // return the removed one, or null if nothing removed
if(task != null) // if was existing
task.cancel(); // cancel task
You are creating and resetting hashmap EACH move with afkCountdown = new HashMap<UUID, Integer>(). You should do it only one time.
Finally, this is the code that I propose to fix everything :
private final HashMap<UUID, Integer> afkCountdown = new HashMap<>();
private final HashMap<UUID, BukkitRunnable> afkCountdownTask = new HashMap<>();
#EventHandler
public void onPlayerMoveEvent(PlayerMoveEvent e) {
Player p = e.getPlayer();
int x = 52;
int y = 80;
int z = 255;
int x1 = 72;
int y1 = 100;
int z1 = 275;
Location loc = e.getTo();
if (p.getWorld().getName().equals("Void")) {
if (loc.getBlock().isLiquid()) {
if (loc.getX() > x && loc.getX() < x1) {
if (loc.getY() > y && loc.getY() < y1) {
if (loc.getZ() > z && loc.getZ() < z1) {
if (!afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.put(p.getUniqueId(), 5);
afkCountdownTask.put(p.getUniqueId(), new BukkitRunnable() {
#Override
public void run() {
int amount = afkCountdown.getOrDefault(p.getUniqueId(), 0) - 1;
afkCountdown.put(p.getUniqueId(), amount);
if (amount <= 0) {
p.sendMessage("Testing");
afkCountdown.put(p.getUniqueId(), 5);
} else if (!afkCountdown.containsKey(p.getUniqueId())) {
cancel();
}
}
});
afkCountdownTask.get(p.getUniqueId()).runTaskTimer(Main.getInstance(), 20, 20);
}
return; // don't want to remove, so end method now
}
}
}
}
}
if (afkCountdown.containsKey(p.getUniqueId())) {
afkCountdown.remove(p.getUniqueId());
BukkitRunnable task = afkCountdownTask.remove(p.getUniqueId());
if(task != null)
task.cancel();
}
}

Peg solitaire solution change destination

I wrote a program that solves a peg solitaire in java.
My program gets a starting board and a destination board and try to finish the game.
I have a sort of counter that count my turn because I have a destination with more the 1 peg rest and as assume that if I have to remove only 2 pegs so I can solve it in only 2 moves.
I have an board class that I create:
public class Board {
private int board[][] = new int[7][7];
public Board(String place)
{
board[0][0]=2;
board[1][0]=2;
board[5][0]=2;
board[6][0]=2;
board[0][1]=2;
board[1][1]=2;
board[5][1]=2;
board[6][1]=2;
board[0][5]=2;
board[1][5]=2;
board[5][5]=2;
board[6][5]=2;
board[0][6]=2;
board[1][6]=2;
board[5][6]=2;
board[6][6]=2;
int loca=0;//location on the string of place
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
if(board[i][j]!=2) {
if (place.charAt(loca) == 'O') {
loca++;
board[i][j] = 1;
} else if (place.charAt(loca) == '.') {
loca++;
board[i][j] = 0;
}
}
System.out.print(board[i][j]);//print for test
}
System.out.println();//print for test
}
System.out.println();
}
public Board(Board copy){
for(int i=0;i<7;i++)
{
for(int j=0;j<7;j++)
{
board[i][j]=copy.getValue(i,j);
}
}
}
public int getValue(int x, int y)
{
return board[x][y];
}
public boolean isFinished(Board destination)
{
for(int i=0;i<7;i++)
{
for(int j=0;j<7;j++)
{
if(this.getValue(i,j)!=destination.getValue(i,j))
{
return false;
}
}
}
return true;
}
public Board turn(Board board,String direction,int x,int y)
{
if(direction.equals("right"))
{
board.setValue(x,y,0);
board.setValue(x+1,y,0);
board.setValue(x+2,y,1);
return board;
}
else if(direction.equals("left"))
{
board.setValue(x,y,0);
board.setValue(x-1,y,0);
board.setValue(x-2,y,1);
return board;
}
else if(direction.equals("up"))
{
board.setValue(x,y,0);
board.setValue(x,y-1,0);
board.setValue(x,y-2,1);
return board;
}
else if(direction.equals("down"))
{
board.setValue(x,y,0);
board.setValue(x,y+1,0);
board.setValue(x,y+2,1);
return board;
}
else{
System.out.println("there is not such direction, method turn on board class");
return null;//just for caution
}
}
public boolean isLegal(int x, int y){
if(board[x][y]==2)
{
return false;
}
else{
return true;
}
}
public boolean canTurn(String direction,int x,int y){
if(direction.equals("right"))
{
if(x<5) {
if (board[x][y] == 1 && board[x + 1][y] == 1 && board[x + 2][y] == 0) {
return true;
}
}
}
else if(direction.equals("left"))
{
if(x>1) {
if (board[x][y] == 1 && board[x - 1][y] == 1 && board[x - 2][y] == 0) {
return true;
}
}
}
else if(direction.equals("up"))
{
if(y>1) {
if (board[x][y] == 1 && board[x][y - 1] == 1 && board[x][y - 2] == 0) {
return true;
}
}
}
else if(direction.equals("down"))
{
if(y<5) {
if (board[x][y] == 1 && board[x][y + 1] == 1 && board[x][y + 2] == 0) {
return true;
}
}
}
else{
System.out.println("there is not such direction, method canTurn on board class");
return false;//just for caution
}
return false;
}
public void setValue(int x, int y, int value)
{
board[x][y]=value;
}
}
and I wrote my "solver" class.
public class PegSolver {
public int peg =1;
Board destinationBoard = new Board("OOOOOOOOOOOOOOOOO..OOOOOOOOOOOOOO");
Board board = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");
public void start(){
solve(0,board);
}
public boolean solve(int turn, Board board){
Board temp = new Board(board);
if(turn>peg)
{
return false;
}
else if(turn==peg){
//todo:check if solve
if(temp.isFinished(destinationBoard))
{
System.out.println("solution");
return true;
}
else
{
return false;
}
}
else//lower then 8
{
for(int i=0;i<7;i++){
for (int j=0;j<7;j++)
{
if(board.isLegal(i,j)) {
if(board.canTurn("right",i,j) && solve(turn++, temp.turn(temp, "right", i, j)))
{
return true;
}
else if(board.canTurn("left",i,j) && solve(turn++, temp.turn(temp, "left", i, j)))
{
return true;
}
else if(board.canTurn("up",i,j) && solve(turn++, temp.turn(temp, "up", i, j)))
{
return true;
}
else if(board.canTurn("down",i,j) && solve(turn++, temp.turn(temp, "down", i, j)))
{
return true;
}
}
}
}
}
return false;
}
}
When the program finds a solution, it needs to print "solution" but for some reason my program can't find a solution even when it's a basic destination with one move.
Can someone help me please?
Please review the modified code.
Many of the changes are related to indices and directions inconsistency across the code: where x represents horizontal index and y represents vertical index: array index should be board[y][x] (and not board[x][y]).
Many "magic numbers" were changed to constants for better readability of the code.
A toString method was added to the Boardclass to print out a board state. It uses special characters to make a nice printout :
This is helpful when debugging.
public class PegSolver {
private final Board startBoard, destinationBoard;
public PegSolver(Board startBoard, Board destinationBoard) {
super();
this.startBoard = startBoard;
this.destinationBoard = destinationBoard;
}
public void start(){
solve(0,startBoard);
}
private boolean solve(int turn, Board board){
//check if solve
if(board.isFinished(destinationBoard))
{
System.out.println("solved after "+ turn +" turns");
return true;
}
for(int x=0;x<board.boardSize();x++){
for (int y=0;y<board.boardSize();y++)
{
if(board.isLegal(x,y)) {
if(board.canTurn("right",x,y)
//turn++ changed to turn+1 so turn is incremented before invoking next solve
//and avoid changing the value of turn
&& solve(turn+1, board.turn(new Board(board), "right", x, y)))
return true;
else if(board.canTurn("left",x,y)
&& solve(turn+1, board.turn(new Board(board), "left", x, y)))
return true;
else if(board.canTurn("up",x,y)
&& solve(turn+1, board.turn(new Board(board), "up", x, y)))
return true;
else if(board.canTurn("down",x,y)
&& solve(turn+1, board.turn(new Board(board), "down", x, y)))
return true;
}
}
}
return false;
}
public static void main(String[] args) {
Board[] destinationBoards = {
//by order of number of turns
new Board("OOOOOOOOOOOOOO..OOOOOOOOOOOOOOOOO"), //one right turn
new Board("OOO.OOOO.OOOOO.OOOOOOOOOOOOOOOOOO"), //right, down
new Board("OOO.OO..OOOOOO.OOOOOOOOOOOOOOOOOO"), //right, down,right
new Board("OOO.OOO.OOOOO..OOOOO.OOOOOOOOOOOO"), //right, down,right,up
new Board("OOOOOOO..OOOO...OOOO.OOOOOOOOOOOO"), //right, down,right,up,up
new Board(".OO.OOO.OOOOO...OOOO.OOOOOOOOOOOO"), //right, down,right,up,up,down
new Board(".OO.OOO.OOOOO...OOOOO..OOOOOOOOOO"), //right, down,right,up,up,down, left
new Board(".OO.OOO.OOOOO...OOOOO.OOOOO.OO.OO"), //right, down,right,up,up,down,left,up
new Board(".OO.OO..O.OOO...OOOOO.OOOOO.OO.OO"), //10 turns
new Board("..O..O.O..OOO...OOOO..OOOOO..O..O"), //15 turns
new Board(".........O................O......"), //30 turns
new Board("...................O............."), //31 turns
};
Board startBoard = new Board("OOOOOOOOOOOOOOOO.OOOOOOOOOOOOOOOO");
for(Board destinationBoard : destinationBoards ){
new PegSolver(startBoard, destinationBoard).start();
}
}
}
class Board {
//int representation of the three states of a board cell
private final static int EMPTY = 0, PEG = 1, BORDER = 2;
/*cahr representation of the three states of a board cell
special chars are used to get nice printout
todo: change board to char[][] to avoid the need for two
representations (int and char)
*/
private final static char[] CHAR_REPRESENTATION = {9898,9899,10062};
private final static char ERROR = '?';
private final int BOARD_SIZE=7, CORNER_SIZE=2;
private final int board[][] = new int[BOARD_SIZE][BOARD_SIZE];
public Board(String place) {
int loca=0;
for(int y=0;y<BOARD_SIZE;y++){
for(int x=0;x<BOARD_SIZE;x++){
if(isWithinBoard(x,y)) {
if (place.charAt(loca) == 'O') {
loca++;
board[y][x] = PEG;
} else if (place.charAt(loca) == '.') {
loca++;
board[y][x] = EMPTY;
}
}else{
board[y][x] = BORDER;
}
}
}
//for testing
//System.out.println(this);
}
//copy constructor
public Board(Board copy){
for(int x=0;x<BOARD_SIZE;x++)
{
for(int y=0;y<BOARD_SIZE;y++)
{
board[y][x]=copy.getValue(x,y);
}
}
}
public int getValue(int x, int y)
{
return board[y][x]; //and not return board[x][y];
}
public boolean isFinished(Board destination)
{
for(int i=0;i<BOARD_SIZE;i++)
{
for(int j=0;j<BOARD_SIZE;j++)
{
if(this.getValue(i,j)!=destination.getValue(i,j))
return false;
}
}
return true;
}
public Board turn(Board board,String direction,int x,int y)
{
if(direction.equals("right"))
{
board.setValue(x,y,EMPTY);
board.setValue(x+1,y,EMPTY);
board.setValue(x+2,y,PEG);
return board;
}
else if(direction.equals("left"))
{
board.setValue(x,y,EMPTY);
board.setValue(x-1,y,EMPTY);
board.setValue(x-2,y,PEG);
return board;
}
else if(direction.equals("up"))
{
board.setValue(x,y,EMPTY);
board.setValue(x,y-1,EMPTY);
board.setValue(x,y-2,PEG);
return board;
}
else if(direction.equals("down"))
{
board.setValue(x,y,EMPTY);
board.setValue(x,y+1,EMPTY);
board.setValue(x,y+2,PEG);
return board;
}
System.out.println("there is not such direction, method turn on startBoard class");
return null;
}
public boolean isLegal(int x, int y){
if(board[y][x]==BORDER) //and not if(board[x][y]==BORDER)
return false;
return true;
}
public boolean canTurn(String direction,int x,int y){
if(direction.equals("right") && x < BOARD_SIZE - 2)
{
if (board[y][x] == PEG && board[y][x + 1] == PEG && board[y][x + 2] == EMPTY)
return true;
}
else if(direction.equals("left") && x > 1)
{
if (board[y][x] == PEG && board[y][x - 1] == PEG && board[y][x - 2] == EMPTY)
return true;
}
else if(direction.equals("up") && y > 1)
{
if (board[y][x] == PEG && board[y - 1][x] == PEG && board[y - 2][x] == EMPTY)
return true;
}
else if(direction.equals("down") && y < BOARD_SIZE - 2)
{
if (board[y][x] == PEG && board[y + 1][x] == PEG && board[y + 2][x] == EMPTY)
return true;
}
return false;
}
public void setValue(int x, int y, int value)
{
board[y][x]=value; //and not board[x][y]=value;
}
//for square nxn board
public int boardSize(){
return board.length;
}
public boolean isWithinBoard(int x, int y){
//check bounds
if (x < 0 || y < 0 || x >= BOARD_SIZE || y >= BOARD_SIZE) return false;
//left top corner
if (x < CORNER_SIZE && y < CORNER_SIZE) return false;
//right top corner
if(x >= BOARD_SIZE - CORNER_SIZE && y < CORNER_SIZE) return false;
//left bottom corner
if(x < CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;
//right bottom corner
if(x >= BOARD_SIZE - CORNER_SIZE && y >= BOARD_SIZE - CORNER_SIZE) return false;
return true;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int[] row : board) {
for(int cell : row){
if(cell<CHAR_REPRESENTATION.length && cell >= 0) {
sb.append(CHAR_REPRESENTATION[cell]);
}else{
sb.append(ERROR);
}
}
sb.append("\n"); //new line
}
return sb.toString();
}
}
Todo:
The code is working but it needs further in-depth testing and debugging.
If something is not clear, don't hesitate to ask.

Looking for a better java OO approach to a lot of Boolean if else statements

I was given this project by a friend who is coding in school, but I am trying to find a better way to code it. It is calling different Boolean methods, checking if true and adding to a count for latter use.
public static void testHand(PokerHand d) {
if (d.isRoyalFlush()) {
royalFlush++;
} else if (d.isStraightFlush()) {
straightFlush++;
} else if (d.is4OfAKind()) {
fourtOfAKind++;
} else if (d.isFullHouse()) {
fullHouse++;
} else if (d.isFlush()) {
flush++;
} else if (d.isStraight()) {
straight++;
} else if (d.is3OfAKind()) {
threeOfAKind++;
} else if (d.is2Pair()) {
twoPair++;
} else if (d.isPair()) {
pair++;
} else if(d.isHighCard()){
highCard++;
}
}
The code for PokerHand is as follows:
public class PokerHand {
private Card[] hand; // the hand of 5 cards
// the default constructor
public PokerHand() {
hand = new Card[5];
}
// A constructor to help with testing
public PokerHand(Card c0, Card c1, Card c2, Card c3, Card c4) {
hand = new Card[5];
hand[0] = c0;
hand[1] = c1;
hand[2] = c2;
hand[3] = c3;
hand[4] = c4;
}
/* This methods fills the hand with cards from the deck.
It uses an insertion sort so that the cards are ordered by rank.*/
public void fillHand(Deck deck) {
for (int i = 0; i < 5; i++) {
int j = i - 1;
Card temp = deck.dealCard();
while (j >= 0 && hand[j].getRank() > temp.getRank()) {
hand[j + 1] = hand[j];
j--;
}
hand[j + 1] = temp;
}
}
//PLACE ADDITIONAL METHODS AFTER THIS COMMENT
/*Checking for Royal flush by first checking if straight flush
and then if highest card is an Ace.*/
public boolean isRoyalFlush() {
return (isStraightFlush() && hand[4].getRank() == 14);
}
//Check for Straight Flush by seeing if it is both a straight and a flush
public boolean isStraightFlush() {
return (isFlush() && isStraight());
}
/*Checking if hand is a Four-of-a-kind. Done by looking at first card and
checking if it equals next 3 cards. If not, then checking second card and
checking if it equals next three cards.*/
public boolean is4OfAKind() {
boolean isFour = false;
for (int i = 0; i < hand.length - 3; i++) {
int card = hand[i].getRank();
if (card == hand[i + 1].getRank()
&& card == hand[i + 2].getRank()
&& card == hand[i + 3].getRank()) {
isFour = true;
}
}
return isFour;
}
//Checking if hand holds a Full House By:
public boolean isFullHouse() {
//Setting two boolean values
boolean a1, a2;
//First checking if it is a pair followed by a 3-of-a-kind.
a1 = hand[0].getRank() == hand[1].getRank() &&
hand[2].getRank() ==hand[3].getRank() && hand[3].getRank() == hand[4].getRank();
//Second, checking if it is 3-of-a-cind followed by a pair
a2 = hand[0].getRank() == hand[1].getRank() && hand[1].getRank() == hand[2].getRank() &&
hand[3].getRank() == hand[4].getRank();
//Returns true if it is either.
return (a1 || a2);
}
/*Checking if hand is a Flush by first getting the first card's suit
and checking if it is the same for all cards.*/
public boolean isFlush() {
String suit = hand[0].getSuit();
return hand[1].getSuit().equals(suit)
&& hand[2].getSuit().equals(suit)
&& hand[3].getSuit().equals(suit)
&& hand[4].getSuit().equals(suit);
}
/*Checking id hand is a Straight by first getting the rank of the first
card, then checking if the following cards are incremented by 1*/
public boolean isStraight() {
int card = hand[0].getRank();
return (hand[1].getRank() == (card + 1) &&
hand[2].getRank() == (card + 2) &&
hand[3].getRank() == (card + 3) &&
hand[4].getRank() == (card + 4));
}
/*Checking if hand is a Three-of-a-kind. Done by looking at first card and
checking if it equals next 2 cards. If not, then checking next card and
checking if it equals next 2 cards. This is done three times in total.*/
public boolean is3OfAKind() {
boolean threeKind = false;
for (int i = 0; i < hand.length - 2; i++) {
int card = hand[i].getRank();
if (card == hand[i + 1].getRank() &&
card == hand[i + 2].getRank()) {
threeKind = true;
}
}
return threeKind;
}
//Checking hand for 2 pairs by:
public boolean is2Pair() {
int count = 0; // Number of pairs.
int firstPair = 0; //If pair found, store rank.
//Go through hand
for (int i = 0; i < hand.length - 1; i++) {
int card = hand[i].getRank();
//Finding pairs. Cannot be same rank pairs.
if (card == hand[i + 1].getRank() && card != firstPair) {
firstPair = card;
count++;
}
}
return count == 2;
}
/*Checking if hand is a Pair. Done by looking at first card and
checking if it equals the next card. If not, then it checks the next card and
sees if it equals the next card. This is done four times in total.*/
public boolean isPair() {
boolean isPair = false;
for (int i = 0; i < hand.length - 1; i++) {
int card = hand[i].getRank();
if (card == hand[i + 1].getRank()) {
isPair = true;
}
}
return isPair;
}
//If hand is not equal to anything above, it must be High Card.
public boolean isHighCard() {
return !(isRoyalFlush() || isStraightFlush() || is4OfAKind()
|| isFullHouse() || isFlush() || isStraight()
|| is3OfAKind() || is2Pair() || isPair());
}
}
You could use the ternary operator ? : to shorten the code. Like
public static void testHand(PokerHand d) {
royalFlush += d.isRoyalFlush() ? 1 : 0;
straightFlush += d.isStraightFlush() ? 1 : 0;
fourtOfAKind += d.is4OfAKind() ? 1 : 0; // <-- this appears to be a typo.
fullHouse += d.isFullHouse() ? 1 : 0;
flush += d.isFlush() ? 1 : 0;
straight += d.isStraight() ? 1 : 0;
threeOfAKind += d.is3OfAKind() ? 1 : 0;
twoPair += d.is2Pair() ? 1 : 0;
pair += d.isPair() ? 1 : 0;
highCard += d.isHighCard() ? 1 : 0;
}
Alternatively, you could encode the hand types with an enum. Give the PokerHand a HandType (or create a factory method). Something like,
enum HandType {
ROYALFLUSH, STRAIGHTFLUSH, FOUROFAKIND, FULLHOUSE, FLUSH,
STRAIGHT, THREEOFAKIND, TWOPAIR, PAIR, HIGHCARD;
static HandType fromHand(PokerHand d) {
if (d.isRoyalFlush()) {
return ROYALFLUSH;
} else if (d.isStraightFlush()) {
return STRAIGHTFLUSH;
} else if (d.is4OfAKind()) {
return FOUROFAKIND;
} else if (d.isFullHouse()) {
return FULLHOUSE;
} else if (d.isFlush()) {
return FLUSH;
} else if (d.isStraight()) {
return STRAIGHT;
} else if (d.is3OfAKind()) {
return THREEOFAKIND;
} else if (d.is2Pair()) {
return TWOPAIR;
} else if (d.isPair()) {
return PAIR;
} else {
return HIGHCARD;
}
}
}
Then you can create an array of counts for testHand like
private static int[] handCounts = new int[HandType.values().length];
public static void testHand(PokerHand d) {
handCounts[HandType.fromHand(d)]++;
}
I would suggest you model the potential hands as an enum. They are a good use case because they have a fixed set of members.
Something like the following:
enum Rank {
ROYAL_FLUSH(Hand::isRoyalFlush),
FOUR_OF_A_KIND(Hand::isFourOfAKind),
...
public static Rank getRank(Hand hand) {
for (Rank rank: values()) {
if (rank.test.test(hand))
return rank;
}
throw new IllegalStateException("No rank for hand " + hand.toString());
}
private final Predicate<Hand> test;
Rank(Predicate<Hand> test) {
this.test = test;
}
}
Then all your if statements can be replaced by Rank.getRank(hand).
Maybe the switch statement will suit your needs :
switch (d) {
case d.isRoyalFlush() : royalFlush++; break;
case d.isStraightFlush(): straightFlush++; break;
... ... ...
default : do-something();
}

Why does this message print extra times (more than I want it to print)?

import java.util.ArrayList;
import java.lang.Math;
public class War {
ArrayList deck = new ArrayList(0);
ArrayList player1 = new ArrayList(0);
ArrayList player2 = new ArrayList(0);
int sum1 = 0;
int sum2 = 0;
int count = 0;
private void setup ()
{
for (int x = 1; x <= 13; x++)
{
for (int y = 1; y <= 4; y++)
{
deck.add(x);
}
}
while (deck.size() > 26)
{
double x = Math.random() * deck.size();
int y = (int) x;
player1.add(deck.remove(y));
}
while (deck.size() > 0)
{
double x = Math.random() * deck.size();
int y = (int) x;
player2.add(deck.remove(y));
}
for (int x = 0; x < 26; x++)
{
sum1 += (int) player1.get(x);
sum2 += (int) player2.get(x);
}
System.out.println("Player 1's starting power is " + sum1 + ".");
System.out.println();
System.out.println("Player 2's starting power is " + sum2 + ".");
System.out.println();
if (sum1 == sum2)
{
System.out.println("The two player's starting powers are equal! This'll be a good one, folks!");
}
}
public void play ()
{
if (hasSomeoneWon() || count == 0)
{
setup();
}
while (!player1.isEmpty() && !player2.isEmpty())
{
int a = (int) player1.get(0);
int b = (int) player2.get(0);
if (a > b)
{
player1.add(player1.remove(0)); // The winner's card is re-added to his deck before
player1.add(player2.remove(0)); // the loser's is added to the winner's deck.
}
if (a < b)
{
player2.add(player2.remove(0));
player2.add(player1.remove(0));
}
if (a == b)
{
war();
}
}
victory();
}
private void war ()
{
ArrayList temp1 = new ArrayList(0);
ArrayList temp2 = new ArrayList(0);
temp1.add(player1.remove(0));
temp2.add(player2.remove(0));
int x = 0;
while (!(player1.isEmpty() || player2.isEmpty()) && x < 3)
{
temp1.add(player1.remove(0));
temp2.add(player2.remove(0));
x++;
}
int a = (int) temp1.get(temp1.size() - 1);
int b = (int) temp2.get(temp2.size() - 1);
if (a == b)
{
if (temp1.size() != temp2.size())
{
if (temp1.size() > temp2.size())
{
while (!temp1.isEmpty())
{
player1.add(temp1.remove(0));
}
while (!temp2.isEmpty())
{
player1.add(temp2.remove(0));
}
}
else
{
while (!temp2.isEmpty())
{
player2.add(temp2.remove(0));
}
while (!temp1.isEmpty())
{
player2.add(temp1.remove(0));
}
}
}
else
{
if (player1.isEmpty() || player2.isEmpty())
{
if (player1.isEmpty())
{
while (!temp2.isEmpty())
{
player2.add(temp2.remove(0));
}
while (!temp1.isEmpty())
{
player2.add(temp1.remove(0));
}
}
else
{
while (!temp1.isEmpty())
{
player1.add(temp1.remove(0));
}
while (!temp2.isEmpty())
{
player1.add(temp2.remove(0));
}
}
}
else
{
war();
}
}
}
else
{
if (a > b)
{
while (!temp1.isEmpty())
{
player1.add(temp1.remove(0));
}
while (!temp2.isEmpty())
{
player1.add(temp2.remove(0));
}
}
else
{
while (!temp2.isEmpty())
{
player2.add(temp2.remove(0));
}
while (!temp1.isEmpty())
{
player2.add(temp1.remove(0));
}
}
play();
}
}
private void victory ()
{
if (player1.isEmpty() && sum2 > sum1)
{
System.out.println("Player 2 has won!");
}
if (player1.isEmpty() && sum1 > sum2)
{
System.out.println("Upset! Player 2 has won!");
}
if (player2.isEmpty() && sum1 > sum2)
{
System.out.println("Player 1 has won!");
}
if (player2.isEmpty() && sum2 > sum1)
{
System.out.println("Upset! Player 1 has won!");
}
hasSomeoneWon();
}
private boolean hasSomeoneWon ()
{
if (player1.isEmpty() || player2.isEmpty())
{
count++;
return true;
}
return false;
}
}
Sorry for including all of my code, but I don't know which part is causing the extra printing.
This is the card game War. It's supposed to play out the game between two players on its own and then print the winner. Whenever I've played it, though, the victory message (whichever one gets printed) is printed a variable number of times. I'm guessing this has something to do with either where and how often I've included my calls to some of the methods OR the recursive call to war within the war() method.
My only experience with Java is an AP Computer Science class I took this school year, so I'm for sure a noob by the standards of everyone reading this.
In the war() method you are recursively calling war() and war() will eventually end by recalling play(). So there will be multiple instances executing play(). Eventually when the cards run out for one player all this instances will comeback as the stack is unwound and continue executing play(). And finally of course calling victory() and printing it multiple times (should be the amount of times war() was called).
So each war() may print out a player has won once it reached the bottom of the recursion. I don't think the call to play() in war() is necessary, it will go back to the play() method after it finishes the war() method anyway.

Categories

Resources