I understand what tail recursion is but I am having trouble converting this method to tail recursion. I am trying to count how many adjacent elements there are in a matrix. Therefore, my question is how would you turn this into a tail recursion?
public static int ExploreAndLabelColony(char[][] grid, int i, int j, char c,int count) {
grid[i][j] = c;
count = 1;
if ((i>0 && i<grid.length && j<grid[0].length) && (grid[i-1][j] == '1')) { //vertical bottom
count +=ExploreAndLabelColony(grid, i-1,j,c,count);
}
if (i+1<grid.length && j<grid[0].length && grid[i+1][j] == '1') { //vertical top
count+=ExploreAndLabelColony(grid, i+1,j ,c,count);
}
if (j>0 && i<grid.length && j<grid[0].length && grid[i][j-1] == '1') { //horizontal left
count +=ExploreAndLabelColony(grid, i,j-1 ,c,count);
}
if (i<grid.length && j+1<grid[0].length && grid[i][j+1] == '1') { //horizontal right
count+=ExploreAndLabelColony(grid, i,j+1 ,c,count);
}
if (i+1<grid.length && j+1<grid[0].length && grid[i+1][j+1] == '1') { //diagonal bottom right
count+=ExploreAndLabelColony(grid, i+1,j+1 ,c,count);
}
if (j>0 && i+1<grid.length && j<grid[0].length && grid[i+1][j-1] == '1') { //diagonal bottom right
count+=ExploreAndLabelColony(grid, i+1,j-1 ,c,count);
}
if (i>0 && i<grid.length && j+1<grid[0].length && grid[i-1][j+1] == '1') { //diagonal top right
count+=ExploreAndLabelColony(grid, i-1,j+1 ,c,count);
}
if (i>0 && j>0 && i<grid.length && j<grid[0].length && grid[i-1][j-1] == '1') { //diagonal top left
count+=ExploreAndLabelColony(grid, i-1,j-1 ,c,count);
}
return count;
}
This might qualify as tail recursion:
static class Queue {
int i;
int j;
Queue next;
Queue(int i, int j, Queue next) {
this.i = i;
this.j = j;
this.next = next;
}
}
int exploreAndLabel(char[][] grid, int i, int j, char c) {
return exploreAndLabel(new Queue(i, j, null), grid, c, 0);
}
int exploreAndLabel(Queue queue, char[][] grid, char c, int count) {
if (queue == null) {
return count; // no more work
}
int i = queue.i;
int j = queue.j;
queue = queue.next;
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length) {
// outside grid
} else if (grid[i][j] != '1') {
// outside colony
} else {
grid[i][j] = c; // label
count++;
queue = new Queue(i - 1, j - 1, queue);
queue = new Queue(i - 1, j, queue);
queue = new Queue(i - 1, j + 1, queue);
queue = new Queue(i, j - 1, queue);
queue = new Queue(i, j + 1, queue);
queue = new Queue(i + 1, j - 1, queue);
queue = new Queue(i + 1, j, queue);
queue = new Queue(i + 1, j + 1, queue);
}
return exploreAndLabel(queue, grid, c, count);
}
and here is a test:
#Test
public void testTailRecursion() {
char[][] grid = Stream.of(
"00000101",
"00001101",
"00111010")
.map(String::toCharArray)
.toArray(char[][]::new);
int count = exploreAndLabel(grid, 2, 3, '2');
Assert.assertEquals(9, count);
}
Related
Can someone explain to me the time complexity for these three methods and help me understand why it is that time complexity. Both methods takes in a matrix and finds all its surrounding neighbours. The first method is done through recursion and the second one is done through iterations and stacks and third method is done through tail recursion.
Here is the first method:
public static int ExploreAndLabelColony(char[][] grid, int i, int j, char c) {
grid[i][j] = c;
int count = 1;
if ((i>0 && i<grid.length && j<grid[0].length) && (grid[i-1][j] == '1')) { //vertical top
count += ExploreAndLabelColony(grid, i-1,j,c);
}
if (i+1<grid.length && j<grid[0].length && grid[i+1][j] == '1') { //vertical bottom
count +=ExploreAndLabelColony(grid, i+1,j,c);
}
if (j>0 && i<grid.length && j<grid[0].length && grid[i][j-1] == '1') { //horizontal left
count +=ExploreAndLabelColony(grid, i,j-1,c);
}
if (i<grid.length && j+1<grid[0].length && grid[i][j+1] == '1') { //horizontal right
count +=ExploreAndLabelColony(grid, i,j+1,c);
}
if (i+1<grid.length && j+1<grid[0].length && grid[i+1][j+1] == '1') { //diagonal bottom right
count +=ExploreAndLabelColony(grid, i+1,j+1,c);
}
if (j>0 && i+1<grid.length && j<grid[0].length && grid[i+1][j-1] == '1') { //diagonal bottom left
count +=ExploreAndLabelColony(grid, i+1,j-1,c);
}
if (i>0 && i<grid.length && j+1<grid[0].length && grid[i-1][j+1] == '1') { //diagonal top right
count += ExploreAndLabelColony(grid, i-1,j+1,c);
}
if (i>0 && j>0 && i<grid.length && j<grid[0].length && grid[i-1][j-1] == '1') { //diagonal top left
count +=ExploreAndLabelColony(grid, i-1,j-1,c);
}
return count;
}
}
This is the second method:
public static int ExploreAndLabelColony(char[][] grid, int i, int j, char c) {
Stack<String> strStack = new Stack<>();
strStack.push(i + "," + j);
while (!strStack.empty()) {
String x = strStack.pop();
int row = Integer.parseInt(x.split(",")[0]);
int col = Integer.parseInt(x.split(",")[1]);
if(row<0 || col<0 || row>=grid.length || col>=grid[0].length || visited[row][col] || grid[row][col]!='1')
continue;
visited[row][col]=true;
grid[row][col]=c;
count++;
strStack.push(row + "," + (col-1)); //left
strStack.push(row + "," + (col+1)); //right
strStack.push((row-1) + "," + col); //up
strStack.push((row+1) + "," + col); //down
strStack.push((row-1) + "," + (col-1)); //left & up
strStack.push((row-1) + "," + (col+1)); //right & up
strStack.push((row+1) + "," + (col-1)); //left & down
strStack.push((row+1) + "," + (col+1)); //right & down
}
return count;
}
This is the third method:
static int ExploreAndLabelColony(Point point, char[][] grid, char c, int count) {
if (point == null) {
return count; // no more work
}
int i = point.i;
int j = point.j;
point = point.next;
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length) {
} else if (grid[i][j] != '1') {
} else {
grid[i][j] = c; // label
count++;
point = new Point(i - 1, j - 1, point);
point = new Point(i - 1, j, point);
point = new Point(i - 1, j + 1, point);
point = new Point(i, j - 1, point);
point = new Point(i, j + 1, point);
point = new Point(i + 1, j - 1, point);
point = new Point(i + 1, j, point);
point = new Point(i + 1, j + 1, point);
}
return ExploreAndLabelColony(point, grid, c, count);
}
Given n rows and m cols the time complexity should be O(mn) for both. Since you are passing grid by reference and not revisiting visited cells this is a depth first search. The second method is the same as the first method but replaces the call stack in the first method with your own stack.
so I got a little stuck in the code and if possible help
so the matrix is
{1,1,0,0,0},
{0,1,0,0,1},
{1,0,0,1,1},
{0,0,0,0,0},
{1,0,1,1,0}};
so i need to to return how much islands of 1 i have .Here in the example there are 4
I tried to do a double loop and any repetition of it would be added to my count 1 but I got into trouble with an edge case etc. this is some of my code.
There are probably duplicates because I tried something different every time
help
if(A[i][j]==1&& A[i-1][j]==1&&i>=1 && j>=0 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i - 1, j); } // up
if(A[i][j]==1&&A[i][j+1]==1&& i>=0 && j>=0 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i, j + 1); } // right
if(A[i][j]==1&& A[i][j-1]==1 && i>=0 && j>=1 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i, j - 1);} // left
if(A[i][j]==1&&A[i-1][j-1]==1&& i>=1 && j>=1 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i-1, j - 1); }
if(A[i][j]==1&&A[i-1][j+1]==1&& i>=1 && j>=0 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i-1, j + 1); }
if(A[i][j]==1&&A[i+1][j+1]==1&& i>=0 && j>=0 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i+1, j + 1); }
if(A[i][j]==1&& A[i+1][j+1]==1&&i>=0 && j>=1 && j<=A[i].length-1 &&i<=A.length-1 ) {
A[i][j]=0;
count++;
printPath1(A, i, j - 1); }
if(A[i][j]==1&& A[i+1][j]==1&& i>=0 && j>=0 && j<=A[i].length-1 &&i<=A.length-1 ) {
count++;
A[i][j]=0;
printPath1(A, i + 1, j);} // down
Not only do you not need recursion but it is better not to use it.
Keep a list of unvisited cells. Scroll through the matrix looking for unvisited cells, when you find one, you have a new group. Mark all the cells in that group as already visited (so they don't count another group).
A simple way to do it is:
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
public class Program {
public static void main(String... args) {
int[][] mx = {
{1, 1, 0, 0, 0},
{0, 1, 0, 0, 1},
{1, 0, 0, 1, 1},
{0, 0, 0, 0, 0},
{1, 0, 1, 1, 0}};
N2[] cross = new N2[]{new N2(0, -1), new N2(0, 1), new N2(-1, 0), new N2(1, 0)};
N2[] diagonal = new N2[]{new N2(0, -1), new N2(0, 1), new N2(-1, 0), new N2(1, 0), new N2(-1,-1), new N2(1,-1), new N2(-1,1), new N2(1,1)};
System.out.printf("%d%n", countGroupsOf(1, mx, cross));
System.out.printf("%d%n", countGroupsOf(1, mx, diagonal));
System.out.printf("%d%n", countGroupsOf(0, mx, cross));
System.out.printf("%d%n", countGroupsOf(0, mx, diagonal));
}
private static int countGroupsOf(int value, int[][] mx, N2[] adjacentDeltas) {
int groups = 0;
// visited cells with `value` must not be revisited
Set<N2> visited = new HashSet<>();
// check all `value` cells
for (int i = 0; i < mx.length; i++)
for (int j = 0; j < mx[i].length; j++) {
N2 p = new N2(i, j);
if (mx[i][j] == value && !visited.contains(p)) {
// new group encountered
groups += 1;
// visit all `value` cells in this group
Queue<N2> expand = new LinkedList<>();
expand.add(p);
visited.add(p);
while (!expand.isEmpty()) {
N2 q = expand.poll();
// arbitrary adjacency
for (N2 d : adjacentDeltas) {
N2 r = q.add(d);
// adjacent, valid, not visited, `value` cells must be expanded
if (r.x >= 0 && r.y >= 0 && r.x < mx.length && r.y < mx[r.x].length && mx[r.x][r.y] == value && !visited.contains(r)) {
expand.add(r);
visited.add(r);
}
}
}
}
}
return groups;
}
static class N2 {
int x;
int y;
N2 add(N2 p) {
return new N2(x + p.x, y + p.y);
}
N2(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
return x + 7 * y;
}
#Override
public boolean equals(Object z) {
if(!(z instanceof N2))
return false;
N2 w = (N2) z;
return w.x == x && w.y == y;
}
}
}
With result:
5
4
2
1
This algorithm works for any adjacency relationship you want to define.
The time and memory complexity is O(n) (where n is the total cells number).
Here is a solution.
public class IslandsMentallurg {
private static class Cell {
int col;
int row;
Cell(int row, int col) {
this.row = row;
this.col = col;
}
#Override
public String toString() {
return "(" + row + "," + col + ")";
}
}
private static int[][] matrix = { //
{ 1, 1, 0, 0, 0 }, //
{ 0, 1, 0, 0, 1 }, //
{ 1, 0, 0, 1, 1 }, //
{ 0, 0, 0, 0, 0 }, //
{ 1, 0, 1, 1, 0 } //
};
private static boolean[][] visited;
private static Cell findNewCell(Cell currentCell) {
for (int i = -1; i <= 1; i++) {
int row = currentCell.row + i;
if (row < 0 || row >= matrix.length) {
continue;
}
for (int j = -1; j <= 1; j++) {
int col = currentCell.col + j;
if (col < 0 || col >= matrix[row].length) {
continue;
}
if (!visited[row][col] && matrix[row][col] == 1) {
return new Cell(row, col);
}
}
}
return null;
}
public static void main(String[] args) {
visited = new boolean[matrix.length][matrix.length];
for (int i = 0; i < visited.length; i++) {
for (int j = 0; j < visited[i].length; j++) {
visited[i][j] = false;
}
}
List<List<Cell>> islands = new ArrayList<>();
for (int i = 0; i < visited.length; i++) {
for (int j = 0; j < visited[i].length; j++) {
if (!visited[i][j] && matrix[i][j] == 1) {
List<Cell> island = new ArrayList<>();
islands.add(island);
island.add(new Cell(i, j));
visited[i][j] = true;
int currentCellIndex = 0;
do {
Cell currentCell = island.get(currentCellIndex);
Cell newCell = findNewCell(currentCell);
if (newCell != null) {
island.add(newCell);
visited[newCell.row][newCell.col] = true;
currentCellIndex = island.size() - 1;
continue;
}
currentCellIndex--;
} while (currentCellIndex >= 0);
}
}
}
System.out.printf("Number of 1-islands: %d\n", islands.size());
for (int i = 0; i < islands.size(); i++) {
System.out.printf("Island %d: ", i + 1);
List<Cell> island = islands.get(i);
for (Cell cell : island) {
System.out.printf("%s, ", cell);
}
System.out.printf("\n");
}
}
}
Find the length of the longest line with given square (MxM) matrix. (vertical, horizontal, or diagonal allowed) (Length of the longest line = number of consecutive 1's)
i.e.)
input:
{
{0,0,0,0,0,0,0,0},
{0,0,1,0,1,0,0,0},
{0,1,0,1,0,0,0,0},
{1,1,1,1,1,1,1,0},
{0,1,0,0,0,1,0,0},
{1,1,0,0,0,0,1,0},
{0,1,0,0,0,0,0,1},
{0,0,0,0,0,0,0,0}
}
output: 7 (The 4th horizontal row is the longest line in this case.)
My java code:
public class LongestLine {
private int hmax = 0;
private int vmax = 0;
private int rdmax = 0; // right down direction
private int ldmax = 0; // left down direction
public int longestLine(int[][] grid) {
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[i].length; j++) {
if(grid[i][j] == 1) update(grid, i, j);
}
}
return Math.max(Math.max(hmax, vmax), Math.max(rdmax, ldmax));
}
private void update(int[][] grid, int i, int j) {
int h = 1, v = 1, rd = 1, ld = 1;
if(j < grid[i].length - 1 && grid[i][j+1] == 1) h = updateH(grid, i, j+1, h);
if(i < grid.length - 1 && grid[i+1][j] == 1) v = updateV(grid, i+1, j, v);
if(j < grid[i].length - 1 && i < grid.length - 1 && grid[i+1][j+1] == 1)
rd = updateRD(grid, i+1, j+1, rd);
if(j > 0 && i < grid.length - 1 && grid[i+1][j-1] == 1)
ld = updateLD(grid, i+1, j-1, ld);
hmax = Math.max(h, hmax);
vmax = Math.max(v, vmax);
rdmax = Math.max(rd, rdmax);
ldmax = Math.max(ld, ldmax);
}
private int updateH(int[][] grid, int i, int j, int h) {
h++;
if(j < grid[i].length - 1 && grid[i][j+1] == 1) h = updateH(grid, i, j+1, h);
return h;
}
private int updateV(int[][] grid, int i, int j, int v) {
v++;
if(i < grid.length - 1 && grid[i+1][j] == 1) v = updateV(grid, i+1, j, v);
return v;
}
private int updateRD(int[][] grid, int i, int j, int rd) {
rd++;
if(j < grid[i].length - 1 && i < grid.length - 1 && grid[i+1][j+1] == 1)
rd = updateRD(grid, i+1, j+1, rd);
return rd;
}
private int updateLD(int[][] grid, int i, int j, int ld) {
ld++;
if(j > 0 && i < grid.length - 1 && grid[i+1][j-1] == 1)
ld = updateLD(grid, i+1, j-1, ld);
return ld;
}
}
My code seems to work, but I'm not sure if this is the most efficient code. Do you think this is OK? Or are there any faster/simpler implementation? (Answer in Java format preferred.)
i think you need to use backtracking technique because it try all possible way in the board
please read about backtracking
How do I prevent the same tic tac toe coordinate from being inputted by the user?
The user input is taken at the main method in the Game Class.
The tic tac toe cells with [x, y] coordinates ranging from (0-2) can be either:
0(_), 1 (X) or 2 (O)
Grid Class with alpha beta search tree pruning algorithm
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Grid {
List<Cell> availableCells;
int[][] board = new int[3][3];
Scanner scan = new Scanner(System.in);
// Set limit to search tree depth
int treeDepth = 9;
List<CellsAndScores> rootsChildrenScore = new ArrayList<>();
public int score() {
int score = 0;
// Check all columns
for (int j = 0; j < 3; ++j) {
int X = 0;
int O = 0;
for (int i = 0; i < 3; ++i) {
if (board[i][j] == 0) {
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score += changeInScore(X, O);
}
// Check all rows
for (int i = 0; i < 3; ++i) {
int X = 0;
int O = 0;
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score += changeInScore(X, O);
}
int X = 0;
int O = 0;
// Check diagonal (first)
for (int i = 0, j = 0; i < 3; ++i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
}
}
score += changeInScore(X, O);
X = 0;
O = 0;
// Check Diagonal (Second)
for (int i = 2, j = 0; i > -1; --i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
}
}
score += changeInScore(X, O);
return score;
}
private int changeInScore(int X, int O) {
int change;
if (X == 3) {
change = 100;
} else if (X == 2 && O == 0) {
change = 10;
} else if (X == 1 && O == 0) {
change = 1;
} else if (O == 3) {
change = -100;
} else if (O == 2 && X == 0) {
change = -10;
} else if (O == 1 && X == 0) {
change = -1;
} else {
change = 0;
}
return change;
}
public int alphaBetaMinimax(int alpha, int beta, int depth, int turn) {
if (beta <= alpha) {
System.out.println("Pruning at tree depth = " + depth + " alpha: " + alpha + " beta: " + beta);
if (turn == 1)
return Integer.MAX_VALUE;
else
return Integer.MIN_VALUE;
}
if (depth == treeDepth || gameOver()) {
return score();
}
List<Cell> cellsAvailable = getAvailableStates();
if (cellsAvailable.isEmpty()) {
return 0;
}
if (depth == 0) {
rootsChildrenScore.clear();
}
int maxValue = Integer.MIN_VALUE, minValue = Integer.MAX_VALUE;
for (int i = 0; i < cellsAvailable.size(); ++i) {
Cell cell = cellsAvailable.get(i);
int currentScore = 0;
if (turn == 1) {
placeAMove(cell, 1);
currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 2);
maxValue = Math.max(maxValue, currentScore);
// Set alpha
alpha = Math.max(currentScore, alpha);
if (depth == 0) {
rootsChildrenScore.add(new CellsAndScores(currentScore, cell));
}
} else if (turn == 2) {
placeAMove(cell, 2);
currentScore = alphaBetaMinimax(alpha, beta, depth + 1, 1);
minValue = Math.min(minValue, currentScore);
// Set beta
beta = Math.min(currentScore, beta);
}
// reset board
board[cell.x][cell.y] = 0;
// Do not evaluate the rest of the branches after search tree is pruned
if (currentScore == Integer.MAX_VALUE || currentScore == Integer.MIN_VALUE)
break;
}
return turn == 1 ? maxValue : minValue;
}
public boolean gameOver() {
// Game is over is someone has won, or board is full (draw)
return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
}
public boolean hasXWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1)
|| (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
// System.out.println("X Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("X Row or Column win");
return true;
}
}
return false;
}
public boolean hasOWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2)
|| (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
// System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
public List<Cell> getAvailableStates() {
availableCells = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
availableCells.add(new Cell(i, j));
}
}
}
return availableCells;
}
public void placeAMove(Cell Cell, int player) {
board[Cell.x][Cell.y] = player; // player = 1 for X, 2 for O
}
public Cell returnBestMove() {
int MAX = -100000;
int best = -1;
for (int i = 0; i < rootsChildrenScore.size(); ++i) {
if (MAX < rootsChildrenScore.get(i).score) {
MAX = rootsChildrenScore.get(i).score;
best = i;
}
}
return rootsChildrenScore.get(best).cell;
}
public void displayBoard() {
System.out.println();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0)
System.out.print("_" + " ");
if (board[i][j] == 1)
System.out.print("X" + " ");
if (board[i][j] == 2)
System.out.print("O" + " ");
}
System.out.println("");
}
System.out.println();
}
public void resetGrid() {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
board[i][j] = 0;
}
}
}
}
Cell class
class Cell {
int x, y;
public Cell(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "[" + x + ", " + y + "]";
}
}
class CellsAndScores {
int score;
Cell cell;
CellsAndScores(int score, Cell cell) {
this.score = score;
this.cell = cell;
}
}
Game Class with main method - takes user input
import java.util.Random;
public class Game {
public static void main(String[] args) {
Grid grid = new Grid();
Random random = new Random();
grid.displayBoard();
System.out.print("Who moves first? [1]Computer(X) [2]User(O): ");
int turn = grid.scan.nextInt();
if (turn == 1) {
Cell p = new Cell(random.nextInt(3), random.nextInt(3));
grid.placeAMove(p, 1);
grid.displayBoard();
}
while (!grid.gameOver()) {
int x = 0, y = 0;
System.out.print("Please enter an x coordinate [0-2]: ");
x = grid.scan.nextInt();
System.out.print("Please enter an y coordinate [0-2]: ");
y = grid.scan.nextInt();
Cell userMove = new Cell(y, x);
grid.placeAMove(userMove, 2); // 2 for O and O is the user
grid.displayBoard();
if (grid.gameOver())
break;
grid.alphaBetaMinimax(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 1);
for (CellsAndScores pas : grid.rootsChildrenScore)
System.out.println("Cell: " + pas.cell + " Score: " + pas.score);
grid.placeAMove(grid.returnBestMove(), 1);
grid.displayBoard();
}
if (grid.hasXWon()) {
System.out.println("Unfortunately, you lost!");
grid.resetGrid();
} else if (grid.hasOWon()) {
System.out.println("You win!");
grid.resetGrid();
} else {
System.out.println("It's a draw!");
grid.resetGrid();
}
}
}
My answer would be to add a boolean check method into your Grid.java class and then in your main method - call this boolean check method before the placeAMove() method.
For example, in your Grid.java class, adding the following method:
/*
* Return true if space is ok to use.
*/
public boolean isMoveOK(Cell cell) {
return board[cell.x][cell.y] == 0;
}
This way, using your pre-existing 0/1/2 values that keep track of empty/X/O space values, you may provide a check to see if the space value is zero or not.
This would be one way to use it in your main method, to answer your question of, 'How do I prevent the same tic tac toe coordinate from being inputted by the user?'
Cell userMove = new Cell(y, x);
if (grid.isMoveOK(userMove)) {
grid.placeAMove(userMove, 2); // 2 for O and O is the user
} else {
System.out.println("Please try a different space/cell");
continue;
}
grid.displayBoard();
if (grid.gameOver())
break;
In this way, I am skipping the remaining loop code in your main method loop, until there's a valid open space. (When there is, then the program should proceed to check for winning values or whether to proceed playing)
Hope this answers your question! :)
Cheers
I'm struggling with a minimax exercise, I'm just trying to make a connect four ai with it. Mine works when only exploring one node deep but I can't figure out why it messes up once it goes deeper.
private int minimax(Gameboard gameBoard, int alpha, int depth, char color) {
Gameboard gb = new Gameboard(gameBoard);
int value = 0;
int bestChoice = 0;
int bestValue = alpha;
// determine which use the value is for
if (gb.computerColor == color) {
value = 1;
} else {
value = -1;
}
if (gb.CheckForWinner(gb.lastSpacePlayed)) {
if(gb.winner == gb.computerColor){
bestValue = (1000000000 - depth);
}else{
bestValue = (-1000000000 - depth);
}
}
// get the bestValue at our maximum recrusion depth
else if (depth == maxDepth) {
int moveWeight = (threatLevel(gb, color));
if (moveWeight != 0) {
bestValue = value * (moveWeight - depth);
} else {
bestValue = moveWeight;
}
} else {
// Generate moves for each col and find the best score from each of
// the generated moves.
for (int c = 0; c < 7; c++) {
Gameboard game = new Gameboard(gb);
int selectedPlace = game.PlacePiece(c, color);
// Recursive call the generated game grid and compare the
// current value to move value
// If move is higher, make it the new current value.
if (selectedPlace != -1) {
char tempColor;
// change the user for the child node after a piece is played
if (color == 'Y') {
tempColor = 'R';
} else {
tempColor = 'Y';
}
// call the function so we can explore to maximum depth
if (depth < maxDepth) {
int v = minimax(new Gameboard(game), -1000000, depth + 1, tempColor);
if (v > bestValue) {
bestChoice = c;
bestValue = v;
}
System.out.println(v);
}
}
}
}
if (depth == 0) {
if (threatLevel(gb, gb.lastSpacePlayed.getColor()) == 0) {
return gb.spaces.get(0).get(3).getColor() == gb.playerColor ? 2
: 3;
} else {
return bestChoice;
}
} else {
return bestValue;
}
}
I'm starting it off as so return minimax(gameBoard, -1000000, 0, gameBoard.computerColor);
My understanding is just looping over all children and returning a value a maximum value if the nodes are the same as the parent and a minimum value if the nodes aren't. Any direction would be appreciated.
private int minimax(Gameboard gameBoard, int depth, char color) {
Gameboard gb = new Gameboard(gameBoard);
int bestChoice = 0;
int bestValue = 0;
//If we've won, return highest possible value. If we've lost, return lowest.
if (gb.CheckForWinner(gb.lastSpacePlayed)) {
if(gb.winner == color){
return Integer.MAX_VALUE
}else{
return Integer.MIN_VALUE
}
}
//if we hit maximum depth, resort to our heuristic method.
else if (depth == maxDepth) {
return threatLevel(gb, color);
} else {
// Generate moves for each col and find the best score from each of
// the generated moves. Keep track of the worst one.
int worstBestResponse = Integer.MAX_INT
boolean tie = true;
for (int c = 0; c < 7; c++) {
Gameboard game = new Gameboard(gb);
int selectedPlace = game.PlacePiece(c, color);
// Recursive call the generated game grid and compare the
// current value to move value
// If move is higher, make it the new current value.
if (selectedPlace != -1) {
tie = false;
char tempColor;
// change the user for the child node after a piece is played
if (color == 'Y') {
tempColor = 'R';
} else {
tempColor = 'Y';
}
// call the function so we can explore to maximum depth
if (depth < maxDepth) {
int v = minimax(new Gameboard(game), depth + 1, tempColor);
if (v < worstBestResponse) {
worstBestResponse = v;
}
}
}
}
if(tie) {
//if game is a tie, we return 0, to show no favour.
return 0;
} else {
//After determining the value of the opponents best response, we return the negative value of it. That is, what's bad for them is good for us and visa versa.
return -worstBestResponse;
}
}
}
I believe something like this is more what you're looking for. This is assuming that threatLevel is a heuristic method for determining approximately who is winning in a given game.
I've taken out any knowledge the method may have about who it's rooting for. It should only be rooting for whoever "color" is. I've also cleaned up your arbitrarily large integers to show winning and losing. MAX_VALUE and MIN_VALUE is much more elegant.
Anyway, try this out, and call it with depth of 0. Let me know if you have questions
If your approach isn't working you could try to mimic the basic structure of the pseudocode on wiki or here. In java I have:
/**
* initialize MiniMax(0, player1) where player1 is computer
* #param deg depth
* #param player either MAX or MIN
* #return int {score,column}
*/
int[] MiniMax(int deg, char player) {
// list of possible columns to place a checker
List<int[]> moves = nextMoves();
int v;
char prev;
if (player==player1) prev = player2;
else prev = player1;
// IF depth = 0 OR there is a connect 4 OR the board is full (draw) THEN
// return your static evaluation (heuristic)
// if the current position has a connect 4, return -inf or inf
// depending on if player is MIN or MAX respectively
if (checkPos(prev) || (deg == this.deg) || moves.isEmpty())
return new int[] {eval(),bestMove};
//MAX
else if (player==player1) {
v = -inf;
for (int[] move : moves) { // move = {row,column}
board[move[0]][move[1]] = player1; // make move
int score = MiniMax(deg+1,player2)[0];
board[move[0]][move[1]] = ' '; // undo move
if (score>v) {
v = score;
bestMove = move[1];
}
}
return new int[] {v,bestMove};
}
//MIN
else {
v = inf;
for (int[] move : moves) { // move = {row,column}
board[move[0]][move[1]] = player2; // make move
int score = MiniMax(deg+1,player1)[0];
board[move[0]][move[1]] = ' '; // undo move
if (v>score) {
v = score;
bestMove = move[1];
}
}
return new int[] {v,bestMove};
}
}
It could be useful to use a character array or something, rather than a class, to represent the board. The most efficient way though is to represent the board as a long as you can check if there is a connect four using 4 bit shifts. Here's some sloppy java that shows the above stuff working:
import java.util.ArrayList;
import java.util.List;
public class Connect4 {
static final int WIDTH = 7;
static final int HEIGHT = 6;
private static final int inf = 9999999;
private static final char player1 = 'X';
private static final char player2 = 'O';
char[][] board = new char[WIDTH][HEIGHT];
private int deg;
int bestMove;
static char[][] copy(char[][] aArray) {
char[][] copy = new char[aArray.length][aArray[0].length];
for (int idy = 0; idy < aArray.length; ++idy) {
for (int idx = 0; idx < aArray[0].length; ++idx) {
copy[idy][idx] = aArray[idy][idx];
}
}
return copy;
}
void prints() {
System.out.println();
for (int i = 0; i < 6; i++) {
System.out.println("=============================");
for (int j = 0; j < 7; j++) {
if (j == (WIDTH - 1)) {
if (board[i][j]==' ') {
System.out.println("| |");
}
else {
if(board[i][j]==player1 ) System.out.println("| " +"\u001B[32m"+board[i][j]+"\u001B[0m" + " |");
else System.out.println("| " +"\u001B[34m"+board[i][j]+"\u001B[0m" + " |");
}
}
else {
if (board[i][j]==' ') {
System.out.print("| ");
}
else {
if(board[i][j]==player1 ) System.out.print("| " +"\u001B[32m"+board[i][j]+"\u001B[0m" + " ");
else System.out.print("| " +"\u001B[34m"+board[i][j]+"\u001B[0m" + " ");
}
}
}
}
System.out.println("=============================");
}
//STATIC EVALUATION
int eval3(char player) {
if (checkPos(player)) {
return inf;
}
int count;
int open;
int evaluation = 0;
//evaluation = number of open 3 in rows
//go through all possible 4in rows and check
//horz
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < 4; j++) {
count = 0;
open = 0;
if (board[i][j]==player) count++;
else if(board[i][j]==' ') open++;
if (board[i][j + 1]==player) count++;
else if(board[i][j + 1]==' ') open++;
if (board[i][j + 2]==player) count++;
else if(board[i][j + 2]==' ') open++;
if (board[i][j + 3]==player) count++;
else if(board[i][j + 3]==' ') open++;
if ((count == 3) && (open == 1)) evaluation++;
}
}
//vert
for (int j = 0; j < WIDTH; j++) {
for (int i = 0; i < 3; i++) {
count = 0;
open = 0;
if (board[i][j]==player) count++;
else if (board[i][j]==' ') open++;
if (board[i + 1][j]==player) count++;
else if (board[i+1][j]==' ') open++;
if (board[i + 2][j]==player) count++;
else if (board[i + 2][j]==' ') open++;
if (board[i + 3][j]==player) count++;
else if (board[i + 3][j]==' ') open++;
if ((count == 3) && (open == 1)) evaluation++;
}
}
// pos slope diag
for (int j = 0; j < 4; j++) {
for (int i = 3; i < HEIGHT; i++) {
count = 0;
open = 0;
if (board[i][j]==player) count++;
else if (board[i][j]==' ') open++;
if (board[i - 1][j + 1]==player) count++;
else if (board[i - 1][j + 1]==' ') open++;
if (board[i - 2][j + 2]==player) count++;
else if (board[i - 2][j + 2]==' ') open++;
if (board[i - 3][j + 3]==player) count++;
else if (board[i - 3][j + 3]==' ') open++;
if ((count == 3) && (open == 1)) evaluation++;
}
}
// neg slope diag
for (int j = 0; j < 4; j++) {
for (int i = 0; i < (3); i++) {
count = 0;
open = 0;
if (board[i][j]==player) count++;
else if (board[i][j]==' ') open++;
if (board[i + 1][j + 1]==player) count++;
else if (board[i + 1][j + 1]==' ') open++;
if (board[i + 2][j + 2]==player) count++;
else if (board[i + 2][j + 2]==' ') open++;
if (board[i + 3][j + 3]==player) count++;
else if (board[i + 3][j + 3]==' ') open++;
if ((count == 3) && (open == 1)) evaluation++;
}
}
return evaluation;
}
int eval() {return eval3(player1) - eval3(player2);}
boolean checkPos(char cur) {
//horz
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < 4; j++) {
if ( board[i][j]==cur &&
board[i][j + 1]==cur &&
board[i][j + 2]==cur &&
board[i][j + 3]==cur) {
return true;
}
}
}
//vert
for (int j = 0; j < WIDTH; j++) {
for (int i = 0; i < 3; i++) {
if ( board[i][j]==cur &&
board[i + 1][j]==cur &&
board[i + 2][j]==cur &&
board[i + 3][j]==cur) {
return true;
}
}
}
// pos slope diag
for (int j = 0; j < 4; j++) {
for (int i = 3; i < HEIGHT; i++) {
if ( board[i][j]==cur &&
board[i - 1][j + 1]==cur &&
board[i - 2][j + 2]==cur &&
board[i - 3][j + 3]==cur) {
return true;
}
}
}
// neg slope diag
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 3; i++) {
if ( board[i][j]==cur &&
board[i + 1][j + 1]==cur &&
board[i + 2][j + 2]==cur &&
board[i + 3][j + 3]==cur) {
return true;
}
}
}
return false;
}
List<int[]> nextMoves() {
List<int[]> result = new ArrayList<>();
for (int j = 0; j < WIDTH; j++) {
//if column j isnt full then add
if (board[0][j]==' ') result.add(new int[] {findY(j),j});
}
return result;
}
int findY(int col) {
int i = board.length - 1;
while (i > -1) {
if (board[i][col]==' ') break;
i--;
}
return i;
}
/**
* #param deg depth
* #param player either MAX or MIN
* #return int {score,column}
*/
int[] MiniMax(int deg, char player) {
// list of possible columns to place a checker
List<int[]> moves = nextMoves();
int v;
char prev;
if (player==player1) prev = player2;
else prev = player1;
// IF depth = 0 OR there is a connect 4 OR the board is full (draw) THEN
// return your static evaluation (heuristic)
// if the current position has a connect 4, return -inf or inf
// depending on if player is MIN or MAX respectively
if (checkPos(prev) || (deg == this.deg) || moves.isEmpty())
return new int[] {eval(),bestMove};
//MAX
else if (player==player1) {
v = -inf;
for (int[] move : moves) { // move = {row,column}
board[move[0]][move[1]] = player1; // make move
int score = MiniMax(deg+1,player2)[0];
board[move[0]][move[1]] = ' '; // undo move
if (score>v) {
v = score;
bestMove = move[1];
}
}
return new int[] {v,bestMove};
}
//MIN
else {
v = inf;
for (int[] move : moves) { // move = {row,column}
board[move[0]][move[1]] = player2; // make move
int score = MiniMax(deg+1,player1)[0];
board[move[0]][move[1]] = ' '; // undo move
if (v>score) {
v = score;
bestMove = move[1];
}
}
return new int[] {v,bestMove};
}
}
public static void main(String[] args) {
Connect4 c = new Connect4();
c.deg = 5;
char[][] b = {
{' ',' ',' ',' ',' ',' ',' '},
{' ',' ',' ',' ',' ',' ',' '},
{' ',' ',' ',' ',' ',' ',' '},
{' ',' ',' ',' ',' ',' ',' '},
{' ','X','X','X',' ',' ',' '},
{' ','O','X','X','X',' ',' '}
};
c.board = copy(b);
c.prints();
System.out.println("best value = " + c.MiniMax(0, player1)[1]);
}
}