I have a problem where it needs to navigate a spider in a grid system (X, Y co-ordinate) with proper instruction. Initially, the spider is in (0,0) and face towards the positive Y axis.
There are 3 possible instructions for the navigation: 'F' for forward (1 grid in the same direction ), 'R' for right turn (90 degree) and 'L' for left turn (90 degree) and initially, the spider faces towards positive Y axis.
Say, if I pass the direction String of "LFF", the position should be (-2,0). I solve the problem and the current state of code is as following,
public static void spiderNavigator(String str ){
if( str == null || str.length() == 0)
return;
int [] initial = {0,0};
boolean xPos = false, xNeg = false, yPos = true, yNeg = false;
char[] ch = str.toCharArray();
for( char c: ch){
// the initial position of the spider is towards the positive Y axis
if(c == 'L'){
if(xPos){
xPos = false;
yPos = true;
}
else if ( xNeg){
xNeg = false;
yNeg = true;
}
else if(yPos){
xNeg = true;
yPos = false;
}
else if (yNeg){
yNeg = false;
xPos = true;
}
}
else if ( c == 'R'){
if(xPos){
xPos = false;
yNeg = true;
}
else if ( xNeg){
yPos = true;
xNeg = false;
}
else if(yPos){
yPos = false;
xPos = true;
}
else if (yNeg){
yNeg = false;
xNeg = true;
}
}
else if (c == 'F'){
if(xNeg){
initial[0] -= 1;
}
else if (xPos){
initial[0] += 1;
}
else if (yNeg){
initial[1] -=1;
}
else if( yPos){
initial[1] += 1;
}
}
}
System.out.println(Arrays.toString(initial));
}
However, the code feels quite ugly even to me. How can I design the algorithm in better way ?
Here's a shorter and more elegant solution:
public static void spiderNavigator(String str) {
if (str == null || str.length() == 0)
return;
int[] initial = {0, 0};
int angle = 90;
char[] ch = str.toCharArray();
for (char c : ch) {
if (c == 'L') {
angle = (angle + 90) % 360;
} else if (c == 'R') {
angle = (angle - 90) % 360;
} else if (c == 'F') {
initial[0] += (int) Math.cos(Math.toRadians(angle));
initial[1] += (int) Math.sin(Math.toRadians(angle));
}
}
System.out.println(Arrays.toString(initial));
}
Angle represents the direction spider is facing, and using trigonometric functions you can easily calculate where it should move based on current position and an angle it is facing.
Here is how I would approach it.
Have a direction variable spider_dir (where your spider is going to go now). It will store 4 different kind of values (like U, R, D, L).
Have a function change_direction which takes a current direction a and value L or R and returns a new direction. Notice that if L is passed you need to take previous circular value in array of values (['U', 'R', 'D', 'L']) of your previous value. If R than the next circular value.
Have a hash that maps your direction to your steps (assume +x, +y). U will be (0, 1), L will be (-1, 0).
Now that you have this simply iterate through your string and if you see F move add value to your current position depending on your spider_dir. If you see anything else - change your spider_dir depending on where to rotate and spider_dir
Here is a version built on a similar concept to the very nice answer by #MateuszDryzek, but without using trigonometric functions.
public static void spiderNavigator(String str) {
if (str == null || str.isEmpty())
return;
int x = 0, y = 0, dir = 0;
for (char c : str.toCharArray())
if (c == 'R')
dir = (dir + 1) % 4; // dir++: 0 -> 1 -> 2 -> 3 -> 0
else if (c == 'L')
dir = (dir + 3) % 4; // dir--: 3 -> 2 -> 1 -> 0 -> 3
else if (c == 'F')
if (dir == 0)
y++; // 0: Up
else if (dir == 1)
x++; // 1: Right
else if (dir == 2)
y--; // 2: Down
else
x--; // 3: Left
System.out.printf("(%d,%d)%n", x, y);
}
Related
Write a method named consecutive that accepts three integers as parameters and returns true if they are three consecutive numbers; that is, if the numbers can be arranged into an order such that there is some integer k such that the parameters' values are k, k+1, and k+2. Your method should return false if the integers are not consecutive. Note that order is not significant; your method should return the same result for the same three integers passed in any order.
For example, the calls consecutive(1, 2, 3), consecutive(3, 2, 4), and consecutive(-10, -8, -9) would return true. The calls consecutive(3, 5, 7), consecutive(1, 2, 2), and consecutive(7, 7, 9) would return false.
This is what I have so far and keep getting infinite loop error and skipped tests
public boolean consecutive(int x, int y, int z) {
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2)))
||((z < y && ((z - x) == 1) && ((y - x) == 2))))
{
return true;
} else if (((y < x && y < z)&& (x < z && ((x - y) == 1) && ((z - y) == 2)))
|| ((z < x && ((z - y) == 1) && ((x - y) == 2))))
{
return true;
} else if (((z < x && z < y)&& (y < x && ((y - z) == 1) && ((x - z) == 2)))
||((x < y && ((x - z) == 1) && ((y - z) == 2))))
{
return true;
} else {
return false;
}
What you have there is serious overkill and pretty much unreadable to anyone who hasn't spent a large proportion of their career in C :-)
You should always strive for readability (and hence maintainability) first, reverting to less readable code only when absolutely necessary. Even if you do revert, you should then document why and what you've done so the next poor soul that has to maintain your code won't be cursing your name.
For this specific case, what you are attempting can be achieved in much simpler code such as the following (pseudo-code):
def areConsecutive(a, b, c):
if a > b: swap a, b
if b > c: swap b, c
if a > b: swap a, b
return (b - a == 1) and (c - b == 1)
The three if statements are simply an unrolled bubble sort to ensure a, b and c are in ascending order, then you simply check to ensure the difference between them is one in both cases.
There's no need to put them into a list or array to sort them since sorting three items is relatively easy (the swap can be done with int t = a; a = b; b = t;).
In terms of Java code (once you've moved the input to outside the function where it belongs), you'd end up with something like:
bool areConsecutive(int a, int b, int c) {
int t;
if (a > b) { t = a; a = b; b = t; }
if (b > c) { t = b; b = c; c = t; }
if (a > b) { t = a; a = b; b = t; }
return (b - a = 1) && (c - b == 1);
}
When you are passing value why using scanner?
Remove those lines and its working. You can use another logic to determine consecutive numbers.
public boolean consecutive(int x, int y, int z) {
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2))) ||((z < y && ((z - x) == 1) && ((y - x) == 2)))) {
return true;
} else if (((y < x && y < z)&& (x < z && ((x - y) == 1) && ((z - y) == 2))) ||
((z < x && ((z - y) == 1) && ((x - y) == 2)))){
return true;
} else if (((z < x && z < y)&& (y < x && ((y - z) == 1) && ((x - z) == 2))) ||((x < y && ((x - z) == 1) && ((y - z) == 2)))){
return true;
} else
return false;
}
Just Remove these code:
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
Or use Like this:
public class test{
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
System.out.println("Result:" + consecutive(x, y, z));
}
public static boolean consecutive(int x, int y, int z) {
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2))) || ((z < y && ((z - x) == 1) && ((y - x) == 2)))) {
return true;
} else if (((y < x && y < z) && (x < z && ((x - y) == 1) && ((z - y) == 2)))
|| ((z < x && ((z - y) == 1) && ((x - y) == 2)))) {
return true;
} else if (((z < x && z < y) && (y < x && ((y - z) == 1) && ((x - z) == 2))) || ((x < y && ((x - z) == 1) && ((y - z) == 2)))) {
return true;
} else {
return false;
}
}
}
Create a list with the numbers, sort it and do de diference between the elements:
public static boolean myConsecutive(int x, int y, int z) {
final List<Integer> list = new ArrayList<>();
list.add(x);
list.add(y);
list.add(z);
Collections.sort(list);
return (list.get(2) - list.get(1) == 1 && list.get(1) - list.get(0) == 1);
}
The consecutive method is passed with three values , then why you are reading from the console.
create an array with the size of 3 elements.
Sort the arrays using Arrays.Sort method
Check the difference between the second and the first number is 1 and difference between Third and the second number is 1.
Code :
public boolean consecutive(int x, int y, int z) {
int [] numbers = new int [3];
numbers[0] = x;
numbers[1] = y;
numbers[2] = z;
Arrays.sort(numbers);
boolean isConsecutive = (numbers[1]==numbers[0]+1)&&(numbers[2]==numbers[1]+1);
return isConsecutive;
}
Our assignment is to make a simple 'pac-man' game that just eats the trash inside a rectangle. The trash being "*".
My code so far:
public class Sweeper
{
public static void main(String[] args) throws InterruptedException
{
//************************************************************
// Variable set up
int sy = 10, sx= 10; // Box size
int x= 5, y= 5; // Start point
int maxmc = 8; // Max distance moving
char [] [] env = new char[sy][sx];
int right = sx - 2, top = sy - 2, left = sx - (right + 1) , bottom = sy - (top + 1);
//************************************************************
//************************************************************
// Filling the grid with Stars
for(int i=0; i<sy;i++)
{
for(int j =0; j < sx; j++)
{
env[i][j] = '*';
}
}
//************************************************************
int mc = 0, direction = 0, count = (right * top);
// The actual game
while(count != 0)
{
direction = (int)(Math.random() * 3);
// Display
//System.out.println("\n\n\n\n\n");
for(int i = 0; i < sy; i++)
{
for(int j= 0; j< sx; j++)
{
System.out.print(env[i][j]);
}
System.out.println();
}
System.out.println("\n\n\n");
Thread.sleep(700);
System.out.println(direction);
if((x <= right && x >= left && y <= top && y >= bottom))
{
if(env[x][y] == ' ')
{
// RIGHT
if(direction == 0)
{
env[y][x] = '#';
x--;
env[y][x+2] = ' ';
}
// left
else if(direction == 1)
{
env[y][x] = '#';
x++;
env[y][x-2] = ' ';
}
// TOP
else if(direction ==2)
{
env[y][x] = '#';
y--;
env[y+2][x] = ' ';
}
// bottom
else if(direction ==3)
{
env[y][x] = '#';
y++;
env[y-2][x] = ' ';
}
}
else
{
if(direction == 0)
{
env[y][x] = '#';
x--;
env[y][x+2] = ' ';
}
// left
else if(direction == 1)
{
env[y][x] = '#';
x++;
env[y][x-2] = ' ';
}
// TOP
else if(direction ==2)
{
env[y][x] = '#';
y--;
env[y+2][x] = ' ';
}
// bottom
else if(direction ==3)
{
env[y][x] = '#';
y++;
env[y-2][x] = ' ';
}
// Keeps track of the trash
count--;
}
}
}
}
}
My problem:
It copies the '#' and stops moving sometimes.
Im trying to make it move aorund until all the inside 8x8 star symbols are all gone.
Just to clean your code: your moving algorithm is duplicated the (4 if/else statements) . You need to put only count-- in a if(! env[x][y] == ' ') statement.
For your problem: your badly checkin your bounds condition, you do
env[y][x] = '#';
x--;
env[y][x+2] = ' ';
But if x=1, then after the code it's 0, and because you have
if((x <= right && x >= left && y <= top && y >= bottom))
With x=0 and right=0, then it never enter the statement, count cannot change, and it loops infinetly.
Consider the following code :
if (xPoint > 0 && yPoint > 0) {
m_navigations = Directions.SouthEast;
}
else if (xPoint > 0 && yPoint < 0) {
m_navigations = Directions.NorthEast;
}
else if (xPoint < 0 && yPoint > 0) {
m_navigations = Directions.SouthWest;
}
else if (xPoint < 0 && yPoint < 0) {
m_navigations = Directions.NorthWest;
}
else if (xPoint == 0 && yPoint < 0) {
m_navigations = Directions.North;
}
else if (xPoint == 0 && yPoint > 0) {
m_navigations = Directions.South;
}
else if (xPoint > 0 && yPoint == 0) {
m_navigations = Directions.East;
}
else if (xPoint < 0 && yPoint == 0) {
m_navigations = Directions.West;
}
This is quite ugly , and I want to use switch case , but how can I use switch with 2 variables ?
I thought about something like this - the answer of #Frits van Campen , but I need to use > and < operators ...
Thanks
You can do everything with enums. I created examples for the first two values, you can continue with the rest.
public enum Direction
{
SouthEast(1,1),
NorthEast(1,-1);
int _xPoint, _yPoint;
Direction(int xPoint, int yPoint)
{
_xPoint = xPoint;
_yPoint = yPoint;
}
public static Direction getDirectionByPoints(int xPoint, int yPoint)
{
for (Direction direction : Direction.values())
{
if( Integer.signum(xPoint) == direction._xPoint
&& Integer.signum(yPoint) == direction._yPoint )
{
return direction;
}
}
throw new IllegalStateException("No suitable Direction found");
}
}
So you can just call:
m_navigations = Direction.getDirectionByPoints(xPoint,yPoint);
Use signum to get -1, 0 or 1 on the direction like this:
String direction = Integer.signum(xPoint)+","+Integer.signum(yPoint);
switch(direction){
case "1,1":
m_navigations = Directions.SouthEast;
break;
case "-1,0"
m_navigations = Directions.West;
break;
etc..
}
The simplest and easiest solution is to use multidimensional arrays.
public class CalculateDirections {
private final static Directions DIRECTION_MAP[][] = {
{Directions.NorthWest, Directions.North, Directions.NorthEast},
{Directions.West, null, Directions.East},
{Directions.SouthWest, Directions.South, Directions.SouthEast},
};
public static void main(String[] args) {
int x = Integer.valueOf(args[0]);
int y = Integer.valueOf(args[1]);
int signumX = Integer.signum(x);
int signumY = Integer.signum(y);
Directions direction = DIRECTION_MAP[signumY + 1][signumX + 1];
System.out.println(direction);
}
}
enum Directions {
SouthEast, NorthEast, SouthWest, NorthWest, North, South, East, West
}
There are several advantages:
No if/else cascades which take some runtime and are hard to manage.
No creation of temporary Strings. In a tight game loop this may be important.
No linear search through lists or arrays.
Similar to other answers but without strings. Just for fun :-)
public Directions getDirection(int xPoint, int yPoint) {
int num = 8 * (xPoint == 0 ? 0 : xPoint > 0 ? 1 : 2);
num += yPoint == 0 ? 0 : yPoint > 0 ? 1 : 2;
switch (num) {
case 01:
return Directions.South;
case 02:
return Directions.North;
case 010:
return Directions.East;
case 011:
return Directions.SouthEast;
case 012:
return Directions.NorthEast;
case 020:
return Directions.West;
case 021:
return Directions.SouthWest;
case 022:
return Directions.NorthWest;
}
return Directions.None;
}
boolean xNeg = xPoint < 0;
boolean yNeg = yPoint < 0;
boolean xZero = xPoint == 0;
boolean yZero = yPoint == 0;
We have four bits, we have 2^4 possibilities, an array of Directions may do the rest...
int index =
((xNeg ?1:0)<<3)|
((yNeg ?1:0)<<2)|
((xZero?1:0)<<1)|
((yZero?1:0)<<0);
Directions dir = directions[index];
with directions a static final array of Directions initialized at class loading time.
static final Directions[] directions = {
Direction.NorthEast, // false, false, false, false ==> x > 0 && y > 0
Direction.East, // false, false, false, true ==> x > 0 && y == 0
Direction.North, // false, false, true , false ==> x == 0 && y > 0
...
}
Indexing an array with an integer computed from ternaries, shift and or operators is less CPU consuming than a string concatenation used in a string switch and works well from Java 1.0.
At the moment :
String direction = Integer.signum(xPoint) + "|" + Integer.signum(yPoint);
switch(direction)
{
case "1|1":
{m_navigations = Directions.SouthEast; break;}
case "1|-1":
{m_navigations = Directions.NorthEast; break;}
case "-1|1":
{m_navigations = Directions.SouthWest; break;}
case "-1|-1":
{m_navigations = Directions.NorthWest; break;}
case "0|-1":
{m_navigations = Directions.North; break;}
case "0|1":
{m_navigations = Directions.South; break;}
case "1|0":
{m_navigations = Directions.East; break;}
case "-1|0":
{m_navigations = Directions.West; break;}
default: break;
}
Now I'll try what #danieln has suggested .
I am a beginner in java.
I have been working on an maze problem trying it solve it by recursion.
I have written the code which seems to work on few inputs and not others.
The input is a maze consisting of 0's and 1's. # is the start and # is the exit.0 is wall and 1's are open.The output will be the hops from # to #.
Though i am solving the problem by recursion,I must be going wrong with the logic.
Please let me know where I am wrong.
Class practisenumwords
import java.util.Scanner;
class practisenumwords {
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int r=in.nextInt();
int c=in.nextInt();
maze maz=new maze(r,c); /*input in string copied to array*/
char[] ch;
ch = "00000000111111101111011001101##11100".toCharArray();
int l=0;
for(int i=0;i<r;i++)
{
for(int j=0;j<c;j++) /*initialising the maze elements*/
{
maz.m[i][j]=new cells();
maz.m[i][j].c=ch[l];
maz.m[i][j].row=i;
maz.m[i][j].col=j;
l++;
}
}
for(int i=0;i<r;i++) /*print the input maze */
{
for(int j=0;j<c;j++)
{
System.out.print(""+maz.m[i][j].c);
}
System.out.println();
}
maz.escape();
maz.find(maz.startx,maz.starty,maz.hops);
}
}
Class cells
class cells {
char c;
int row;
int col;
boolean done=false; /*initially all cells are unvisited*/
}
Class maze
class maze{
maze (int a,int b){
rows=a;
cols=b;
m=new cells[rows][cols];
}
int rows;
int cols;
cells[][] m;
int startx,starty;
int hops=0;
void escape()
{
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
if(m[i][j].c=='#')
{
startx=i;
starty=j;
System.out.println(startx+" "+starty);
}
}
}
}
void find(int x,int y,int h)
{
if ((x+1<rows && m[x+1][y].c=='#' && m[x+1][y].done!=true)
||(x-1>=0 && m[x-1][y].c=='#' && m[x-1][y].done!=true)
||(y+1<cols && m[x][y+1].c=='#' && m[x][y+1].done!=true)
||(y-1>=0 && m[x][y-1].c=='#' && m[x][y-1].done!=true)){
h++;
System.out.println(h);
}
else
{
if(x-1>=0 && m[x-1][y].c=='1' && m[x-1][y].done!=true){ /*north cell*/
m[x][y].done=true;
h++;
find(x-1,y,h);
}
if(x+1<rows && m[x+1][y].c=='1' && m[x+1][y].done!=true){ /*south cell*/
m[x][y].done=true;
h++;
find(x+1,y,h);
}
if(y+1<cols && m[x][y+1].c=='1' && m[x][y+1].done!=true){ /*east cell*/
m[x][y].done=true;
h++;
find(x,y+1,h);
}
if(y-1>=0 && m[x][y-1].c=='1' && m[x][y-1].done!=true){ /*west cell*/
m[x][y].done=true;
h++;
find(x,y-1,h);
}
}
}
}
Now,i get the right output for the inputs as the 1 in program.
000000
001111
111011
110110
01101#
#11100
output- 12 (obtaining right output)
00#000
001111
111011
110110
011011
#11100
output- 7 (obtaining right output)
BUT NOT FOR OTHER INPUTS like
0 0 0 0 # 0
0 1 0 1 1 0
1 1 1 1 0 1
0 1 0 1 0 0
0 0 # 1 1 1
0 1 1 0 0 1
correct output - 6 output obtained -7
Also the output changes with the order in which the adjacent cells are checked.
Honestly, I'd implement your recursive function a little differently:
And there's no need to check whether a bool value is != true, !boolValue is fine.
int find(int x,int y,int h)
{
int result = -1;
if ((x+1<rows && m[x+1][y].c=='#' && !m[x+1][y].done)
||(x-1>=0 && m[x-1][y].c=='#' && !m[x-1][y].done)
||(y+1<cols && m[x][y+1].c=='#' && !m[x][y+1].done)
||(y-1>=0 && m[x][y-1].c=='#' && !m[x][y-1].done)){
return h + 1;
}
else
{
if(x-1>=0 && m[x-1][y].c=='1' && !m[x-1][y].done){ /*north cell*/
m[x][y].done=true;
result = find(x-1,y,h + 1)
if (result > -1) {
return result;
}
m[x][y].done=false;
}
Implement the other three directions the same way, then result should still be -1 if no solution was found.
return result;
}
In a fast reading I notice:
if(...) {
...
h++;
find(x-1,y,h);
}
For each if-block.
Inside second if-block h == h+2 when first if-condition is satisfied and the same goes for third and fourth if-block
Maybe you should write:
if(...) {
...
// h++;
find(x-1,y,h+1);
}
int find(int x, int y, int h) {
if ((x + 1 < rows && m[x + 1][y].c == '#' && m[x + 1][y].done != true)
|| (x - 1 >= 0 && m[x - 1][y].c == '#' && m[x - 1][y].done != true)
|| (y + 1 < cols && m[x][y + 1].c == '#' && m[x][y + 1].done != true)
|| (y - 1 >= 0 && m[x][y - 1].c == '#' && m[x][y - 1].done != true)) {
h++;
finish = true;
return h;
} else {
if (x - 1 >= 0 && m[x - 1][y].c == '1' && m[x - 1][y].done != true
&& !finish) { /* north cell */
m[x][y].done = true;
int temp = find(x - 1, y, h);
if (temp != 0)
h = temp + 1;
return h;
}
if (x + 1 < rows && m[x + 1][y].c == '1'
&& m[x + 1][y].done != true && !finish) { /* south cell */
m[x][y].done = true;
int temp = find(x + 1, y, h);
if (temp != 0)
h = temp + 1;
return h;
}
if (y + 1 < cols && m[x][y + 1].c == '1'
&& m[x][y + 1].done != true && !finish) { /* east cell */
m[x][y].done = true;
int temp = find(x, y + 1, h);
if (temp != 0)
h = temp + 1;
return h;
}
if (y - 1 >= 0 && m[x][y - 1].c == '1' && m[x][y - 1].done != true
&& !finish) { /* west cell */
m[x][y].done = true;
int temp = find(x, y - 1, h);
if (temp != 0) {
h = temp + 1;
}
return h;
}
return 0;
}
}
Also include a boolean finish = false; in your maze class.
and also change the return of the main function
maz.hops = maz.find(maz.startx, maz.starty, maz.hops);
System.out.println(maz.hops);
I'm creating a simple engine that plays Othello, using minimax with alpha beta cuts.
It's playing well, but sometimes i get a weird index out of bounds exception (near the
endgame, always).
Here' my algorithm
private float minimax(OthelloBoard board, OthelloMove best, float alpha, float beta, int depth)
{
calls++;
float bestResult = -Float.MAX_VALUE;
OthelloMove garbage = new OthelloMove();
int state = board.getState();
int currentPlayer = board.getCurrentPlayer();
if (state == OthelloBoard.STATE_DRAW)
return 0.0f;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.BLACK))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.WHITE))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.WHITE))
return -Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.BLACK))
return -Float.MAX_VALUE;
if (depth == maxDepth)
return OthelloHeuristics.eval(currentPlayer, board);
ArrayList<OthelloMove> moves = board.getAllMoves(currentPlayer);
for (OthelloMove mv : moves)
{
board.makeMove(mv);
alpha = - minimax(board, garbage, -beta, -alpha, depth + 1);
board.undoMove(mv);
if (beta <= alpha)
return alpha;
if (alpha > bestResult)
{
best.setFlipSquares(mv.getFlipSquares());
best.setIdx(mv.getIdx());
best.setPlayer(mv.getPlayer());
bestResult = alpha;
}
}
return bestResult;
}
Inside makeMove and undoMove i update the game state(black wins, white wins, draw).
I also toggle the players inside these methods. When a player has no moves i make a dummy
move without changing the board, and toggle the players.
There's a lot more of code, but i think the problem happens when the algorithm hits the
game over position. This problem doesn't happen when i set the engine to play random moves, so the problem should be the alpha beta algorithm.
Here is getAllMoves, this call getFlips:
public ArrayList<OthelloMove> getAllMoves(int player)
{
ArrayList<OthelloMove> moves = new ArrayList<OthelloMove>();
for (int i = 10; i < 90; i++)
{
int col = i % 10;
if (col != 0 && col != 9)
{
if (cells[i] == EMPTY)
{
ArrayList<Integer> flips = getFlips(i, player);
if (flips.size() > 0)
{
OthelloMove mv = new OthelloMove();
mv.setFlipSquares(flips);
mv.setIdx(i);
mv.setPlayer(player);
moves.add(mv);
}
}
}
}
return moves;
}
Here is getFlips.
public ArrayList<Integer> getFlips(int idx, int player)
{
int opponent = getOpponent(player);
ArrayList<Integer> flips = new ArrayList<Integer>();
if (cells[idx] != EMPTY)
return flips;
for (Integer dir : DIRECTIONS)
{
int distance = 1;
int tempIdx = idx;
while (cells[tempIdx += dir] == opponent)
distance++;
if ((cells[tempIdx] == player) && (distance > 1))
{
while (distance-- > 1)
{
tempIdx -= dir;
flips.add(tempIdx);
}
}
}
return flips;
}
Here is updateState:
public void updateState()
{
int opponent = getOpponent(currentPlayer);
int playerMoves = getAllMoves(currentPlayer).size();
int opponentMoves = getAllMoves(opponent).size();
if ( ((playerMoves == 0) && (opponentMoves == 0)) || (emptyCells == 0))
{
int blackDiscs = countDiscs(BLACK);
int whiteDiscs = countDiscs(WHITE);
if (blackDiscs > whiteDiscs)
state = STATE_BLACK_WINS;
else if (blackDiscs < whiteDiscs)
state = STATE_WHITE_WINS;
else
state = STATE_DRAW;
}
}
Thanks!
I am not familiar with the game specifically, but I believe it has something to do with the fact hat in the line:
while (cells[tempIdx += dir] == opponent)
You should also check you are not out of bound, otherwise - if there is still an opponent on the end of the board, you will keep increasing dir
Try changing this line to:
while (tempIdx + dir >= 0 && tempIdx + dir < cells.length && cells[tempIdx += dir] == opponent)
As a rule of thumb, usually it is a good practice in array accesses, especially in loops, to guard against going out of bound by checking the length explicitly.
Found the problem, thanks anyway.
The bug was a situation where a player can't move, and must pass the turn.
The tricky is to play a 'ghost move' (i.e. a move that doesn't change the board), and
toggle the players turn, so that the Minimax doesn't even notice this situation.
I was doing this, but in the wrong place! The code is like:
public void makeMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = player;
for (Integer flip : flips)
cells[flip] = player;
emptyCells--;
this.updatePhase();
}
this.toogleCurrentPlayer();
}
public void undoMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
int opponent = getOpponent(player);
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = EMPTY;
for (Integer flip : flips)
cells[flip] = opponent;
emptyCells++;
this.updatePhase();
}
this.toogleCurrentPlayer();
}