The assignment is to create a game of reversi. I have it working except for moves that involve changing more than one chip
x o
o o
o o o
# <-- the # is x's move that crashes the game.
, in which case the program crashes. The crash takes place somewhere in isPlayable().
Where am I going wrong?
//there are 7 more such methods for different directions
// (down, left, right, diagonals).
public void searchN(int x, int y, int increment){
if(x-increment>=0){
if(this.isPlayed(x-increment,y)){
if(increment>1 && chips[x-increment][y]==turnColor){
//turnColor is an int value 1 or 2
// (0 represents unplayed space in chips[][])
// 1 corresponding to white, 2 corresponding to black.
playable[0] = true;
leads[0] = false;
} else {
if(chips[x-increment][y]!=turnColor){
leads[0]=true;
}
}
}else
leads[0]=false;
}else
leads[0]=false;
}
public boolean isPlayable(int x, int y){
this.searchN(x,y,1); //7 other directions are searched
while(leads[0]||leads[1]||leads[2]||leads[3]||leads[4]
||leads[5]||leads[6]||leads[7]){
int i = 2;
if(leads[0]) // 7 other directions are searched given that their marker is true.
this.searchN(x,y,i);
}
if(playable[0]||playable[1]||playable[2]||playable[3]||playable[4]
||playable[5]||playable[6]||playable[7])
return true;
else
return false;
}
Per your comment, it sounds like you're experiencing a hang, not a crash. When a program hangs, you should look for places where the program can get indefinitely "stuck". The prime suspect in isPlayable is your while loop. As long as any of the eight booleans are true it will never complete.
I would add some logging so you can see what's happening:
while(leads[0]||leads[1]||leads[2]||leads[3]||leads[4]
||leads[5]||leads[6]||leads[7]){
System.out.println("leads[0]: " + leads[0]);
System.out.println("leads[1]: " + leads[1]);
// etc.
int i = 2;
if(leads[0]) // 7 other directions are searched given that their marker is true.
this.searchN(x,y,i);
}
Once you've verified that this is the problem, start looking into your search methods to figure out why its happening.
Related
Sorry for lack of details, I made a few edits.
I've got a problem in my java class that I can't wrap my head around.
I need to make a program that draws random numbers which are formations in a 10 round competition.
In this competition judges score you based on the formations you draw.
You can only have 5 or 6 points per round. You can only have 6 points if you draw 3 - 2 point formations.
The range of formations is 1-38.
Formations 1-16 are worth 1 point.
The rest are worth 2 points.
I tried this.
int formation;
int points;
int round;
boolean isOnePoint;
int maneuvers = 0;
public void main(String args[]){
while(points < 5){
getRandomFormation();
if(points < 5 && isOnePoint){
points++;
}
else if(points == 4 && !isOnePoint && maneuvers < 3){
points +=2;
} maneuvers++;
}
}
public void getRandomFormation(){
formation = rand.nextInt(38);
if(formation < 17){ isOnePoint = true; } else isOnePoint = false;
}
The problem I am facing is with this code that I came up with I am still able to get something like:
// example of output round with points recieved
Round 6: 1, 2, 1, 2
The problem however, is that you cannot get 6 points with anything other than getting three random 2 point combinations...
What am I overlooking / missing in my code to be able to get it right?
Based off what you have posted you seem to only want three numeric values.
Change your while(points<5) to while(maneuver<3) and increment maneuver at the end of the loop.
That would be the quickest way to solve your problem. The logic in your if statements are quite convoluted. You only check to see the number of maneuvers in the second if statement which means that you can still meet the parameters for the first if.
for(int i=0;i<3;i++){
getRandomFormation();
if(isOnePoint)
points++;
else
points +=2;
}
I am new in this page, it hope get to some help, basically I am doing a minesweeper game on Java but it have a problem with a function: discover the region with no mines and no numbers like in the game on windows, when you click in one place and all withe cells appear. I tried make recursive but I can't, some help?
Sorry for the code, the original is in spanish but i tried make a pseudocode:
Matriz = multidimensional Array (the minesweeper)
min and max returns the index min and max to iterate (8 sorroud cells)
private void discoverWitheCell(int x, int y) {
if(matriz[x][y].getdiscovered() == false){
matriz[x][y].setDiscovered(true);
}
else{
if(matriz[x][y].getNumberOfMinesArround() == 0){
for(int i=min(x);i<max(x);i++)
for(int j=min(y);j<max(y);j++)
discoverWhiteCell(i,j);
}
}
}
There's not a lot of code here but I feel like you're coming at it backwards.
Sorry, I'm not a Java speaker so I'm guessing at some of the syntax. Note that this can go out of bounds--personally, I would add a layer of empty cells around my map so I never need to concern myself with bounds checking.
private void ClickSquare(int x, int y)
{
// Did the user click an already exposed square? If so, ignore
if (matriz[x][y].getDiscovered()) return;
matriz[x][y].SetDiscovered(true);
if (matriz[x][y].getNumberOfMinesAround != 0) return;
// If empty, click all the neighbors
for (int xloop = x - 1; xloop <= x + 1; xloop++)
for (int yloop = y - 1; yloop <= y + 1; yloop++)
ClickSquare(xloop, yloop);
}
I believe you have the discovered test messed up and your version appears to be able to go into infinite (until the stack overflows) recursion as if the neighbor is also zero it will come back to the original cell. My version stops this recursion by only processing a cell if it hasn't already been processed.
So I'm currently making a game where the instructions are to move left or right within an array using the integer stored at a marked index (circle in this case) until we can get the circle to the last index of the array. The last integer of the array is always 0.
For example,
[4] 1 2 3 1 0, here we start at the circle 0 (index)
We move 4 to the right, 4 1 2 3 [1] 0
Then 1 time to the right, 4 1 2 3 1 [0]. Here the game stops and we win.
My code is as follows for a recursive method:
public static boolean rightWing (int circle, int[] game, List<Integer> checkerList){
int last = game.length-1;
if (circle == last){ // base case for recursion
return true;
}
if (circle < 0){ // if we go out of bounds on the left
return false;
}
if (circle > last){ // if we go out of bounds on the right
return false;
}
if (checkerList.contains(circle)){ // check for the impossible case
return false;
}
checkerList.add(circle); // adds the circle value for the last check to checkerList so we can check for the impossible case
int moveRight = circle + game[circle]; // these two integers help the game move according to the value of the int at circle
int moveLeft = circle - game[circle];
return rightWing( moveRight, game, checkerList) || rightWing(moveLeft, game,checkerList);
}
This works great, but the only problem is it's recursive and slow. I'm trying to redesign it using loops and stacks/queues to make it more efficient, but I'm stuck after writing this (in pseudo):
Boolean rightWing (int circle, List<int> game, List<int> checkerList)
Int lastPlace = game.size() - 1
For int i <- 0 to game.size() - 1 do
If i equals lastPlace then // returns true when i is at the last position of the game
Return true
Any input on how to go forward would be appreciated!
The most important bit: when debugging app for the slowness, you should collect some performance data first to identify where your app is spending the most of its time. Otherwise fixing performance is inefficient. You can use jvisualvm it's bundled with jdk.
Data structures rule the world of performance
One thing why it can be slow is because of this:
if (checkerList.contains(circle)){ // check for the impossible case
return false;
}
The more items you have in the list, the slower it becomes. List has linear complexity for the contains method. You can make it constant complexity if you'll use HashSet. E.g. if you have list with 100 elements, this part will be around slower 100 times with List than with HashSet.
Another thing which might be taking some time is boxing/unboxing: each time you put element to the list, int is being wrapped into new Integer object - this is called boxing. You might want to use IntSet to avoid boxing/unboxing and save on the GC time.
Converting to the iterative form
I won't expect this to affect your application speed, but just for the sake of completeness of the answer.
Converting recursive app to iterative form is pretty simple: each of the method parameters under the cover is stored on a hidden stack on each call of your (or others function). During conversion you just create your own stack and manage it manually
public static boolean rightWingRecursive(int circle, int[] game) {
Set<Integer> checkerList = new HashSet<Integer>();
Deque<Integer> statesToExplore = new LinkedList<>();
int last = game.length - 1;
statesToExplore.push(circle);
while (!statesToExplore.isEmpty()) {
int circleState = statesToExplore.pop();
if (circleState == last) { // base case for recursion
return true;
}
if (circleState < 0) { // if we go out of bounds on the left
continue;
}
if (circleState > last) { // if we go out of bounds on the right
continue;
}
if (checkerList.contains(circle)) { // check for the impossible case
continue;
}
checkerList.add(circle); // adds the circle value for the last check to
// checkerList so we can check for the
// impossible case
int moveRight = circle + game[circle]; // these two integers help the
// game move according to the
// value of the int at circle
int moveLeft = circle - game[circle];
statesToExplore.push(moveRight);
statesToExplore.push(moveLeft);
}
return false;
}
I'm having a problem with my Collision method below. the problem is when there are two enemies in the game. It intersects one enemy within the loop, and proceeds to return true for colliding. But if there is a second enemy within this array List It will not be colliding with that second object, therefore causing it to return false and the player continues walking. any idea's on how I can make him stop when he is in contact with ANY enemy, instead of just continuing because he isn't in contact with all the enemies?
Thanks, here is the code.
public void checkCollision(){
ArrayList<Enemy> enemy = c.getEnemyList();
for ( int i = 0; i < enemy.size(); i++){
Enemy e = enemy.get(i);
if (!getBounds().intersects(e.getBounds())){
walk();
return;
}
if (getBounds().intersects(e.getBounds())){
if (e.getHP() <= 0){
c.removeEnemy(e);
walk();
return;
}
fight();
if (count == 25 || count == 65){
int dd = DCalc.calcDmg(atk, atkMAX);
e.dmg(dd);
}
}
}
}
This is just another example of the 'early return' problem. This problem manifests when you need to do a check of the format (if ANY, x, else y) or (if ALL, x, else y) and you rephrase it in the format (if FIRST, x, else y).
To solve this, you need to remake the algorithm as follows:
bool collided = false
For each enemy:
Are we colliding with this enemy?
If we are, do collision detection and set `collided` to true
end for
If `collided` is false, NOW we can run the code that should only run if we collided with nothing
Im making a text based battleship game and the player plays against the computer. 3 random 3 unit long ships are placed on the board, and I want the computer to be able to guess around where his last guess was if his last guess was a hit. (but I want it to work so that he keeps guessing around the same spot until he got a hit and keep guessing around there until he gets the whole ship, or 3 hits)
It works a bit; the computer will guess near his last guess if it was a hit, but if he misses that guess then he starts guessing randomly again. Can someone help me out a bit?
-getGuess() method is the one with the AI-
/*
* computer class to handle computers guesses/ etc
* most methods are copied from player class, but slightly altered to account for variable names
* Methods that havent been copied have comments
*/
public class Computer{
static int firstCo, secondCo;
static int[] guessedHits={7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
//int array to store last guess
static int[] lastGuess = new int[2];
//int array to store current guess
static int[] guess=new int[2];
public static int[] computerShip1=new int[6];
public static int[] computerShip2=new int[6];
public static int[] computerShip3=new int[6];
/*
* method to choose random guess for computer - but make it guess around last guess if last guess was a hit
* return guess coordinate numbers in an array
*/
public static int[] getGuess(){
int[] guess=new int[2];
int firstCo, secCo;
int ran; //random int between 0 and 1 - will help to make random choices for guesses
if(isHit(lastGuess[0],lastGuess[1])){
ran=(int)(Math.random()*2);
//if ran is 0 and last guesses x coordinate was correct, set next guess to last x, and next y to last y +1
if((ran==0 && lastGuess[0]==Player.playerShip1[0]) || (ran==0 && lastGuess[0]==Player.playerShip1[2]) || (ran==0 && lastGuess[0]==Player.playerShip1[4])){
guess[0]=lastGuess[0];
guess[1]=lastGuess[1]+1;
//if ran is 1 and last guesses x coordinate was correct, set next guess to last x, and next y to last y -1
}else if((ran==1 && lastGuess[0]==Player.playerShip1[0]) || (ran==1 && lastGuess[0]==Player.playerShip1[2]) || (ran==1 && lastGuess[0]==Player.playerShip1[4])){
guess[0]=lastGuess[0];
guess[1]=lastGuess[1]-1;
//if ran is 0 and last guesses y coordinate was correct, set next guess to last y, and next x to last x +1
}else if((ran==0 && lastGuess[1]==Player.playerShip1[1]) || (ran==0 && lastGuess[1]==Player.playerShip1[3]) || (ran==0 && lastGuess[1]==Player.playerShip1[5])){
guess[0]=lastGuess[0]+1;
guess[1]=lastGuess[1];
//if ran is 1 and last guesses y coordinate was correct, set next guess to last y, and next x to last x -1
}else if((ran==1 && lastGuess[1]==Player.playerShip1[1]) || (ran==1 && lastGuess[1]==Player.playerShip1[3]) || (ran==1 && lastGuess[1]==Player.playerShip1[5])){
guess[0]=lastGuess[0]-1;
guess[1]=lastGuess[1];
}
return guess;
}else{
guess[0]=(int)(Math.random()*7);
guess[1]=(int)(Math.random()*7);
return guess;
}
}
public static boolean isHit(int firstC, int secC){
for(int i=0; i<Player.playerShip1.length; i=i+2){
if(firstC==Player.playerShip1[i] && secC==Player.playerShip1[i+1]){
return true;
}
if(i==4){
break;
}
}
for(int i=0; i<Player.playerShip2.length; i=i+2){
if(firstC==Player.playerShip2[i] && secC==Player.playerShip2[i+1]){
return true;
}
if(i==4){
break;
}
}
for(int i=0; i<Player.playerShip3.length; i=i+2){
if(firstC==Player.playerShip3[i] && secC==Player.playerShip3[i+1]){
return true;
}
if(i==4){
break;
}
}
return false;
}
public static void addHits(int firstC, int secC){
int index=-1;
for(int i=0; i<guessedHits.length; i++){
if(guessedHits[i]==7){
index=i;
break;
}
}
guessedHits[index]=firstC;
guessedHits[index+1]=secC;
}
public static void setComputerShips(){
int randX, randY;
int direction; //will be random int 0-1, determines direction ship will extend(up/down, left/right)
randX=(int)(Math.random()*7);
randY=(int)(Math.random()*7);
direction=(int)(Math.random()*2);
computerShip1[0]=randX;
computerShip1[1]=randY;
if(direction==0){//extend upwards or downwards 2 units(y values change, x stays the same)
computerShip1[2]=randX;
computerShip1[4]=randX;
if(randY>3){//if y value is greater than 3, has to extend down or it wont fit
computerShip1[3]=randY-1;
computerShip1[5]=randY-2;
}else if(randY<2){//if y value is less than 2, has to extend up or it wont fit
computerShip1[3]=randY+1;
computerShip1[5]=randY+2;
}else{//if direction doesnt matter, just extend upwards
computerShip1[3]=randY+1;
computerShip1[5]=randY+2;
}
}else if(direction==1){//extends left or right 2 units(y values stay the same, x changes)
computerShip1[3]=randY;
computerShip1[5]=randY;
if(randX>3){//if x is greater than 3, must extend left or it wont fit
computerShip1[2]=randX-1;
computerShip1[4]=randX-2;
}else if(randX<2){//if x is less than 2, must extend right or it wont fit
computerShip1[2]=randX+1;
computerShip1[4]=randX+2;
}else{//if direction doesnt matter, just extend right
computerShip1[2]=randX+1;
computerShip1[4]=randX+2;
}
}
//do same for both other ships
do{
randX=(int)(Math.random()*7);
randY=(int)(Math.random()*7);
}while((randX==computerShip1[0] && randY==computerShip1[1])||(randX==computerShip1[2]&&randY==computerShip1[3])||(randX==computerShip1[4]&&randY==computerShip1[5]));
direction=(int)(Math.random()*2);
computerShip2[0]=randX;
computerShip2[1]=randY;
if(direction==0){
computerShip2[2]=randX;
computerShip2[4]=randX;
if(randY>3){
computerShip2[3]=randY-1;
computerShip2[5]=randY-2;
}else if(randY<2){
computerShip2[3]=randY+1;
computerShip2[5]=randY+2;
}else{
computerShip2[3]=randY+1;
computerShip2[5]=randY+2;
}
}else if(direction==1){
computerShip2[3]=randY;
computerShip2[5]=randY;
if(randX>3){
computerShip2[2]=randX-1;
computerShip2[4]=randX-2;
}else if(randX<2){
computerShip2[2]=randX+1;
computerShip2[4]=randX+2;
}else{
computerShip2[2]=randX+1;
computerShip2[4]=randX+2;
}
}
do{
randX=(int)(Math.random()*7);
randY=(int)(Math.random()*7);
}while((randX==computerShip1[0] && randY==computerShip1[1])||(randX==computerShip1[2]&&randY==computerShip1[3])||(randX==computerShip1[4]&&randY==computerShip1[5])||(randX==computerShip2[0] && randY==computerShip2[1])||(randX==computerShip2[2]&&randY==computerShip2[3])||(randX==computerShip2[4]&&randY==computerShip2[5]));
direction=(int)(Math.random()*2);
computerShip3[0]=randX;
computerShip3[1]=randY;
if(direction==0){
computerShip3[2]=randX;
computerShip3[4]=randX;
if(randY>3){
computerShip3[3]=randY-1;
computerShip3[5]=randY-2;
}else if(randY<2){
computerShip3[3]=randY+1;
computerShip3[5]=randY+2;
}else{
computerShip3[3]=randY+1;
computerShip3[5]=randY+2;
}
}else if(direction==1){
computerShip3[3]=randY;
computerShip3[5]=randY;
if(randX>3){
computerShip3[2]=randX-1;
computerShip3[4]=randX-2;
}else if(randX<2){
computerShip3[2]=randX+1;
computerShip3[4]=randX+2;
}else{
computerShip3[2]=randX+1;
computerShip3[4]=randX+2;
}
}
}
public static boolean hasWon(){
if(guessedHits[17]!=7)
return true;
else
return false;
}
}
Your getGuess() function is the one you're after right?
1) You never account for times when you guess the same spot twice. Make a boolean value that determines whether the coordinates you're attempting to guess haven't already been guessed.
2) Your method of keeping ship coordinates is very awkward where 0,2,4 are X coords while 1,3,5 are Y coords? You're better off creating a Ship class that handles coordinates, and checks like isHit.
public class Ship {
int[] xCoords = new int[3];
int[] yCoords = new int[3];
public boolean isHit(int x, int y) {
return (Arrays.asList(xCoords).contains(x) && Arrays.asList(yCoords).contains(y));
}
}
Then you can:
if (Player.ship1.isHit(guess[0],guess[1])) {
....
}
At the very heart of it you have a little ways to go. You'll get better responses here if you start working at the problem then come back with specific problems you may have. Try to be as concise as possible when giving code snippets because not many people will spend much time going through an entire class to find a line or two giving issues.
Good luck!
---PS---
I wrote a battleship game about 3-4 years ago with some fairly advanced AI. I'll link it here:
https://github.com/GrahamBlanshard/AI-Battleship/blob/master/prograham/battleship/player/AIPlayer.java
First, I apologize for the... lame code (I was a much younger programmer, I swear!). If you want to view it to get hints that is fine. A brief explanation:
At the heart of it you need to create some form of datatype that stores his hits. Once a "hit" is scored you push it to the datatype, I used a Stack. The shots that are successful hits get stored on the stack until the ship is sunk. At that point it removes shots from the stack that belonged to the ship that just sunk. If there are shots still on the stack it knows it has hit a second ship during that process and continues to guess in the area.
To accomplish this, it goes through phases:
1) Shoot randomly until a hit.
2) Shoot around that shot (use a random(4) call to get N/S/E/W direction)
-- Keep doing this until you score a second shot
3) Create a "line" with the two points and fire along it until the ship sinks or...
4) Reverse the line and shoot the other direction.
Does that give you a good start to work with?
That's a lot of code to look at. So for now I will give some general suggestions that come to mind:
When the computer AI gets a "hit", set a "global" flag (more likely a class variable) and "remember" where the hit occured. On the following turns, guess the neighboring squares in some predetermined order (say north, south, east, west) until another hit is found. Then set another flag and on the next turn guess in the same direction as the second hit. The initial flag should only be reset when all three hits are found. This should fix the problem that a subsequent miss causes the computer AI to start guessing randomly again.