Finding a loop within a Grid (Java) - java

I am currently working on a problem in which I have a 2D List of Characters with n rows and m columns. Said Characters in the list are either 'N', 'S', 'E', or 'W' representing the 4 coordinates.
For example, a 2D list of 4 rows and 5 columns could be represented as List<List<Character>> grid = :
SESWE
EESNW
NWEEN
EWSEN
In this problem, I also have a starting position within the grid (always somewhere on the edge). For example, in this problem my starting position would be (0,0).
The problem that I am having to solve is that I must following the directions through the grid and identify where a loop occurs, how many instructions come before the loop, and how many instructions the loop has. To follow the directions through the grid, you simply follow whatever coordinate you are at in the grid. For example, (0,0) is S or South, so you go down one element to (1, 0) which is E or East. From there you go east one coordinate to (1,1) which is another E or East. So on and so forth.
For this particular grid, if you follow the coordinates from (0,0), the path through the grid should look like the following:
In this grid, the loop itself has 8 instructions, and there are 3 instructions before the loop.
I am having a hard time coming up with an algorithm that can accomplish this goal. I thought at first that I should first follow the path through the grid and leave a little breadcrumb behind at each element that I have visited so that if I visit it again I know that I am in the loop. So in this case it would look like this after I have visited all of the elements:
. E . . E
. . . . .
N W . . .
E W S E N
I accomplished this with the following:
while (gridCopy.get(curRow).get(curCol) != '.') {
if (gridCopy.get(curRow).get(curCol) != '.') {
if (gridCopy.get(curRow).get(curCol) == 'N') {
gridCopy.get(curRow).set(curCol, '.');
curRow--;
} else if (gridCopy.get(curRow).get(curCol) == 'S') {
gridCopy.get(curRow).set(curCol, '.');
curRow++;
} else if (gridCopy.get(curRow).get(curCol) == 'W') {
gridCopy.get(curRow).set(curCol, '.');
curCol--;
} else if (gridCopy.get(curRow).get(curCol) == 'E') {
gridCopy.get(curRow).set(curCol, '.');
curCol++;
}
}
}
All this does, though, is tell me that I do in fact have a loop in the grid and doesnt tell me how many instructions the loop is and how many instructions are before the loop.
Can anyone help point me in the right direction on this?

Here's one possible implementation. Basically you store the coordinates you've traversed in a list and if the coordinate is already in that list then you've found a loop. I didn't add code to check if the search goes out of bounds so I'm assuming that all the grids are well behaved and that they all have at least one loop. Anyhow below is the code with more explanations as comments:
// Class to store the coordinates
public static class Coordinate {
private int x;
private int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coordinate that = (Coordinate) o;
if (x != that.x) return false;
return y == that.y;
}
}
Loop finder:
public static void loopFinder(List<List<Character>> grid, int startingX, int startingY) {
// Not checking if one went outside of the grid
// Store the coordinates we've traversed, if we find a duplicate then there's a loop
List<Coordinate> traversedCoordinates = new ArrayList<>();
Coordinate currentCoordinate = new Coordinate(startingX, startingY);
traversedCoordinates.add(currentCoordinate);
while (true) {
char direction = grid.get(currentCoordinate.getY()).get(currentCoordinate.getX());
switch (direction){
case 'N':
currentCoordinate = new Coordinate(currentCoordinate.getX(), currentCoordinate.getY() - 1);
break;
case 'E':
currentCoordinate = new Coordinate(currentCoordinate.getX() + 1, currentCoordinate.getY());
break;
case 'S':
currentCoordinate = new Coordinate(currentCoordinate.getX(), currentCoordinate.getY() + 1);
break;
case 'W':
currentCoordinate = new Coordinate(currentCoordinate.getX() - 1, currentCoordinate.getY());
break;
}
if(traversedCoordinates.contains(currentCoordinate)) {
// found a loop
traversedCoordinates.add(currentCoordinate);
break;
}
traversedCoordinates.add(currentCoordinate);
}
// find index of current coordinate that will give us how many instructions before the loop started
int numOfInstructionsBefore = traversedCoordinates.indexOf(currentCoordinate);
int lengthOfLoop = traversedCoordinates.size() - numOfInstructionsBefore - 1;
// I don't know if you need to include the starting and ending point of the loop
// in that case you might have to offset the two values above.
System.out.println("Number of instruction before the loop: " + numOfInstructionsBefore);
System.out.println("Length of the loop: " + lengthOfLoop);
}
Usage:
public static void main(String[] args) {
List<List<Character>> grid = new ArrayList<>();
grid.add(List.of('S', 'E', 'S', 'W', 'E'));
grid.add(List.of('E', 'E', 'S', 'N', 'W'));
grid.add(List.of('N', 'W', 'E', 'E', 'N'));
grid.add(List.of('E', 'W', 'S', 'E', 'N'));
loopFinder(grid, 0, 0);
}
Output:
Number of instruction before the loop: 3
Length of the loop: 8

Related

JAVA 2D Map using String? i.e. "+" and "C"

so I've been looking but I simply just don't know how to state my problem.
So I'm just going to break an egg, and if you can link to the correct answer anyhow then please don't be afraid to, this is a long shot and I know this exists many places, I am just unable to find it.
I am looking at making a 2D map, based off on PLUS signs (+) and ONE (C), the C is the characters current location.
It would look like this
C+++++++++++++++++++
++++++++++++++++++++
++++++++++++++++++++
++++++++++++++++++++
++++++++++++++++++++
When printed.
Notice C is based off of integers, namely currentX and currentY (1 & 1).
This is my current code in bp_Map.class
public class bp_Map {
// Map
public static String mapP = "+";
public static String mapC = "C";
public static int sizeY = 19;
public static int sizeX = 19;
public static void drawMap(int currX, int currY) {
int currentY = 0;
while (currentY <= sizeY) {
drawX();
System.out.print("\n");
currentY ++;
}
}
public static void drawX() {
int currentX = 0;
while (currentX <= sizeX) {
System.out.print(mapP);
currentX++;
}
}
I could use an array, instead of mapP and mapC and just do
public static final String mapChar[] = {"+", "C"}
But I don't feel the need to do this atm.
My current problem is I don't want 20 if statements (or 1 if and 19 if else statements) to check the location of X, and then print correspondingly Y.
I am new to java and still learning, I have used while, but should I use for? I'm a bit lost, hope you guys can help me. This is for a text-based rpg, and I'm working on it alongside my studies.
You don't need if-else cases - this is a perfect usage example for loops.
First of all, define things which will never change as final fields in your class:
private static final String EMPTY = "+";
private static final String CHARACTER = "C";
private static final int SIZE_X = 20;
private static final int SIZE_Y = 5;
For this example, I'll be using fields for the current X and Y coordinates too, but you may want to change this since I assume they come from elsewhere in your program:
private static int currentX = 7;
private static int currentY = 3;
Now, think of how a TV draws pixels on a screen: from the top to the bottom and from left to right, pixel by pixel, at least 30 times a second. Let's try and do the same, and draw one row at a time:
public static void main(String[] args) {
if(currentX > SIZE_X - 1 || currentY > SIZE_Y - 1) {
throw new IllegalStateException("Out of bounds");
}
for (int y = 0; y < SIZE_Y; y++) {
drawRow(y);
}
}
What would the drawRow() function look like? One possible implementation is below:
private static void drawRow(int i) {
// Use a StringBuilder, ~30 times faster than String concatenation!
StringBuilder row = new StringBuilder();
if(i == currentY) {
// Create this row differently, as it contains the character.
for (int x = 0; x < SIZE_X; x++) {
if(x == currentX) {
row.append(CHARACTER);
} else {
row.append(EMPTY);
}
}
} else {
// Create an empty row.
for (int x = 0; x < SIZE_X; x++) {
row.append(EMPTY);
}
}
// "Draw" the row by printing it to the console.
System.out.println(row.toString());
}
This produces:
++++++++++++++++++++
++++++++++++++++++++
++++++++++++++++++++
+++++++C++++++++++++
++++++++++++++++++++
Try playing around with the coordinates and run main again. This is just one of many possible solutions - the neat thing about the above code is that no Map or even array is needed, but it may be that you do need them eventually and the code would have to change to accommodate this (e.g. keep a bit matrix, make a nested for loop over it and draw the set bit as the character). Let us know if you would like an example of this.
An approach I would use in this case is the following in pseudo-code:
Create a character matrix with the dimensions of sizeX by sizeY
Use the java.util.Arrays.fill builtin to fill the entire matrix with the character '+'
Replace the character at position {currX, currY} (1-indexed) with character 'C'
Pretty-print the matrix
Here a possible implementation of what I described above:
/*
* Prints a block of sizeX by sizeY of the filler character,
* where the character at position {posX, posY} (1-indexed) is replaced with the replacement character
*
* TODO: Validation checks. Currently assumes posX and posY are always within range of the matrix
*/
public void drawMap(int sizeX, int sizeY, char fillerChar, char replacementChar, int posX, int posY){
// Create a char-matrix of dimensions sizeX by sizeY
char[][] matrix = new char[sizeX][sizeY];
// Fill this matrix initially with the filler-character
for(char[] row : matrix)
java.util.Arrays.fill(row, fillerChar);
// Replace the character at position {currX, currY} (1-indexed) with the replacement-character
matrix[posX-1][posY-1] = replacementChar;
// Print the matrix
prettyPrintMatrix(matrix);
}
private void prettyPrintMatrix(char[][] matrix){
for(char[] row : matrix){
for(char ch : row)
System.out.print(ch);
System.out.println();
}
}
This could then be called with:
drawMap(10, 10, '+', 'C', 4, 2);
Which will output:
++++++++++
++++++++++
++++++++++
+C++++++++
++++++++++
++++++++++
++++++++++
++++++++++
++++++++++
++++++++++
Try it online.
Some things to note:
I've added the size and characters as parameter to the method. In my TIO-link above you can see a call with different sizes or characters also works (i.e. m.drawMap(5, 5, 'a', 'B', 5, 5);).
I've added a TODO for validation checks. If the given posX or poxY are larger than the sizeX or sizeY respectively, it will of course give an ArrayOutOfBoundsException. So perhaps a check at the top of the method to see if the given pos-parameters are valid is in order depending on how you want to use it.

Java- Iterating backwards through Array under a condition

So for one of my game models, there is an array of elements represented as a string "--X-X" so that this board has 5 boxes, and positions are 0-4. Each 'X' can only move left. There is an "X-Index" so that if I run getIXPosition(1) it will return the position of the first X which is 2. And getXPosition(2) will return second X's position which is 4. So the string board is "--X-X" but in my code it's represented as an array as "00102" so that I can keep track of xIndex.
Now my issue is that I need to make a move method that prevents the second x from skipping over the first X into position 1 or 0. That is not allowed in this game. I thought I wrote the method correctly but my tests aren't passing when I test to make sure second X can not hop over any X's before it.
public void move(int xIndex, int newPosition)
{
int oldPosition = getXPosition(xIndex);
for(int i= oldPosition - 1; i >= 0;i--)
{
while (board[i] == 0 )
{
board[oldPosition] = '0'; // so this spot is open '-'
board[newPosition] = xIndex;
}
throw new IllegalArgumentException("Error cannot move X to new position");
}
}
What am I doing wrong?
If you know the position you want to move to, you don't have to search for it, just move there.
if (board[newPosition] == '0') {
board[newPosition[ = xIndex;
board[oldPosition] = '0';
} else {
throw new IllegalArgumentException("Error cannot move X to new position");
}
Note: The character '0' is not the value 0 (Actually it is 48 in ASCII)
I think the code is a bit flawed. First, I don't think you need to iterate all the way to 0. You should only iterate until you hit newPosition.
Then the while loop doesn't make much sense. I think you were after an if.
Lastly, personally I wouldn't throw an IllegalArgumentException in this case (actually, you're throwing after the first iteration regardless, so that's another flaw). It's the state of the board that's problematic, not the arguments. I would maybe throw IllegalArgumentException if one of the arguments was negative etc.
public void move(int xIndex, int newPosition) {
int oldPosition = getXPosition(xIndex);
for(int i= oldPosition - 1; i >= newPosition; i--) {
if(board[i] == '0') {
board[oldPosition] = '0';
board[i] = xIndex;
oldPosition = i;
} else {
//throw some custom exception; we found the other X here.
}
}
}

Simply changing the value of a variable changes the value of a node in my linked list? This should not happen

So, I am making a chess engine in Java. Given a current board configuration, the AI should figure out each possible move it can make, add it to a linked list of all possible moves, and return that list. Currently I am testing the following board configuration:
/*bRbNbBbQbKbBbNbR
bPbPbPbPbPbPbPbP
NuNuNuNuNuNuNuNu
NuNuNuwRNuNuNuNu
NuNuNuNuNuNuNuNu
NuNuNuNuNuNuNuNu
wPwPwPwPwPwPwPwP
NuwNwBwQwKwBwNwR*/
"bR" means black rook, "bN" is black knight, etc. "Nu" means null or no piece. In this configuration I moved the bottom-left white rook to the middle of the board.
The following method, possibleMoves(), in my Mobility class is what should generate and return a linked list of all of the possible board configurations. Each index i corresponds to pieces on the board starting from the left. In this case the AI is white, so 0 is the leftmost white pawn, 7 is the rightmost white pawn, 8 is the white rook that is now in the center of the board, 9 is the other white rook, etc. Right now I'm only testing the rooks, so the other conditionals are empty. nonPawnBoardGen() returns a sublist of possible board configurations.
public LL possibleMoves(){
LL children = new LL();
/*
* check each piece
*/
for(int i = 0; i < 16; i++){
if(i < 8){//pawns
}
else if(i < 10){//rooks
children.joinWith(nonPawnBoardGen(i, 0, -1)); //positions to the left
children.joinWith(nonPawnBoardGen(i, 0, 1)); //right
children.joinWith(nonPawnBoardGen(i, -1, 0)); //up
children.joinWith(nonPawnBoardGen(i, 1, 0)); //down
}
else if(i < 12){
// checkKnight(r, c, int dR, int dC)
}
else if(i < 14){//bishops
}
else{ //king, queen
}
}
return children;
}
joinWith(), in my LL class, joins a sublist with the total children linked list.
public void joinWith(LL newList){
if(newList.isEmpty())
return;
if(this.isEmpty()){
first = newList.getFirst();
last = newList.getLast();
}
else{
last.next = newList.getFirst();
last = newList.getLast();
}
}
The following function, nonPawnBoardGen(), is another function in my Mobility which gets passed a piece index and a unit vector. So, if I want to check all of the possible left moves of the rook in the center of the board, I would call nonPawnBoardGen(8, 0, -1) because the rook is index 8, it will remain in the same row, and it will iterate through columns to the left. That function call should return a sublist of all of the possible board configurations involving this rook because I would still need to check everything to the right, up, and down from the rooks current position.
private LL nonPawnBoardGen(int index, int vecR, int vecC){
LL boardSubLst = new LL();
int sR, sC; //source row and col
if(turn == true){//white
//last 16 coords are white pieces
if(coords[index + 16] == null){//if piece does not exist, return
return null;
}
sR = coords[index + 16].getRow(); //each coord is an object containing a row and col value
sC = coords[index + 16].getCol();
}
else{//black
//first 16 coords are black pieces
if(coords[index] == null){
return null;
}
sR = coords[index].getRow();
sC = coords[index].getCol();
}
int curR = sR; //current row
int curC = sC; //current col
curR+=vecR; //iterate by unit vector
curC+=vecC;
while(curR > -1 && curR < 8 && curC > -1 && curC < 8){ //while in range of board
if(turn == true){//white
if(board[curR][curC].charAt(0) != 'w'){ //if space is free or opposite color, valid move
coords[index + 16].setRow(curR); //move rook to new position
coords[index + 16].setCol(curC);
if(board[curR][curC].charAt(0) == 'b'){ //if space contains piece of opposite color,
int r, c; //piece needs to be removed
for(int j = 0; j < 16; j++){ //iterate through 16 opponent pieces
r = coords[j].getRow();
c = coords[j].getCol();
if(curR == r && curC == c){ //check which opponent's piece's coords match
coords[j] = null; //the rook's current coords, then remove opp's piece
boardSubLst.insert(coords); //add board config to sublist
break;
}
}
break;
}
else{ //if the space is null, simply add board config to sublist
boardSubLst.insert(coords);
}
}
else{ //if space is same color, break
break;
}
}
else{//black
if(board[curR][curC].charAt(0) != 'b'){
coords[index].setRow(curR);
coords[index].setCol(curC);
if(board[curR][curC].charAt(0) == 'w'){
int r, c;
for(int j = 0; j < 16; j++){
r = coords[j + 16].getRow();
c = coords[j + 16].getCol();
if(curR == r && curC == c){
coords[j + 16] = null;
boardSubLst.insert(coords);
break;
}
}
break;
}
else{
boardSubLst.insert(coords);
}
}
else{
break;
}
}
curR+=vecR;
curC+=vecC;
}
return boardSubLst;
}
To make this long story short, in nonPawnBoardGen(), every time I get a new valid board configuration, I edit the board coordinates (white in this case):
coords[index + 16].setRow(curR);
coords[index + 16].setCol(curC);
and add them to a list of board configurations:
boardSubLst.insert(coords);
However, every time I edit coords, each value in the boardSubList linked list changes to the current value of coords. Why is this happening?
EDIT:
I think I can avoid this problem just by having nonPawnBoardGen() generate and return only one set of coordinates. The iterator can be saved in the class rather than locally in the function. Each set of coordinates returned can be added directly to the list of children in possibleMoves(). I will try this and see what happens...
When you call
boardSubLst.insert(coords);
You are passing the same reference to the coords array. I think you will find the easiest solution is to copy the array instead, for example using Arrays.copyOf(T[] original, int newLength)
boardSubLst.insert(Arrays.copyOf(coords, coords.length));
Or, assuming coords is of type Coord[] you could use System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Coord[] coords2 = new Coord[coords.length];
System.arraycopy(coords, 0, coords2, 0, coords.length);
boardSubLst.insert(coords2);
I stopped working on my code for awhile, just came back to it and solved the problem. Thank you Elliott and Anantha. You were both right. My array of chess piece coordinates is actually an array of objects, with each object being a set of coordinates. Originally, I changed my code so that I was making a copy of the array each time before editing the coordinates and adding the array to my list, but this was only making new references to the same set of the array's objects. The solution was to not only make new array references, but to allocate new coordinate objects within the array every time I changed coordinates. I think I'm making this sound a lot more convoluted than it actually is, but yes it was a problem of continuously referencing the same area in memory. It was not too obvious. Thanks a lot!

How to do a recursive search for a word in the Boggle game board?

Can someone help me with a psuedocode or even the recursive formula that describes the recursive search for a word in the Boggle board so I can get started?
Assuming you have a word list available somewhere, likely stored in a Trie data structure (I've created a working Trie with comments on improving its efficiency here).
Once you have a Trie structure (a prefix tree) which allows you to search for words based on their prefixes, you would want to use a recursive method something like the following psudo-code.
char[][] gameBoard = new char[4][4];
List<String> wordList = new ArrayList<String>();
//fill in the game board with characters
//Start the word search at each letter
for(int x = 0; x < 4; x++){
for(int y = 0; y < 4; y++){
recursiveWordSearch(x, y, "");
}
}
recursiveWordSearch(int x, int y, String word){
//Concatenate gameBoard[x][y] to word.
//Check to see if word is a valid word (check against your word list).
//If word, add to wordList
/*Check word list to see if any words contain current prefix. If not,
then there's no point in continuing further (return). IE if AQZ isn't the
start of any word at all in the list, no reason to keep adding letters, it's
never going to make a word. */
//Otherwise recursively call this method moving left/right/up/down
recursiveWordSearch(x+1, y, word); //move right
recursiveWordSearch(x, y+1, word); //move up
recursiveWordSearch(x-1, y, word); //move left
recursiveWordSearch(x, y-1, word); //move down
/*You'll want to make sure that x-1, x+1, y-1 and y+1 are valid values before
sending them. */
}
To store valid words, data structure with methods that check is given string prefix of some valid word and is given string a valid word is needed, e.g. Trie data structure.
To find all possible valid words, we have to start word for each position, and than recursively visit each not visited neighbour. Here are two methods of python class that implements search of all valid words on given table:
def solve_with( self, ind, inds_passed, word):
word += self.table[ind[0]][ind[1]] # Add next character
if self.trie.is_prefix(word): # Is current string prefix of valid word
if len(word) > 2 and self.trie.is_word(word): # Is current string whole word
self.ret.add(word)
inds_passed.add(ind) # Set this position as visited
for n in self.neigbours(ind): # Pass through all neighbours
if n not in inds_passed: # If not visited already
self.solve_with(n, inds_passed, word) # Recursive call
inds_passed.discard(ind) # Remove position as visited
def solve(self):
self.ret = set() # Set of all word found on table
for x in xrange(0, self.dim): # Start search with each position
for y in xrange(0, self.dim):
self.solve_with( (x,y), set(), '')
return self.ret
Java implementation using DFS approach
import java.util.Arrays;
public class WordBoggle {
static int[] dirx = { -1, 0, 0, 1 };
static int[] diry = { 0, -1, 1, 0 };
public static void main(String[] args) {
char[][] board = { { 'A', 'B', 'C', 'E' }, { 'S', 'F', 'C', 'S' }, { 'A', 'D', 'E', 'E' } };
String word = "ABFSADEESCCEA";
System.out.println(exist(board, word));
}
static boolean exist(char[][] board, String word) {
if (board == null || board.length == 0 || word == null || word.isEmpty())
return false;
boolean[][] visited = new boolean[board.length][board[0].length];
for (int i = 0; i < board.length; i++) {
resetVisited(visited);
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == word.charAt(i)) {
return DFS(board, word, i, j, 1, visited);
}
}
}
return false;
}
static void resetVisited(boolean[][] visited) {
for (int l = 0; l < visited.length; l++) {
Arrays.fill(visited[l], false);
}
}
static boolean DFS(char[][] board, String word, int i, int j, int k, boolean[][] visited) {
visited[i][j] = true;
if (k >= word.length())
return true;
for (int z = 0; z < 4; z++) {
if (isValid(board, i + dirx[z], j + diry[z], visited)) {
if (word.charAt(k) == board[i + dirx[z]][j + diry[z]]) {
return DFS(board, word, i + dirx[z], j + diry[z], k + 1, visited);
}
}
}
return false;
}

JAVA - Go game algorithm

I am trying to implement an algorithm to clear dead stones in my Go game.
I hear that floodfill is the best to achieve this as using it recursively would be most effiecient and easier to implement.
I am having trouble using it within my code and was wondering how I should go about implementing it.
This is one of my classes, it is pretty self explanatory.
import java.io.*;
public class GoGame implements Serializable {
int size;
char[][] pos; // This is the array that stores whether a Black (B) or White (W) piece is stored, otherwise its an empty character.
public GoGame(int s){
size = s;
}
public void init() {
pos = new char[size][size];
for (int i=0;i<size;i++) {
for (int j=0;j<size;j++) {
pos[i][j] = ' ';
}
}
}
public void ClearAll() {
for (int i=0;i<size;i++) {
for (int j=0;j<size;j++) {
pos[i][j] = ' ';
}
}
}
public void clear(int x, int y) {
pos[x][y]=' ';
}
public void putB(int x, int y) { //places a black stone on the board+array
pos[x][y]='B';
floodfill(x,y,'B','W');
}
public void putW(int x, int y) { //places a white stone on the board+array
pos[x][y]='W';
floodfill(x,y,'W','B');
}
public char get(int x, int y) {
return pos[x][y];
}
public void floodfill(int x, int y, char placed, char liberty){
floodfill(x-1, y, placed, liberty);
floodfill(x+1, y, placed, liberty);
floodfill(x, y-1, placed, liberty);
floodfill(x, y+1, placed, liberty);
}
}
x and y are the coordinates of the square, placed is the character of the stone put down, liberty is the other character
Any help would be amazing!
while the other answers are technically correct, you are also missing a lot more logic related to go. what you need to do is, i think (on a B move):
for each W neighbour of the move:
check that W group to see if it has any liberties (spaces)
remove it if not
flood fill is useful for finding the extent of a group of stones, but your routine needs a lot more than that (i'm simplifying here, and also trying to guess what this routine is used for - see comments below this answer).
given the above, a flood fill that identifies all the stones in a group would be something like this (note that it uses a second array for the fill, because you don't want to be changing pos just to find a group):
public void findGroup(int x, int y, char colour, char[][] mask) {
// if this square is the colour expected and has not been visited before
if (pos[x][y] == colour && mask[x][y] == ' ') {
// save this group member
mask[x][y] = pos[x][y];
// look at the neighbours
findGroup(x+1, y, colour, mask);
findGroup(x-1, y, colour, mask);
findGroup(x, y+1, colour, mask);
findGroup(x, y-1, colour, mask);
}
}
you can call that to identify a single group (and copy it into mask), so it will help you identify the members of a W group that neighbour a B move (for example), but it is only a small part of the total logic you need.
finally, note that if you want to do something with every stone in a group you have two options. you can call a routine like the one above, and then loop over mask to find the group, or you can put the action you want to do directly inside the routine (in which case you still use mask to control the extent of the flood fill in the test && mask[x][y] == ' ' but you don't use it as a result - all the work is done by the time the routine returns).
(programming something to handle go correctly, following all the rules, is actually quite complex - you've got a lot of work ahead... :o)
I'd use false proof for that. Here is how I find captured stones:
private static final int SIZE = 8;
private static final int VACANT = 0; //empty point
private static final int MY_COLOR = 1; //Black
private static final int ENEMY_COLOR = 2; //White
private static final int CHECKED = 50; //Mark for processed points
private static final int OUT = 100; //points out of the board
private static boolean isCaptured(int col, int row, int[][] board) {
boolean result = !isNotCaptured(col, row, board);
cleanBoard(board);
return result;
}
private static boolean isNotCaptured(int col, int row, int[][] board) {
int value = board[col][row];
if (!(value == MY_COLOR || value == CHECKED))
return true;
int top = row < SIZE - 1 ? board[col][row + 1] : OUT;
int bottom = row > 0 - 1 ? board[col][row - 1] : OUT;
int left = col > 0 ? board[col - 1][row] : OUT;
int right = col < SIZE - 1 ? board[col + 1][row] : OUT;
if (top == VACANT || right == VACANT || left == VACANT || bottom == VACANT)
return true;
board[col][row] = CHECKED;
return (top == MY_COLOR && isNotCaptured(col, row + 1, board))
|| (bottom == MY_COLOR && isNotCaptured(col, row - 1, board))
|| (left == MY_COLOR && isNotCaptured(col - 1, row, board))
|| (right == MY_COLOR && isNotCaptured(col + 1, row, board));
}
private static void cleanBoard(int[][] board) {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == CHECKED)
board[i][j] = MY_COLOR;
}
}
}
Then you can call method like this:
isCaptured(5, 4, board)
I think that BFS will be better for this case because you need to explore the neighbors first, so that if any of them is captured then the point is captured.
As others pointed out, there is also a "ko rule" in Go which roughly means that you are not allowed to capture back immediately when a single stone is captured (simplified). In summary, you may want to use an existing library for this.
I recommend the brugo repository, which is available in maven.
<!-- https://mvnrepository.com/artifact/be.brugo/brugo -->
<dependency>
<groupId>be.brugo</groupId>
<artifactId>brugo</artifactId>
<version>0.1.0</version>
</dependency>
It roughly works like this.
(warning: code not tested)
// create a starting position
Position position = new Position(boardSize, komi);
// play a move
Intersection whereToPlay = Intersection.valueOf(4,4);
IntStatus colorToPlay = IntStatus.BLACK;
Position position2 = position.play(whereToPlay, colorToPlay);
// watch the result.
IntStatus[][] matrix = position2.getMatrix()
It also contains objects to export to Load/Save SGF. The loading of SGF files does not only support UTF-8 but also Asian encodings. Here is a screenshot that shows how difficult this is to implement yourself:
If you also plan to use javafx, then run this demo: brugo.go.ui.javafx.goban.GobanComponentDemo
Enough to get you started.

Categories