move up method in checkers game in java - java

I was reading this program that I saw in class relating to a checkers game this is the method that is called getup.
int rowUp = row-1;
if(column == 0 && row != 0)
{
for(int i = column; i < column+2; i++)
{
if(gameData[column][row] != 0 && gameData[i][rowUp] != 0)
{
if(canJump(column, row, i, rowUp) == true) {
int jumpCol = jumpPos(column, row, i, rowUp)[0];
int jumpRow = jumpPos(column, row, i, rowUp)[1];
availablePlays[jumpCol][jumpRow] = 1;//makes it available
}
}
else if(baseGameData[i][rowUp] == 1 && gameData[i][rowUp] == 0)
availablePlays[i][rowUp] = 1;
}
}
the Part that I dont understand is:
int jumpCol = jumpPos(column, row, i, rowUp)[0];
int jumpRow = jumpPos(column, row, i, rowUp)[1];
I really dont get what the col row i and row up parameters are and what the [0] or [1] mean. and how can this even be assigned to an int

The int jumpCol can be assigned because its the position of the spot in the checkers game. For example, if you had a row of names (Alex, Allen, George, Greg). REMEMBER: THE FIRST ONE OF AN ARRAY IS ALWAYS 0. In programming, we could assume that Alex would be at position 0 (or [0]), Allen would at [1], George would be position [2], and Greg would be position [3]. Technically there would still be 4 (0,1,2,3) but we start off with 0 instead of 1. So, you are technically assigning the position of the item. In our small example, you would be assigning the location of the item instead of the actually item. Sorry, that might of sounded a bit confusing. To shorten it, you are assigning the integer to Allen. But in reality your actually assigning the integer to the location. So because Allen is in [1] (position 1) your integer would have the value of 1, instead of "Allen.
Hope this helped.

Related

Min Path Through Matrix using Dynamic Programming

I'm working on assignment now for my comp class and am currently getting the hang of dynamic programming. Our current task is that we are given a matrix of size m x n, where either m or n is guaranteed to at least be 2. This matrix is filled with various values that dictate how many "steps" we may move from said position in any cardinal direction, with the goal being that we start at index (0,0) and have to make our way to index (n-1,m-1), or, better put, the bottom right corner of the matrix, in the smallest amount of trips as possible. If such a path is not possible, we must return -1. Here's an example:
1
2
7
5
6
3
The smallest amount of jumps required to get from the top left to the bottom right is 2, since we start at (0,0), which, with value of 1, means we can move 1 step in any direction. We then go to (0,1), which has a value of 2, which we'll use to "step" two positions down into (2,1), which is the bottom right corner. Overall, this journey took two trips: the one from (0,0) to (0,1), and then another from (0,1) to (2,1). It is worth nothing that at position (0,0), I could have also gone down to (0,1), but that wouldn't have resulted in anything useful.
I'm taking a dynamic programming approach, with a table to store previously calculated values and cut down runtime, with a separate table to let me know if I've calculated that position or not. My code is as follows:
private static int [][] dpArray;
private static int [][] solveState;
private static int rowDest;
private static int colDest;
public static int min_moves(int[][] board) {
rowDest = board.length - 1;
colDest = board[0].length - 1;
solveState = new int[rowDest + 1][colDest + 1];
dpArray = new int[rowDest + 1][colDest + 1];
int ans = minMoveRecur(0,0, board);
if (ans == 100000000) {
return -1;
}
private static int minMoveRecur(int row, int col, int[][] board) {
if ((row == rowDest) && (col == colDest)) {
return 0;
}
if ((row < 0) || ( rowDest < row) || (col < 0) || (colDest < col)) {
return 100000000;
}
if (solveState[row][col] == 1) {
return dpArray[row][col];
}
solveState[row][col] = 1;
int up = row - board[row][col];
int down = row + board[row][col];
int right = col + board[row][col];
int left = col - board[row][col];
int vertBest = Math.min(minMoveRecur(up,col,board),minMoveRecur(down,col,board));
int horizBest = Math.min(minMoveRecur(row,right,board),minMoveRecur(row,left,board));
dpArray[row][col] = 1 + Math.min(vertBest,horizBest);
return dpArray[row][col];
}
I take have a recursive relation that finds which path resulted in the minimum number of jumps, but I have been getting the wrong answer for one of test boards I have, where
int [][]board = {{2},{4},{2},{0},{4},{4},{3},{5},{1},{3}};
is supposed to take 4 jumps { (0,0) -> (2,0) -> (4,0) -> (8,0) -> (9,0) }, but I keep getting 2 jumps. I've debugged a few times, and it appears that the issue has to do with the vertBest and horizBest not getting the correct values from the recursive calls. It seems that vertBest's value is always 0, up until the very end where it is 1, which then is added to the 1 from the initial recursive call's dpArray[row][col] = 1 + Math.min(vertBest,horizBest); to be 2. It doesn't seem to be adding on the +1 from its earlier calls.
Can anyone help shed some light on what's going wrong here?
When you try to go up, in the minMoveRecur(up,col,board) call, solveState[row][col] = 1 however you have not updated dpArray[row][col] yet.
As a result since solveState[row][col] = 1 and dpArray[row][col], 0 is returned (due to the 3rd if condition) and 1 is added to it.

Finding the optimal path in a grid

Background
I had an interview today and I was asked the following question.
You are given a grid.
int [][] grid =
{
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
You start from the bottom left off the grid. You can only go up and right. The idea is to get to the TOP right corner. You are to take the path that will get you the most Value.
The output for the above would be 16
My solution
public static int getPathMaxSum(int [][] grid){
int row = grid.length-1;
int col = 0;
int currentSum = grid[row][col];
return getMax(grid,currentSum,row,col);
}
public static int getMax(int [][] grid,int sum,int row,int col){
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))){
return sum;
}else{
sum = sum + grid[row][col];
return Math.max(getMax(grid,sum,row-1,col),getMax(grid,sum,row,col+1));
}
}
public static boolean isTopRight(int [][] grid, int row, int col){
return row == 0 && col == grid[row].length-1;
}
public static boolean isValid(int [][] grid, int row, int col){
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
I am trying to solve this recursively. I figure that i have 2 possible choices to make at every position and i want to get the max of the two choices but for some reason I am not able to get the correct output.
I have two helper functions that check if my position is valid meaning inside the grid and also if i have hit the top right and if i have then i hit my base case.
I would love inputs to see where i went wrong.
You don't need a sum parameter in your method.
I assume you already understand how recursion-topdown approach for this problem.
But again just for the sake of completeness, the basic formula is:
You start with a cell at row, col get its value and then either you look to UP (row-1, col) or RIGHT (row, col+1).
So the result is going to be:
grid[row][col] + Math.max( getMax( row-1, col, grid ), getMax( row, col+1, grid ) )
Base conditions:
a) If it is top right i.e. the destination, you don't need to recurse you just return the value at that cell.
b) If it is an invalid cell like you have written in your isValid method, you need to return Integer.MIN_VALUE because you could have a negative value in your other cells and you want them to be maximum.
So your getMax function needs to be:
public static int getMax(int [][] grid,int row,int col){
if (isTopRight(grid,row,col)) {
return grid[row][col];
} else if (!isValid(grid,row,col)){
return Integer.MIN_VALUE;
} else {
return grid[row][col] + Math.max(getMax(grid,row-1,col),getMax(grid,row,col+1));
}
}
You can see working example here
EDIT: Answer to the edited version of your code
Issues with your new solution:
int currentSum = grid[row][col]; and sum = sum + grid[row][col];
The sum is initialized with the value in the bottom left corner and in the initial call of getMax() the same value is added again. This is not what it should be like. Just start the sum with 0, adding will be done by getMax().
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))) then return sum;
For invalid positions this will work (see restrictions below my code), but not for the top right corner (since we haven't added the value of the corner yet). Thus pull the two conditions apart and only return directly on invalid positions. On any other position first add the value and then, if you reached the "goal", return the sum. Otherwise return the maximum of "going right" and "going up" (the recursive call is correct now).
Fixing these issues and implementing your example, I derived the following code:
public class Pathfinder {
public static void main(String... args) {
int [][] grid = {
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
System.out.println(getPathMaxSum(grid));
}
public static int getPathMaxSum(int[][] grid) {
int row = grid.length - 1;
int col = 0;
return getMax(grid, 0, row, col);
}
public static int getMax(int[][] grid, int sum, int row, int col) {
if(!isValid(grid, row, col))
return sum;
sum = sum + grid[row][col];
if(isTopRight(grid, row, col))
return sum;
return Math.max(getMax(grid, sum, row - 1, col), getMax(grid, sum, row, col + 1));
}
public static boolean isTopRight(int[][] grid, int row, int col) {
return row == 0 && col == grid[row].length - 1;
}
public static boolean isValid(int[][] grid, int row, int col) {
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
}
Note, that this version will work for any grid (as long as the stack is big enough and we are not dealing with too large numbers, e.g. we won't get any integer overflow) if all entries are non-negative. Anyways, a grid with negative entries can be manipulated in such a way, that the best path will be found by this algorithm and the solution can be easily "translated" back to the original grid (just subtract the smallest value from every entry).
ANSWER TO THE ORIGINAL CODE
I see multiple issues with your code:
isValid(grid,row+1,col) and sum1 = grid[row+1][col];
You are trying to add 1 to the row, but you started (correctly) with int row = grid.length-1;. Adding 1 will give you an invalid position, thus the first branch will never be executed. Instead, you will need to subtract 1 from the row to "go up".
sum = sum + Math.max(sum1,sum2);
This changes sum, but you cannot see, in which direction you moved. And directly afterwards ...
getMax(grid,sum,row+1,col); and getMax(grid,sum,row,col+1);
... you do the recursive calls with the new, maximum sum, but from both spots. To get a correct solution, you should call them with the value, their direction represents. Note also, that row+1 needs to be replaced by row-1 here as well.
return sum;
You now return this maximum sum, but completely ignoring the returns of your recursive calls. You should instead compare their returns and return yourself the higher value of both.
Bactracking vs. Dynamic Programming
Your algorithm should work in general and is sufficient for small instances of the problem, but not for bigger ones (since it will make 2 recursive calls for every step and you have 2*(n-1) steps.. resulting in exponential runtime). An alternative approach with quadratic runtime would be to go backwards through the field and choose the best way by looking just one field right or up and adding the value of the current field to the maximum of this. Just start in the top right corner and go left, row by row from right to left.

Determining length of gap of binary number

I am trying to do the following exercise (found on Codility):
The way I have approached it is by using pointers. E.g. the binary representation of 25 is 11001. We start off with i = 0, j = 1, and a variable gLength = 0 that keeps track of the length of the gap.
If the i'th index is 1, check for the j'th index. If the j'th index is 0, increment gLength. If the j'th index is 1, check if gLength is greater than 0. If it is, then we need to store this length in an ArrayList as we have reached the end of the gap. Increment i and j, and repeat.
Here's the method in code:
public static int solution(int N) {
String binaryStr = Integer.toBinaryString(N);
// pointers
int i = 0;
int j = 1;
// length of gap
int gLength = 0;
while (j < binaryStr.length() && i < j) {
if (binaryStr.charAt(i) == 1) {
if (binaryStr.charAt(j) == 0) {
gLength++; // increment length of gap
} else if (binaryStr.charAt(j) == 1) {
// if the digit at the j'th position is the end of a gap, add the gap size to list.
if (gLength > 0)
gapLengths.add(gLength);
i++; // increment i pointer
}
} else {
i++; // increment i pointer
}
j++; // increment j pointer
}
Collections.sort(gapLengths);
// Line 45 (ERROR)
int maxGap = gapLengths.get(gapLengths.size() - 1);
return maxGap;
}
I get the following error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(ArrayList.java:400)
at java.util.ArrayList.get(ArrayList.java:413)
at Codility.solution(Codility.java:45)
at Codility.main(Codility.java:15)
I've marked down where line 45 is in the comments. After further investigating (with the debugger), I found out that I get the error because no length(s) seems to be getting added to the ArrayList. Does anybody know why?
I hope this was clear, if not please let me know. I'm not sure if this method would execute in O(log n) time like required, but for now I just want to have something working - then I will think about the time complexity aspect of it.
Big thanks for any help.
The problem is if (binaryStr.charAt(i) == 1). You are comparing char with int.
Replace:
if (binaryStr.charAt(i) == 1)
and
if (binaryStr.charAt(j) == 0)
With:
if (binaryStr.charAt(i) == '1')
and
if (binaryStr.charAt(j) == '0')
Edit: (As pointed out by Andy)
Before doing int maxGap = gapLengths.get(gapLengths.size() - 1);, you need to check if gapLengths.size() > 0 to make sure you have atleast 1 element in the the ArrayList.
I don't want to be annoying, I think the guys have offered great help for your algorithm. I propose, well, I believe it is an easier approach to use
String[] result = binaryStr.split("1");
And then go about just checking the biggest element of the array.
Edit: apparently I missed the part regarding the big O restriction, so I worked a different algorithm:
If you take a look at this page http://www.convertbinary.com/numbers.php
you'll notice that the gap starts at 5 (0 gap) then 9 (00 gap) then 17 (000 gap) etc..(in increasing order), the quick relation I noticed is if you start at 5 then add (5-1=4 to it) you'll get the 00 gap at 9, then 9+8 = 17 (000 gap) etc..
I believe you might be able to come up with a certain fixed calculation to get the best performance out of this without having to do String or Char work.
A simple solution in Swift:
let number = 5101
let binrygap = String(num, radix:2).componentsSeparatedByString("1").map { (a) -> Int in
a.characters.count}.maxElement()
Simple solution 100%
public int solution(final int N) {
//Convert number to Binary string
String bin = Integer.toString(N, 2);
System.out.println("binary equivalent = " + bin);
int gap = 0;
int maxGap = 0;
for (int i = 1; i < bin.length(); i++) {
if (bin.charAt(i) == '0') {
gap++;
}
else if (bin.charAt(i) == '1') {
if (gap > maxGap) {
maxGap = gap;
}
gap = 0;
}
}
return maxGap;
}
Java 8 implementation.
`
class Solution {
public int solution(int N) {
while(N%2 == 0){
N /= 2;
}
String binaryString = Integer.toBinaryString(N);
String[] matches = binaryString.split("1");
Optional maxValueOptional = Arrays.stream(matches).max(String::compareTo);
return maxValueOptional.isPresent()? ((String) maxValueOptional.get()).length():0;
}
}
`

Minimax Connect 4 AI trouble

I am making connect 4 AI, except the game continues until all 42 spaces are filled.
Score is kept by every 4 in a row gets 1 point.
public int[] Max_Value(GameBoard playBoard, int depth){
GameBoard temp = new GameBoard(playBoard.playBoard);
int h = 0, tempH = 999, tempCol=0;
int myDepth = depth - 1;
int[] tempH2 = new int[2];
boolean noChildren = true;
if(myDepth != -1){
for(int i = 0; i < 7; i++){
if(temp.isValidPlay(i)){
count++;
temp.playPiece(i);
noChildren = false;
tempH2 = Min_Value(temp, myDepth);
if(tempH2[1] < tempH){
tempH=tempH2[1];
tempCol = i;
}
temp.removePiece(i);
}
}
}
int[] x = new int[2];
if(noChildren){
h = temp.getHeuristic();
}
else{
h = tempH;
x[0]=tempCol;
}
x[1]=h;
return x;
}
public int[] Min_Value(GameBoard playBoard, int depth){
GameBoard temp = new GameBoard(playBoard.playBoard);
int h = 0, tempH = -999, tempCol=0;
int myDepth = depth - 1;
int[] tempH2 = new int[2];
boolean noChildren = true;
if(myDepth != -1){
for(int i = 0; i < 7; i++){
if(temp.isValidPlay(i)){
count++;
temp.playPiece(i);
noChildren = false;
tempH2 = Max_Value(temp, myDepth);
if(tempH2[1] > tempH){
tempH=tempH2[1];
tempCol = i;
}
temp.removePiece(i);
}
}
}
int[] x = new int[2];
if(noChildren){
h = temp.getHeuristic();
}
else{
h = tempH;
x[0]=tempCol;
}
x[1]=h;
return x;
}
I feel like I just stumbled through everything, and it feels like terrible code. However, I have never attempted anything like this before, and would appreciate any input. I can't tell where I am going wrong. My evaluation function just gives 1 point for each 4 in a row it can find for any given state. The main function calls the Min_Value function to start things off with a depth of 10.
I am attempting to return the column as well as the value of the heuristic. I hope I have provided enough information. Thanks for any insight.
Allright, after implementing the methods not shown (like evaluation, playmove, remove etc.) I was able to debug this. Assuming that these functions are implemented in some correct way in your version, the mistake is that you never actually call the evaluation function if depth is -1:
You have this:
[...]if(myDepth != -1)
{/*restofthecode*/}[...]
But what you need is something like this:
[...]if(myDepth == -1)
{
return temp.getHeuristic();
}
/*restofthecode*/
[...]
That way, whenever you reach depth -1 (a leaf in your minimax tree), the board will be evaluated and the value returned (which is excactly what you need in minimax).
Do this modification in both parts (min and max) and everything shuold be allright. If there are other problems, feel free to ask.
Even though it isn't stated in the question, I think you are not getting good moves from your search, right?
Without looking through your while code, I can already say that your program will only work during the last 10 moves of the game (last 10 empty fields or forced win in 10). Otherwise, your program will return either the last or the first move it evaluated. That is because of your evaluation function, where you only handle a win (respectively 4 in a row), but not 2 in a row, traps, 3 in a row, etc.). It will think of all moves as equal if it can't force a win.
This is a problem, because starting with an empty field, a win can only be forced by the starting player, and just with the 2nd last piece to be placed on the board. (In your version 4 in a row forced).
And since your searchdepth (10) is smaller than the maximum game moves (42), your program will always play its first move.
If the rest of your algorithm is correctly implemented, you can fix this by simply improve your evaluation function, so that it can differ between "good" and "bad" game positions.

MinMax AI for Checkers

I have created simple Checkers game in Java with minmax AI, but I doesn't work. I use recursive version of minmax, but there must be something wrong with it, because it returns moves, that aren't best, but maybe first generated.
public int minmax(int [][] board, int depth, int curPlayer){
ArrayList<Move> moves = findMoves(curPlayer, board);
if (depth == 0 || moves.size() == 0){
return heurValue(curPlayer, board);
}
int bestVal = 0;
if (curPlayer == GameCore.BLACK){
bestVal = Integer.MIN_VALUE;
curPlayer = GameCore.RED;
}else{
bestVal = Integer.MAX_VALUE;
curPlayer = GameCore.BLACK;
}
for(int i = 0; i<moves.size(); i++){
Move m = moves.get(i);
int [][] boardNew = makeMove(m, board);
int value = minmax(boardNew, depth-1, curPlayer);
board = undoMove(m, boardNew);
// computer plays as black
if (curPlayer == GameCore.BLACK){
if (value < bestVal){
bestMove = m;
bestVal = value;
}
}else{
if (value >= bestVal){
bestMove = m;
bestVal = value;
}
}
}
return bestVal;
}
If I call minmax with depth = 1 it should "return 7 values (there are 7 possible moves), but it returns only 1 if I move from 2,4 to 3,3... but when I tried to debug it, ArrayList moves has correct size. So I don't know what is wrong with it. :(
EDIT:
By "return" I mistakenly mean that first condition (when depth is 0 or moves are empty) happens only once, but if it was correct it should happen 7 times. Sorry for my bad english.
Do you know some site, where is correct recursive pseudocode for minmax (better with alpha/beta, because I will need to expand it) or could you help me to fix this? It must be only trifle.
Thank you!
You've written this to only return the best value, i.e. return bestVal; If you want it to return them all, store them in a List of some sort and change the method signature accordingly.
EDIT: So it is not a signature problem as I first thought.
I've done a quick search about the MinMax algorythm and this is what i found an article
Quickly what I think is important from this page is :
The values here represents how good a move is. So the MAX player will try to select the move with highest value in the end.
So if I'm rigth, MinMax will only return one move, the one with the bestValue.

Categories