I am writing a tic tac toe game and everything seems to work except the checks (horizontal/vertical and diagonal). I am using an array (int [3][3]) to make the board.
These are my checks:
private boolean wonStraightLines( int player)
{
boolean answer = false;
if (
((board[0][0] & board[0][1] & board[0][2]) == (player * 3)) ||
((board[1][0] & board[1][1] & board[1][2]) == (player * 3)) ||
((board[2][0] & board[2][1] & board[2][2]) == (player * 3)) ||
((board[0][0] & board[1][0] & board[2][0]) == (player * 3)) ||
((board[0][1] & board[1][1] & board[2][1]) == (player * 3)) ||
((board[0][2] & board[1][2] & board[2][2]) == (player * 3))
)
{
answer = true;
}
else {
answer = false;
}
return answer;
}
and for the diagonal:
private boolean wonDiagonal( int player)
{
boolean answer = false;
if (
((board[0][0] & board[1][1] & board[2][2]) == (player * 3)) || ((board[0][2] & board[1][1] & board[2][0]) == (player * 3))
)
{
answer = true;
}
else {
answer = false;
}
return answer;
}
when I run the program whenever X or O get 3 from any direction the game keeps running rather than throwing out a "you win" message. Any help would be appreciated. Not sure if any other parts of the code are needed or not.
EDIT: I also tried using the + instead of the & between the board array values but did not work as well.
I'm assuming there are 3 unique values for the states of each element such as for example:
board[row][col] = 0 // no entry
board[row][col] = 1 // player 1
board[row][col] = 2 // player 2
Without being clever a horizontal check for player (using row-major structure) would look like:
(board[0][0] == player && board[0][1] == player && board[0][2] == player)
and repeat for each row.
And similar for vertical, repeating for each column:
(board[0][0] == player && board[1][0] == player && board[2][0] == player)
And one diagonal:
(board[0][0] == player && board[1][1] == player && board[2][2] == player)
If you were determined to use an arithmetic operation then you'd have to change the "player" values to avoid overlap, as in player 1 == 1 and player 2 == 4, such as:
board[row][col] = 0 // no entry
board[row][col] = 1 // player 1
board[row][col] = 4 // player 2
Then you could do something like for a horizontal row:
((board[0][0] + board[0][1] + board[0][2]) == (player * 3))
Note in Java 8 and later adding an int array (a row or column in your case) can be slightly more succinct:
// sum the first row
(IntStream.of(board[0]).sum() == (player * 3))
Now if you really wanted to use the bit-wise "and" operation then this is your check assuming the initial element values (0,1,2) as stated at top:
// check if the player's "bit" is set in all elements of first row.
(board[0][0] & board[0][1] & board[0][2]) == player
Related
I am making a pacman arcade game on Java but my collisions have problems.
If you see my image
here my pacman sprite stands still (no x or y movement) when visiting a corner or when trying to turn back the direction it came (ie goes left but does not go right again). I understand this is because I set my xMovement to 0 and yMovement to 0 if a collision is detected (see second code block under collide()).
How would I allow the pacman sprite to move the other way (ie it comes from the left and I want it to go right) or traverse a corner if my collision() does not allow this?
Below is my draw method in the App.java class which draws the player based on the user's input. The keys wasd correspond with up, left, down, right respectively. this.direction is only used so the player does not waste a move nor go through a wall when a collision happens. *Note, I put (this.player.x_ord + 2) % 16 == 0 because my pacman image is not a 16x16 image unlike my walls which are.
public void draw() { // This is an infinite loop
mapDraw(); // this draws my map constantly so I do not have multiple pacman sprites drawn
this.player.tick(); // see second code below for details
if (keyPressed){ // Used wasd rather than arrow keys
if (key == 's' && this.direction != 's' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){ // We dont want Player to turn ever on a non 16 divisible area
this.player.p = this.loadImage("src/main/resources/playerDown.png");
this.player.yMovement = this.player.speed;
this.player.xMovement = 0;
this.direction = 's';
}
else if (key == 'w' && this.direction != 'w' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerUp.png");
this.player.yMovement = -this.player.speed;
this.player.xMovement = 0; // I do not want my pacman to move diagonally so thats why
this.direction = 'w';
}
else if (key == 'd' && this.direction != 'd' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerRight.png");
this.player.xMovement = this.player.speed;
this.player.yMovement = 0;
this.direction = 'd';
}
else if (key == 'a' && this.direction != 'a' && (this.player.x_ord + 2) % 16 == 0 &&
(this.player.y_ord + 4) % 16 == 0){
this.player.p = this.loadImage("src/main/resources/playerLeft.png");
this.player.xMovement = -this.player.speed;
this.player.yMovement = 0;
this.direction = 'a';
}
}
this.player.draw(this); //see second code below for details
}
Below is my player.java class
*Note again that this.x_ord + 2 == w.x_pos + 16 && this.y_ord + 4 == w.y_pos is offset because of my pacman sprite is larger than my 16x16 walls.
public void tick(){
// //logic
this.detectCollision();
if (this.isLiving){
this.y_ord += this.yMovement;
this.x_ord += this.xMovement;
}
}
public void draw(PApplet a){ //just draw the sprite
if (this.isLiving){
a.image(this.p, this.x_ord, this.y_ord);
}
}
public void collide(boolean isX){ // Is it an x or y collision
if (isX == true){ // If it moves left or right into a wall
this.xMovement = 0;
}
else if (isX == false){ // If it moves up or down into a wall
this.xMovement = 0;
}
}
public void detectCollision(){
for (Walls w: this.wallLocations){ // A list of wall locations from a .txt file
if (this.x_ord + 2 == w.x_pos + 16 && this.y_ord + 4 == w.y_pos){ // Detect left movement into right wall piece
collide(true);
}
if (this.x_ord + 2 + 16 == w.x_pos && this.y_ord + 4 == w.y_pos){ // Detect right movement into left wall piece
collide(true);
}
if (this.y_ord + 4 == w.y_pos + 16 && this.x_ord + 2 == w.x_pos){ // Detect up movement into bottom wall piece
collide(false);
}
if (this.y_ord + 4 + 16 == w.y_pos && this.x_ord + 2 == w.x_pos){ // Detect down movement into top wall piece
collide(false);
}
}
Any help to my problem is much appreciated.
I didn't analyze your code deeply but it seems to me that your main problem is that collide method considers only 2 cases, vertical movement or horizontal movement. If you want to have movement in 4 different directions that method need to have 4 different states.
To achieve that you could create an enum representing direction. Then in detectCollision pass appropriate Direction into collide. Finally, in the collide consider 4 different directions. For example, if there is barrier on the right, xMovement need to be non-positive. The collide method could look like so:
public void collide(Direction direction){
if (direction == Direction.RIGHT){
this.xMovement = Math.min(0, this.xMovement);
}
if (direction == Direction.LEFT){
this.xMovement = Math.max(0, this.xMovement);
}
if (direction == Direction.UP){
this.yMovement = Math.min(0, this.yMovement);
}
if (direction == Direction.DOWN){
this.yMovement = Math.max(0, this.yMovement);
}
}
Hello everyone, I'm trying to re-create the table above for an assignment and I'm stuck due to errors concerning the && function. Note that we are not allowed to use arrays and are limited to "switch" and "If"
This is what I've got so far:
if ((BEAK_MM == 1) && (CLAW_MM == 0) && (COLOR = "Grey")) {
System.out.println ("The type of bird is A.");}
else if ((BEAK_MM == 2) && (CLAW_MM == 1) && (COLOR = "Grey"))
System.out.println ("The type of bird is A.");}
else if ((BEAK_MM == 3) && (CLAW_MM == 2) && (COLOR = "Grey"))
System.out.println ("The type of bird is A.");}
else if ((BEAK_MM == 4) && (CLAW_MM == 3) && (COLOR = "Grey"))
System.out.println ("The type of bird is A.");}
else if ((BEAK_MM <= 4.5) && (CLAW_MM == 4) && (COLOR = "Grey"))
System.out.println ("The type of bird is A.");}
Assuming that BEAK and CLAW are integers and COLOR is a String, then the table could be arranged more like:
// must be grey
if ("Grey".equals(COLOR)) {
if ((BEAK_MM == 1 && CLAW_MM == 0)
|| (BEAK_MM == 2 && CLAW_MM == 1)
|| (BEAK_MM == 3 && CLAW_MM AW == 2)
|| (BEAK_MM == 4 && CLAW_MM == 3)
|| ( (BEAK_MM == 4 || BEAK_MM == 5) && CLAW_MM == 4))) {
System.out.println("The bird is the word");
}
}
The logic here is that according to the table, Bird Type A must be Grey. There are then some specific checks as to beak and claw types, but if it is not Grey, it is not a type A bird. There is also no need for all of the OP's stacked 'else if' statements.
There are many other approaches to solve the problem space, but I'm going with the limits of no arrays, and therefore presumably no other useful data structures such as List or Set.
As noted by #Nicholas K, String objects must be compared with .equals().
I'd also move this stuff to a method isBirdTypeA(int beak, int claw, String color) { ... }
#Test
public void testBirds()
{
final String G = "Grey";
final String P = "Pink";
assertTrue(isBirdTypeA(1, 0, G));
assertFalse(isBirdTypeA(1, 0, P));
assertTrue(isBirdTypeA(2, 1, G));
assertTrue(isBirdTypeA(3, 2, G));
assertTrue(isBirdTypeA(4, 3, G));
assertTrue(isBirdTypeA(4, 4, G));
assertTrue(isBirdTypeA(5, 4, G));
assertFalse(isBirdTypeA(4, 5, G));
assertFalse(isBirdTypeA(4, 0, G));
assertFalse(isBirdTypeA(1, 1, G));
}
private static boolean isBirdTypeA(int beak, int claw, String color)
{
if ("Grey".equals(color)) {
if ((beak == 1 && claw == 0)
|| (beak == 2 && claw == 1)
|| (beak == 3 && claw == 2)
|| (beak == 4 && claw == 3)
|| ( (beak == 4 || beak == 5) && claw == 4)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
int BEAK_MM = Integer.parseInt(args[0]);
int CLAW_MM = Integer.parseInt(args[1]);
String COLOR = args[2];
if (isBirdTypeA(BEAK_MM, CLAW_MM, COLOR)) {
System.out.println("The type of bird is A");
}
}
$ java BirdBeak 1 0 Grey
The type of bird is A
So I've been working on this project for the past 3 weeks now. I managed to get the minimax function to work early on for a 3x3 board, however problems started arising when I tried using it for a 4x4 board, namely Java heap space errors. Since then, with the help of Alpha beta pruning, I've managed to bring down the number of required minimax calls within the minimax function from aprox. 59000 to 16000 to 11000 and then finally to 8000 calls(This is assuming an initial minimax call for a board with one slot already filled). The problem now however, is that the method just keeps running for 4x4 games. It simply keeps calling itself non stop, no errors, no result, nothing. Theoretically, the way I see it, my function should work for arbitrary board sizes, the only problem was memory. Now, since I've reduced the memory greed of my function greatly, I'd expected it to work. Well, it does for the 3x3. However, it doesn't for the 4x4.
A brief explanation of what the function does:
The function returns an array of size 2 containing the most favorable next move from amongst all the possible next moves along with the score expected to be achieved from that move. The scoring system is simple. +10 for an O win, -10 for an X win and 0 for a draw. The function is of course recursive. Within it you will find certain shortcuts that reduce the number of required calls to itself. For example if it's X's turn and the returned score is -10(which is the best possible score for X) then exit the loop, i.e. stop observing other potential moves from this state. Here's the code for class State:
private String [] state; //Actual content of the board
private String turn; //Whose turn it is
private static int n; //Size of the board
public State(int n) {
state = new String[n*n];
for(int i = 0; i < state.length; i++) {
state[i] = "-";
}
State.n = n;
}
public int[] newminimax47(int z) {
int bestScore = (turn == "O") ? +9 : -9; //X is minimizer, O is maximizer
int bestPos = -1;
int currentScore;
int lastAdded = z;
if(isGameOver() != "Not Gameover") {
bestScore= score();
}
else {
int i = 0;
for(int x:getAvailableMoves()) {
if(turn == "X") { //X is minimizer
setX(x);
currentScore = newminimax47(x)[0];
if(i == 0) {
bestScore = currentScore;
bestPos = x;
if(bestScore == -10)
break;
}
else if(currentScore < bestScore) {
bestScore = currentScore;
bestPos = x;
if(bestScore == -10)
break;
}
}
else if(turn == "O") { //O is maximizer
setO(x);
currentScore = newminimax47(x)[0];
if(i == 0) {
bestScore = currentScore;
bestPos = x;
if(bestScore == 10)
break;
}
else if(currentScore > bestScore) {
bestScore = currentScore;
bestPos = x;
if(bestScore == 10)
break;
}
}
i++;
}
}
revert(lastAdded);
return new int [] {bestScore, bestPos};
}
Complementary functions used by newminimax47():
isGameOver():
public String isGameOver() {
if(n == 3) {
//Rows 1 to 3
if((state[0] != "-") && (state[0] == state[1]) && (state[1] == state[2]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[3] != "-") && (state[3] == state[4]) && (state[4] == state[5]))
return (state[3] == "X") ? "X Won" : "O Won";
else if((state[6] != "-") && (state[6] == state[7]) && (state[7] == state[8]))
return (state[6] == "X") ? "X Won" : "O Won";
//Columns 1 to 3
else if((state[0] != "-")&&(state[0] == state[3]) && (state[3] == state[6]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[1] != "-") && (state[1] == state[4]) && (state[4] == state[7]))
return (state[1] == "X") ? "X Won" : "O Won";
else if((state[2] != "-") && (state[2] == state[5]) && (state[5] == state[8]))
return (state[2] == "X") ? "X Won" : "O Won";
//Diagonals
else if((state[0] != "-") && (state[0]==state[4]) && (state[4] == state[8]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[6] != "-") && (state[6] == state[4]) && (state[4] == state[2]))
return (state[6] == "X") ? "X Won" : "O Won";
//Checking if draw
else if((state[0] != "-") && (state[1]!="-") && (state[2] != "-") && (state[3]!="-") &&
(state[4] != "-") && (state[5] != "-") && (state[6] != "-") && (state[7] != "-") &&
(state[8] != "-"))
return "Draw";
else
return "Not Gameover";
}
else {
//Rows 1 to 4
if((state[0] != "-") && (state[0] == state[1]) && (state[1] == state[2]) && (state[2] == state[3]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[4] != "-") && (state[4] == state[5]) && (state[5]==state[6]) && (state[6] == state[7]))
return (state[4] == "X") ? "X Won" : "O Won";
else if((state[8] != "-") && (state[8] == state[9]) && (state[9]==state[10]) && (state[10] == state[11]))
return (state[8] == "X") ? "X Won" : "O Won";
else if((state[12] != "-") && (state[12] == state[13]) &&(state[13] == state[14]) && (state[14] == state[15]))
return (state[12] == "X") ? "X Won" : "O Won";
//Columns 1 to 4
else if((state[0] != "-") && (state[0] == state[4]) && (state[4] == state[8]) && (state[8] == state[12]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[1] != "-") && (state[1] == state[5]) && (state[5] == state[9]) && (state[9] == state[13]))
return (state[1] == "X") ? "X Won" : "O Won";
else if((state[2] != "-") && (state[2] == state[6]) && (state[6] == state[10]) && (state[10] == state[14]))
return (state[2] == "X") ? "X Won" : "O Won";
else if((state[3] != "-") && (state[3] == state[7]) && (state[7] == state[11]) && (state[11] == state[15]))
return (state[3] == "X") ? "X Won" : "O Won";
//Diagonale
else if((state[0] != "-") && (state[0] == state[5]) && (state[5] == state[10]) && (state[10] == state[15]))
return (state[0] == "X") ? "X Won" : "O Won";
else if((state[12] != "-") && (state[12] == state[9]) && (state[9] == state[6]) && (state[6] == state[3]))
return (state[0] == "X") ? "X Won" : "O Won";
//Pruefe ob Gleichstand
else if((state[0] != "-") && (state[1] != "-") && (state[2] != "-") && (state[3]!="-") &&
(state[4] != "-") && (state[5] != "-") && (state[6] != "-") && (state[7] != "-") &&
(state[8] != "-") && (state[9] != "-") && (state[10] != "-") && (state[11] != "-") &&
(state[12] != "-") && (state[13] != "-") && (state[14] != "-") && (state[15] != "-"))
return "Draw";
else
return "Not Gameover";
}
}
Please excuse the bluntness of the isGameOver() method, it merely checks the state of the board( i.e. Win, Draw, Game not Over)
The getAvailableMoves() method:
public int[] getAvailableMoves() {
int count = 0;
int i = 0;
for(int j = 0; j < state.length; j++) {
if(state[j] == "-")
count++;
}
int [] availableSlots = new int[count];
for(int j = 0; j < state.length; j++){
if(state[j] == "-")
availableSlots[i++] = j;
}
return availableSlots;
}
This method merely returns an int array with all available next moves(regarding the current state) or returns empty array if no moves are available or if game is over.
The score() method:
public int score() {
if(isGameOver() == "X Won")
return -10;
else if(isGameOver() == "O Won")
return +10;
else
return 0;
}
setO(), setX() and revert():
public void setX(int i) {
state[i] = "X";
turn = "O";
lastAdded = i;
}
public void setO(int i) {
state[i] = "O";
turn = "X";
lastAdded = i;
}
public void revert(int i) {
state[i] = "-";
if(turn == "X")
turn = "O";
else
turn = "X";
}
My main method looks like this for a 3x3 game:
public static void main(String args[]) {
State s = new State(3);
int [] ScoreAndRecommendedMove = new int[2];
s.setX(8);
ScoreAndRecommendedMove = s.newminimax47(8);
System.out.println("Score: "+ScoreAndRecommendedMove[0]+" Position: "+ ScoreAndRecommendedMove[1]);
}
In this game, X has started the game with a move at position 8. The method in this case will return
Score: 0 Position: 4
Meaning that O's most promising move is at position 4 and in the worst case will yield a score of 0(i.e a draw).
The following image is meant to give an idea of how newminimax47() works. In this case our starting state(board) is given the number 1. Note: The numbers indicate the precedence in creation of the regarded states. 1 was created before 2, 2 was created before 3, 3 was created before 4 and so on.
In this scenario, the score and position eventually returned to state 1 will be
Score: 0 Position: 6
coming from state 8.
Note: The code you see is just snippets of the actual State class. These snippets on their own should allow you to recreate, and play around with, the newminimax47 function without problems(at least for 3x3). Any bugs you may find are not really bugs, they were simply not included in the snippets I copied here and the code should work without them. The lastAdded variable in the setO and setX functions for example is not included in the snippets here but I just realized you don't need it to be able to work with the minimax function, so you can just comment that out.
I played around with your code and there is quite a bit to say
bug
first of all there is a bug. I don't think your code actually works for a 3x3 board. The problem is the location you revert the move you add to the board. You do this at the end of the newminimax47 method exactly once, even though in the method you add moves to the board inside a for loop. This means that calling the method does not only compute something but also changes the board state, and the rest of the code expects it not to.
So remove the revert where it is now and in revert as soon as you can:
setX(x);
currentScore = newminimax47(x)[0];
revert(x);
this also implies you don't need the lastAdded variable.
play
It's a lot easier to see what is happening if you actually play against your own algorithm. Add a method to your State class
public void dump() {
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++) {
System.out.print(state[y * n + x]);
}
System.out.println();
}
}
and in you main something like
public void play() {
State s=new State(3);
Scanner in = new Scanner (System.in);
while (s.isGameOver().equals("Not Gameover")) {
int[] options = s.getAvailableMoves();
s.dump();
System.out.println ("Your options are " + Arrays.toString(options));
int move = in.nextInt();
s.setX(move);
int [] ScoreAndRecommendedMove=new int[2];
ScoreAndRecommendedMove=s.newminimax47(0);
System.out.println("Score: "+ScoreAndRecommendedMove[0]+" Position: "+ ScoreAndRecommendedMove[1]);
s.setO(ScoreAndRecommendedMove[1]);
}
s.dump();
}
and you can actually play against it. On a 3x3 board this works just fine for me. Unfortunately I estimated computing the first move on a 4x4 takes my computer approximately 48 hours.
data types
Your choice of data types is often a bit... strange. If you want to remember a single character, use char instead of String. If you want to return a yes/no decision, try use boolean. There are also some parts of the program that could be replaces by less code doing the same. But that wasn't your question, so on to...
algorithm
Ok, so what's wrong with minimax to solve this problem?
Suppose the first four moves are X5, O8, X6 O7. Another possibility is to start the game with X5, O7, X6, O8. Yet another one is X6, O7, X5, O8. And finally there's X6, O8, X5, O7.
All four of those possibilities for the first four moves of the game lead to the exact same gamestate. But minimax will not recognise they're the same (basically there is no memory of parallel branches) so it will compute them all four. And the number of times each board state is computed will increase rapidly if you search deeper.
The number of possible games vastly outnumbers the number of possible board states. To estimate the number of games: at first there are 16 possible moves, then 15, then 14, 13, ... and so on. A rough estimation is 16!, although minimax won't have to compute all of them, because many of them will have finished before the 16th move.
An estimation for the number of game states is: every square on the board can be either empty, or an X, or an O. So that's 3^16 boards. Not all of them are actually valid boards, because the number of Xs on the board can be at most one more then the number of Os, but still it's close to 3^16.
16! possible games is about half a million times more then 3^16 possible board states. Which means we're approximately computing every board half a milion times in stead of just once.
The solution is to start remembering every gamestate you compute. Every time the recursive function is called first check if you already know the answer, and if so just return the old answer. It's a technique called memoization.
Memoization
I'll describe how to add memoization while using the data structures you already choose (even though I don't agree with them). To do memoization you need a collection on which you can do quick adds and quick lookups. A list (for example ArrayList) won't do us any good. It's fast to add values but to do a lookup is very slow in long lists. There are some options but the easiest one to use is HashMap. In order to use HashMap you need to create something that represents your state and you can use as a key. The most straight-forward is to just make a String with all the X/O/- symbols in it that represent your board.
So add
Map<String,int[]> oldAnswers = new HashMap<String,int[]>();
to your State object.
Then at the start of your newminimax47 method create the String that represents the State and check if we already know the answer:
String stateString = "";
for (String field : state) stateString += field;
int[] oldAnswer = oldAnswers.get(stateString);
if (oldAnswer != null) return oldAnswer;
Finally when you compute a new answer the end of newminimax47 you should not only return it, but also store it in the map:
int[] answer = {bestScore, bestPos};
oldAnswers.put (stateString, answer);
return answer;
With memoization in place I was able to play a 4x4 game against your code. The first move is still slow (20 seconds) but after that everything is computed and it's very fast. If you want to speed it up further, you could look into alpha beta pruning. But the improvement won't be near as much as memoization. Another option is using more efficient data types. It won't reduce the theoretical order of your algorithm, but could still easily make it 5 times faster.
As user3386109 explained, the problem here is how many times you calculate everything. There are a few things that may help you out though, considerin an N-sized grid:
a user cannot win if there are less than N of his symbols, so you can use that in the isGameOver function as a first check
the first thing that you have to do is to prevent your opponent from winning if there's a chance that his next move is a winning one
Keep track of how many "X" and "O" are in each row and column and in the two diagonals by incrementing counters every move. If there are N-1 of the same symbol, the next one will be a winning move for either you or your opponent.
By doing so, you can easily tell which is the best move, because then:
if you have a winning move you put the symbol there
check your opponent, if he has N-1 symbols on the same row/column/diagonal you put it there
if your opponent has more symbols than you on some place, you even out the place (that means +1 or +2, depending on who's starting the game)
if that's not the case, you put your next symbol on the row/colum/diagonal where you have more symbols
if you have the same number of symbols on some places, you just put it where your opponent has more symbols
if you and your opponent are entirely even, just go for your own strategy (random would not be bad, I guess :-) )
Unless you really need it (for example as homework), I wouldn't use recursion for this one.
Just as a side note: I don't think it's good practice to have what is actually a boolean function return a string and then compare that with a fixed value. A true/false return value for the isGameOver function looks much better to me.
I'm having an issue with my very first program in Java. I'm trying to reproduce a game you play with matches, but the program never stops when it's supposed to...
When I enter numbers like 6, 3, 2, 1 or 7, 3, 2, 1 the loop should stop there but it just continues to the next turn as if nothing happened, even though the variables have the right value and should match the end conditions.
I'm pretty sure the problem lies in the while part of the main loop (at the very end) but I can't see it! It's probably something obvious, but well...
Here is the full source code (the rules of the game are below it):
import java.util.Scanner;
public class JeuDeNim {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//Starting the game
int totalMatches;
do {
System.out.println("How many matches do you want to play with? "
+ "(from 6 to 60)");
totalMatches = sc.nextInt();
}
while(totalMatches < 6 || totalMatches > 60);
int matchesPlayer1 = 0;//to keep track of
int matchesPlayer2 = 0;//the previous round
int i = 1;//to know whose turn it is
do{
//player 1
if(!(i % 2 == 0)) {//if odd number, player 1
//round 1
if(i == 1) {
do {
System.out.println("Player 1: How many matches do you "
+ "want to pick? (1, 2 or 3)");
matchesPlayer1 = sc.nextInt();
}
while(matchesPlayer1 < 1 || matchesPlayer1 > 3);
totalMatches = totalMatches - matchesPlayer1;
i++;
}
//odd round x
else {
do {
System.out.println("Player 1: How many matches do you "
+ "want to pick this turn?");
matchesPlayer1 = sc.nextInt();
if(totalMatches - matchesPlayer1 < 0) {
System.out.println("Pick a smaller number");
//totalMatches cannot be negative
} else if(matchesPlayer1 == matchesPlayer2) {
System.out.println("You cannot pick the same number "
+ "of matches as Player 2");
}
}
while(matchesPlayer1 < 1 || matchesPlayer1 > 3
|| (totalMatches - matchesPlayer1 < 0)
|| (matchesPlayer1 == matchesPlayer2));
totalMatches = totalMatches - matchesPlayer1;
if(totalMatches == 0
|| (totalMatches == 1 && matchesPlayer1 == 1)) {
System.out.println("Player 1 Wins!");
}
i++;
}
}
//player 2
else {
//round 2
if(i == 2) {
do {
System.out.println("Player 2: How many matches do you "
+ "want to pick? (1, 2 or 3)");
matchesPlayer2 = sc.nextInt();
if(matchesPlayer2 == matchesPlayer1) {
System.out.println("You cannot pick the same "
+ "number of matches as Player 2");
}
}
while(matchesPlayer2 < 1 || matchesPlayer2 > 3
|| matchesPlayer2 == matchesPlayer1);
totalMatches = totalMatches - matchesPlayer2;
i++;
}
//even round x
else {
do {
System.out.println("Player 2: How many matches do you "
+ "want to pick this turn?");
matchesPlayer2 = sc.nextInt();
if (totalMatches - matchesPlayer2 < 0) {
System.out.println("Pick a smaller number");
//totalMatches cannot be negative
} else if(matchesPlayer2 == matchesPlayer1) {
System.out.println("You cannot pick the same number "
+ "of matches as Player 1");
}
}
while(matchesPlayer2 < 1 || matchesPlayer2 > 3
|| (totalMatches - matchesPlayer2 < 0)
|| (matchesPlayer2 == matchesPlayer1));
totalMatches = totalMatches - matchesPlayer2;
if(totalMatches == 0
|| (totalMatches == 1 && matchesPlayer2 == 1)) {
System.out.println("Player 2 Wins!");
}
i++;
}
}
System.out.println("totalMatches: " + totalMatches + " "
+ "matchesPlayer1: " + matchesPlayer1 + " " + "matchesPlayer2: "
+ matchesPlayer2);//to check that everything is working. It is not...
}
while(totalMatches > 0
|| !(!(i % 2 == 0) && totalMatches == 1 && matchesPlayer1 == 1)
|| !((i % 2 == 0) && totalMatches == 1 && matchesPlayer2 == 1));
}
}
Here are the rules of the game: it's a two-player game, where the players take turns picking matches (1, 2 or 3) and cannot pick the same number of matches as the other player: if player one picks 2 matches, player two will have to pick either 1 or 3 matches. The player who cannot pick anymore matches loses the game, which means there are two end scenarios : when there are no more matches, or there is 1 but the other player picked 1 during the previous round.
Look at the conditions in your final while loop
totalMatches > 0 ||
!(!(i % 2 == 0) && totalMatches == 1 && matchesPlayer1 == 1) ||
!((i % 2 == 0) && totalMatches == 1 && matchesPlayer2 == 1));
This means the loop will repeat as long as there are any matches left, or it isn't player 1's turn with 1 match left and a pick of 1 match, or it isn't player 2's turn with 1 match left and a pick of 1 match.
This can never happen because (among other reasons), it requires i%2==0 and i%2 != 0. Switch the || to &&, and it should fix the problem. As was pointed out in the comments, you also need to reverse the player turn check, because the turn counter has already been incremented by this point.
The reason you want to use && here instead of ||, like in the other spots in your code is that your checking for a different concept. Every other time, you check the reasons why the loop should be repeated. This time, you check the reasons why the loop should end, and negate them. When in doubt, actually plug in values for the comparison, and see if it evaluates to what you think it should.
The game ends when there is no more matches left. So while(totalMatches > 0); is just enough.
Remove the unnecessary lines:
|| !(!(i % 2 == 0) && totalMatches == 1 && matchesPlayer1 == 1)
|| !((i % 2 == 0) && totalMatches == 1 && matchesPlayer2 == 1));
I am having trouble finishing the straight method for a poker hand. I don't understand why my code doesn't work.
public static boolean containsStraight(int [] hand)
{
boolean straight = false;
for(int i = 0; i < 5; i++)
{
if (hand[i] == 2 && hand[i] == 3 && hand[i] == 4 && hand[i] == 5 && hand[i] == 6)
{
straight = true;
}
if (hand[i] == 3 && hand[i] == 4 && hand[i] == 5 && hand[i] == 6 && hand[i] == 7)
{
straight = true;
}
if (hand[i] == 4 && hand[i] == 5 && hand[i] == 6 && hand[i] == 7 && hand[i] == 8)
{
straight = true;
}
if (hand[i] == 5 && hand[i] == 6 && hand[i] == 7 && hand[i] == 8 && hand[i] == 9)
{
straight = true;
}
}
return straight;
}
As pL4Gu33 has already stated in his answer, your comparison is faulty. Essentially, each step through the for-loop leaves hand[i] at a constant value (say, 4). That means that your if-statements are checking:
if(4 == 4 && 4 == 5 && 4 == 6 && 4 == 7 && 4 == 8) {
...
}
This will never evaluate to true. If you knew for certain that you had five elements in the hand and that the hand was already sorted, you could do
if (hand[0] == 2 && hand[1] == 3 && hand[2] == 4 && hand[3] == 5 && hand[4] == 6) {
...
}
However, I'm going to show you a better answer.
The first thing you should do is to sort your hand. Once you do that, it's easy to step through the hand and check to see if the next card in the hand is exactly one greater than the previous card. If you get to the end and this holds true, then it's a straight.
/*
* You will need to import java.util.Arrays
*/
public boolean isStraight(int[] hand) {
if(hand == null || hand.length != 5) {
return false;
}
else {
// Automatically sort the hand
Arrays.sort(hand);
// Set the "previous" variable to a theoretically impossible value
int prev = -1;
// Iterate through the hand and see if the next card is exactly one more than
// the previous one.
for(int i = 0; i < hand.length; i++) {
// If prev is -1, then this is the first time through the for-loop
// If the card that we're on has a value of the previous card + 1,
// we still have the possibility of a straight.
if(prev == -1 || (prev + 1) == hand[i]) {
prev = hand[i];
}
else {
return false;
}
}
return true;
}
}
You say in every iteration hand[i] must be 2 AND 3 AND 4 AND 5. That is impossible. There is only one number in hand[i].
The problem is, that you are using the cycle incorrectly, because you are always checking the value of the same card in hand[i]. My suggestion would be either to do a sort first, or if you want to be more efficient, you can use a second field of booleans, that would indicate, whether a card of given value is present in your hand. This way you can easily check, if you have any number of cards in succesion.
public static boolean containsStraight(int[] cards) {
int count = 0;
boolean[] valueInHand = new boolean[10];
for (int card : cards) {
valueInHand[card] = true;
}
for (boolean value : valueInHand) {
if (value == true) {
count++;
} else {
count = 0;
}
// works for any number of cards
if (count == cards.length) {
return true;
}
}
return false;
}