I have been working on some code to guide a 'robot' through a maze with multiple dead ends and 1 correct path to the goal like this:
I have used a stack to record the direction the robot is facing the first time it reaches a square with 3 or 4 possible exits and if all adjacent squares have already been visited, used pop() to make the robot return from the direction it first came from (opposite to direction arrived). At the end of the run the stack contains the direction arrived at all squares on the route to the target. Following the opposite directions of the stack would take the robot from the goal back to the start point. I am struggling to find out how to use this stack so that on the next run the robot will take the optimal path to reach the goal.
Some of my code:
private int pollRun = 0; // Incremented after each pass
private int explorerMode; // 1 = explore, 0 = backtrack
public void exploreControl(IRobot robot) {
byte exits = nonwallExits(robot);
int direction;
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
case 2: direction = corridor(robot); break;
case 3: direction = junction(robot); break;
default: direction = crossroads(robot); break;
}
if (exits == 1) {explorerMode = 0;}
robot.face(direction);
pollRun++;
}
public void backtrackControl(IRobot robot) {
byte exits = nonwallExits(robot);
int direction = IRobot.CENTRE;
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
case 2: direction = corridor(robot); break;
default: direction = junction(robot); break; // do nothing
}
if (exits > 2) {
if (passageExits(robot) > 0){
exploreControl(robot);
explorerMode = 1;
pollRun++;
return;
} else {
robot.setHeading(st.pop());
robot.face(IRobot.BEHIND);
pollRun++;
return;
}
}
robot.face(direction);
pollRun++;
}
public void optimal(IRobot robot) {
byte exits = nonwallExits(robot);
int direction;
int heading;
for(int i = 0; i < st.size(); i++) {
stNew.push(st.pop());
}
if (exits < 3) {
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
default: direction = corridor(robot); break;
}
robot.face(direction);
} else {
robot.setHeading(stNew.pop());
}
}
public void controlRobot(IRobot robot) {
if ((robot.getRuns() == 0) && (pollRun == 0)) {
robotData = new RobotData(); //reset the data store
explorerMode = 1;
}
if (robot.getRuns() = 1) {
optimal(robot);
return;
}
if (robot.getRuns() <= 0 && (nonwallExits(robot) >= 3)
&& (beenbeforeExits(robot) <= 0)) {
st.push(robot.getHeading());
}
if (explorerMode == 1) {
exploreControl(robot);
} else {backtrackControl(robot);}
}
The optimal method shows my attempt at solving it, however all it does is cause the robot to head straight at every junction
For example this maze,
Would leave me with the stack: EAST, EAST, SOUTH, SOUTH, EAST, SOUTH, SOUTH, EAST, EAST, SOUTH, SOUTH, EAST, EAST, EAST, SOUTH, EAST, SOUTH
It is true that this problem can be solved using a stack and an exhaustive search of the maze. There are more efficient methods but this one will work.
It's fairly difficult to know how your code is intended to function because you've only given a part of it. However in general these sorts of exhaustive searches make heavy use of recursion - which is a very common use case for stacks. I assume yours does the same though I can't see that code in the sample you've provided.
Here is some sample psuedo code for an exhaustive 'depth first' search. This code will end up with all possible solutions rather than just one. You should have a method that looks something like this in your code.
void findPath(Stack currentPath) {
if (currentPath.peek() == goal) {
solutions.add(currentPath);
} else {
for (Position next: currentPath.openPositions()) {
currentPath.push(next);
findPath(currentPath);
currentPath.pop();
}
}
}
The 'openPositions' method needs to explicitly stop any doubling back by looking at the current path - in other words it shouldn't return any positions that are already in the currentPath stack or you will end up with infinite recursion.
Because this finds all possible solutions you would then need to find the one with the shortest length as the optimal path. In your case it seems the mazes only have one path so you can exit as soon as you have found a path.
A final note: you've tried to mix the task of setting the direction the robot needs to turn with the task of finding a path through the maze. I recommend keeping these separate. Use the algorithm above to find a path (or a more efficient one such as a*) and then once you have paths walk through it to determine the list of directions for the robot.
Related
I'm writing snake game, so I've made a SnakeLogic class that represents the logical model of the snake.
The implementation is the following: the snake consist of segments, each segment holds its start location, its length and direction of movement. This is the full code of the Segment class (inner class of SnakeLogic):
protected class Segment{
public Point location;
public SnakeDirection dir;
public int length;
public Segment(Point l, SnakeDirection dir,int length){
location=l;
this.dir=dir;
this.length=length;
}
}
Segments are held with a LinkedList:
private LinkedList<Segment> nodes;
When the direction is changed, the new segment is added at the beginning of LinkedList:
public void setDirection(SnakeDirection dir){
//gets location and direction of first segment
Point head = nodes.getFirst().location;
SnakeDirection currentDir = nodes.getFirst().dir;
//if direction isn't changed, return
if (currentDir == dir) return;
//ignores directions that are opposite to current one.
switch(currentDir){
case LEFT:
if (dir==SnakeDirection.RIGHT) return;
break;
case RIGHT:
if (dir==SnakeDirection.LEFT) return;
break;
case UP:
if (dir==SnakeDirection.DOWN) return;
break;
case DOWN:
if (dir==SnakeDirection.UP) return;
break;
}
//adds new segment with 0 length,current first segment's location
//and given direction
nodes.addFirst(new Segment(head,dir,0));
}
The method Next() calculates movement of the snake. Depending on the direction of movement, the location of first segment is changed; if the snake consists of more than 1 segment, then the length of the first segment increases by the given value (stepSize) and the length of the last one is decreases by this value. If the length of last segment becomes <=0, then the last segment is removed (if length is less than zero then remainder is substracted from the current last segment).
public void Next() {
SnakeDirection headDir = nodes.getFirst().dir;
switch(headDir){
case LEFT:
nodes.getFirst().location.x-=stepSize;
break;
case RIGHT:
nodes.getFirst().location.x+=stepSize;
break;
case UP:
nodes.getFirst().location.y-=stepSize;
break;
case DOWN:
nodes.getFirst().location.y+=stepSize;
break;
}
if (nodes.size()>1){
nodes.getFirst().length+=stepSize;
int newLength = nodes.getLast().length-stepSize;
if (newLength<=0){
nodes.removeLast();
nodes.getLast().length-=newLength;
}
else{
nodes.getLast().length=newLength;
}
}
}
When I started to test it, I found out that for some strange reason the locations of the other segments changes with the first segment's location, when they must stay in place. It looks like this:
Everything else seems to work fine. What's wrong with my code?
When you create a new Segment you pass the location object of the first segment, instead of a COPY of that location. So all of your segment objects share the VERY SAME location object. If you modify it inside the new segment it will be also modified in ALL the other segments, because it's the same object.
(When you pass an object then you pass the reference of the object, not the valuu of the object.)
So instead of this line:
Point head = nodes.getFirst().location;
use this:
Point head = new Point(nodes.getFirst().location);
It's impossible to know where your problem is without an MCVE, but the design seems overly complex. Instead of working with segments, work with points.
Assuming your point looks like
class Point {
int x, y;
// getters/setters if you want
}
Then the snake is represented by a list of points and a direction:
class Snake {
List<Point> body = new LinkedList<>();
Point head; // easier to separate the head, but you can do with including it in the list
Direction dir;
}
to which you can add the next() method to calculate the representation of the snake:
void next() {
int temp1x = head.x;
int temp1y = head.y;
switch(dir) {
case LEFT:
head.x -= stepSize;
break;
//...
}
int temp2x, temp2y;
for (Point point : points) {
temp2x = point.x;
temp2y = point.y;
point.x = temp1x;
point.y = temp1y;
temp1x = temp2x;
temp1y = temp2y;
}
}
I'll leave it to you to simplify the implementation (you can work with Point instead of separate x and y ints if you expand the Point class to allow it).
Notes:
LinkedList is indeed a good choice for the list implementation.
Method names start with a lowercase (next instead of Next).
I am trying to implement a chess game with alpha beta pruning. The following is almost working, but it returns wrong moves.
For example, the following can occur.
White (user) to move, white king position - a1 / Black (computer), black king position - h1
White moves its king from a1 - a2, then black return the move g2 - g1???
It appears that the computer returns a move for the wrong node (board representation), as if the best evaluation of a given board position is not being propagated all the way back up the tree. So in one of the simulated positions explored, the computer "imagines" its king moving to g2 and then returns the move to be made from this position, not realising that this position is a simulated position and not the representation of the actual board (the root node?).
How can I correct the code to make the computer return a move for the actual board representation and not one of the simulations by mistake?
Thank you.
Initial call alphaBeta(3, ChessEngine.invertBoard(ChessEngine.board), -10000, 10000, true);
private static int alphaBetaEvaluate = 0;
private static int alphaBetaSelectedSquare = 0;
private static int alphaBetaMoveToSquare = 0;
public static int alphaBeta(int depth, char[] board, int alpha, int beta, boolean maxPlayer) {
//create a copy of the board
char[] boardCopy = board.clone();
//if terminal state has not been met, keep searching
if (maxPlayer == true && depth > 0) {
//for all of the moves that max can make
for (int i = 0; i < board.length; i++) {
for (int move : ChessEngine.getValidMoves(i, boardCopy)) {
//make the move
boardCopy[move] = boardCopy[i];
boardCopy[i] = '.';
alphaBetaEvaluate = rating(board, boardCopy, i, move);
//store the best move to make
int temp = alphaBeta(--depth, ChessEngine.invertBoard(boardCopy), -10000, 10000, false);
if (temp > alpha) {
alphaBetaSelectedSquare = i;
alphaBetaMoveToSquare = move;
alpha = temp;
}
//reset the board for the next simulated move
boardCopy = board.clone();
if (beta <= alpha) {
break;
}
}
}
return alpha;
} else if (maxPlayer == false && depth > 0) {
//for all of the moves that min can make
for (int i = 0; i < board.length; i++) {
for (int move : ChessEngine.getValidMoves(i, boardCopy)) {
//make the move
boardCopy[move] = boardCopy[i];
boardCopy[i] = '.';
beta = Math.min(beta, alphaBeta(--depth, ChessEngine.invertBoard(boardCopy), -10000, 10000, true));
//reset the board for the next simulated move
boardCopy = board.clone();
if (beta <= alpha) {
break;
}
}
}
return beta;
}
return alphaBetaEvaluate;
}
I dont get your implementation after all. First of all what you want to do is create a tree. A decision tree and propagates the decision up. You want to maximize your evaluation and also expect that the enemy will select the move that minimizes your evaluation in return.
So inverting the board does not sound so reasonable for me unless you know that the evaluation you do uppon the situation is correctly adjusting.
Another serious problem for me is that you always call the min/max for the next move with -10k and 10k as the bounderies for alpha and beta. This way your algorithm does not 'learn' from previous moves.
What you need is to check the algorithm again (wikipedia for instance, which I used) and see that they use alpha and beta being modified by former evaluation. This way the calculation in higher depth can firstly stop and secondly evaluate the best move better.
I am no expert in this. its decades ago when I wrote my implementation and I used something different.
Another idea is not to use min and max within the same method but use the min and max methods instead. It makes it more likely you spot other defects.
Also do not use two kings for evaluation. There is no goal in that. Two kings are random, cant win. One thing might be two knights or four queens and alike. It is not so random and you can see the queens dancing around without being able to catch each other. Or use three knights versus a single queen.
And try to create yourself some unit tests around your other parts. Just to insure that the parts are working correctly independently. And why are you using characters? Why not using enums or objects. You can reuse the objets for each field (its more like kinds of figures).
But anyhow this is style and not algorithm correctness.
I've been working on a small project trying to learn some basics, I'm trying to make a clone of space invaders. I'm not really experienced (which is why I'm doing this) and I've run into something I've never had a problem with before.
My problem is with loops, I've used basic loops but I'm using some nested loops now and it's giving me some problems. Here is the code that breaks my project
public void moveLevel(int l, ArrayList ms){
switch(l){
case 1:{
centerX = 60;
centerY = 35;
alienArray = ms;
moveRight = true;
while(moveRight == true){
x += 1;
}
}
case 2:{
}
}
}
I can show more code if anybody thinks it would help, but basically, this block gets the level number (l) passed to it as well as an array list which holds about 15 'alien' objects. The line 'x+=1' is what moves the aliens (the location of each alien is x).
This code is called from another function which is constantly called from a swing timer.
What is happening, is that when the code reaches this point, the program seems to freeze. I have a button on the JPanel that doesn't react, I have a hotkey to close the application which doesn't react, and exiting the application with the mouse does nothing (I've included a DefaultCloseOperation(EXIT_ON_CLOSE) in the JFrame which works without this while loop).
if I replace the word 'while' with 'if', like below the code works fine.
public void moveLevel(int l, ArrayList ms){
switch(l){
case 1:{
centerX = 60;
centerY = 35;
alienArray = ms;
moveRight = true;
if(moveRight == true){
x += 1;
}
}
case 2:{
}
}
}
I've also tried a do, while loop.
I have no idea what the problem is, I assume it's a logic error but it seems fairly straightforward to me. Again, if anybody would like to see the rest of the code I can post it. Otherwise, if anybody has any suggestions I would appreciate it. I'm open to specific advice or just general advice on code efficiency. Thanks
ANSWER
Okay I've gotten my code moving forward thanks to Ted Hopp, who commented below. Looks like an infinite loop was being executed within the case statement.
Here is my fix for any who are curious, I've included the function that calls the function from the original post.
public void move(int l, ArrayList ms){
level = l;
alienArray = ms;
moveLevel(level, alienArray);
centerX += horizontal;
centerY += vertical;
x += horizontal;
y += vertical;
if(moveRight == true){
horizontal = 1;
vertical = 0;
System.out.println(centerX);
}
else x -= 1;
}
public void moveLevel(int l, ArrayList ms){
switch(l){
case 1:{
alienArray = ms;
moveRight = true;
if(moveRight == true){
if (centerX > 300){
moveRight = false;
}
if(moveRight == false){
if(centerX < 100){
}
}
}
}
break;
case 2:{
}
}
}
This just basically moves all of the aliens to the right and stops so far, but I've moved past the original issue.
You have an infinite loop:
moveRight = true;
while(moveRight == true){
x += 1;
}
because moveRight enters the loop as true and will never become false by repeatedly executing x += 1;.
It looks like you're trying to get something to animate continuous motion to the right. However, this is not the way to do it because your loop won't let any other part of your code (including the rendering) to execute. Without knowing more about your code structure, it's hard to provide specific advice, but I suggest you read up on animation loops. There are lots of tutorial resources on the web on the subject. Look for things like java swing animation loop.
Also, it looks like you need a break; statement at the end of each case.
You loop, as in its current state, is infinite. You'll need to have a condition where your boolean variablemoveRight will be set to false.
Here is the algorithm (not working) Please let me know where the error is
Thanks
private void checkSouth(Location point, int player) {
//Loop through everything south
boolean isthereAnOppositePlayer=false;
int oppositePlayer=0;
//Set opposite player
if (player==1) {
oppositePlayer=2;
}else{
oppositePlayer=1;
}
for (int i = point.getVertical(); i < 8; i++) {
//Create a location point with the current location being compared
MyLocation locationBeingChecked= new MyLocation();
locationBeingChecked.setHorizontal(point.getHorizontal());
locationBeingChecked.setVertical(i);
int value = board[locationBeingChecked.getVertical()][locationBeingChecked.getHorizontal()];
//If the first checked is the opposite player
if (value==oppositePlayer) {
//Then potential to evaluate more
isthereAnOppositePlayer=true;
}
//If it isn't an opposite player, then break
if(!isthereAnOppositePlayer && value!=0){
break;
}
//If another of the player's piece found or 0, then end
if (isthereAnOppositePlayer && value==player || isthereAnOppositePlayer && value==0) {
break;
//end
}
//Add to number of players to flip
if(isthereAnOppositePlayer && value==oppositePlayer && value!=0){
//add to array
addToPiecesToTurn(locationBeingChecked);
}
}
}
It looks like the locations that got rotated back to the other player are the exact same as those rotated during the first move. I would guess that the array being populated by addToPiecesToTurn is perhaps not being cleared out between each move, so all the previous locations are still in there.
If you are storing the pieces to be turned in an ArrayList, you can use the clear() method to erase the contents of the collection between each turn.
Another possible problem is that you are checking for the opposite player, and then instantly beginning to populate addToPiecesToTurn. However, the pieces in that direction are not necessarily valid to be rotated unless they are "sandwiched" in by a second location containing the current player's piece. I don't think your code is properly checking for that case; when that happens, you'll want to somehow skip flipping those pieces to the other player, such as clearing out the array of piecesToTurn.
Edit: Looking at your current solution where you are implementing every direction separately, you are going to have a lot of duplicated code. If you think about what it means to walk along a certain direction, you can think of it as adjusting the x/y value by a "step" amount. The step amount could be -1 for backwards, 0 for no move, or 1 for forwards. Then you could create a single method that handles all directions without duplicating the logic:
private void checkDirection(Location point, int player, int yStep, int xStep) {
int x = point.getHorizontal() + xStep;
int y = point.getVertical() + yStep;
MyLocation locationBeingChecked = new MyLocation();
locationBeingChecked.setHorizontal(x);
locationBeingChecked.setVertical(y);
while (isValid(locationBeingChecked)) {
// do the logic here
x += xStep;
y += yStep;
locationBeingChecked = new MyLocation();
locationBeingChecked.setHorizontal(x);
locationBeingChecked.setVertical(y);
}
}
You would need to implement isValid to check that the location is valid, i.e., in the board. Then you could call this method for each direction:
// north
checkDirection(curPoint, curPlayer, -1, 0);
// north-east
checkDirection(curPoint, curPlayer, -1, 1);
// east
checkDirection(curPoint, curPlayer, 0, 1);
// etc
This is the sort of problem that is ripe for some unit testing. You could very easily set up a board, play a move, and validate the answer, and the test results would give plenty of insight into where your expectations and reality diverge.
why didn't you use a 2d array ?
each cell would contain an enum : EMPTY, PLAYER_1, PLAYER_2 .
then, in order to go over the cells, you simply use loops for each direction.
for example, upon clicking on a cell , checking to the right would be:
for(int x=pressedLocation.x+1;x<cells[pressedLocation.y].length;++x)
{
Cell cell=cells[pressedLocation.y][x];
if(cell==EMPTY||cell==currentPlayerCell)
break;
cells[pressedLocation.y][x]=currentPlayerCell;
}
checking from top to bottom would be:
for(int y=pressedLocation.y+1;y<cells.length;++y)
{
Cell cell=cells[y][pressedLocation.x];
if(cell==EMPTY||cell==currentPlayerCell)
break;
cells[y][pressedLocation.x]=currentPlayerCell;
}
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.