I'm dealing with a problem in my Go Game project.
I have a board (goban), represented by 2D Array of chars. Before every next move, I would like to check for 'bubbles' in the array. Bubble should be an 4-connected area of identical chars surrounded in 4 directions by another group of specific identical chars.
If this 'bubble' exists, the characters inside should be replaced by some others. But there could be more areas and not all of them are enclosed.
For example, I have this board:
1 2 3 4 5 6 7 8 9 10 11 12 13
- - - - - - - - - - - - - - -
A | + + + + + + + + + + + + + |
B | + + + + + + + + + + + + + |
C | + + + + + + + + + + + + + |
D | + + + + + + + + + + + + + |
E | + + + + + + + + + + + + + |
F | + + O O O O + + + + + + + |
G | + O X X X X O + + + + + + |
H | + + O O X X O + + + + + + |
I | + + + + O X X O + + + + + |
J | + + + + O X O + + + + + + |
K | + + + + + O + + + + + + + |
L | + + + + + + + + + + + + + |
M | + + + + + + + + + + + + + |
- - - - - - - - - - - - - - -
And I would like to find the bubble of Xs, count them and replace them with 'Z's.
I've googled it and I think that some Connected-component labeling algorithm or FloodFill can do the job, but I'm not sure how to implement it. Is this the way or something less complicated could solve it?
Thank you
Edit:
I tried to find some pattern which could find the areas of specific character and count their liberties, but it always failed when the location was multilayered.
Changing the data structure might be the solution, but if it is possible, I would like to do it as it is now.
My current solution idea:
public void solve(){
if (boardContainsEnclosedAreas(goban, onMovePlayerStone, oppositePlayerStone){
onMovePlayerScore += countElementsInAreaAndReplaceThem(onMovePlayerStone, 'Z');
}
}
public boolean boardContainsEnclosedAreas(char[][] playingBoard, char searchedChar, char surroundingChar){
// this method should find the bubble in the playingBoard array
}
public int countElementsInAreaAndReplaceThem(char searchedChar, char replacingChar){
// the method should go through the bubble and replace all inner chars
// returns amount of replaced chars
}
You can do that with a recursive method, indeed using the FloodFill theory.
Basically, run through your grid, and each time you find an X, replace it with a Z, as well as its 4 neighbours (if applicable). But the trick is: instead of just replacing them and having to loop again each time, call the same (calling) method again to do it. The recursivity will do the rest.
Here is a (quickly done) pseudo-code version of it:
(assuming your grid is indexed from 0 to xmax, from 0 to ymax)
int numberOfBubbles = 0;
for (x = 0 to xmax) {
for (y = 0 to ymax) {
if (getCharAt(x, y) == "X") { // this if is needed because you want to count the bubbles. If not, you could just do handleBubble(x, y);
numberOfBubbles++;
handleBubble(x, y);
}
}
}
// Recursive method
void handleBubble(int x, int y) {
if (getCharAt(x, y) != "X") {
return; // exit condition
}
getCharAt(x, y) = "Z";
if (x > 0) handleBubble(x-1, y);
if (x < xmax) handleBubble(x+1, y);
if (y > 0) handleBubble(x, y-1);
if (y < ymax) handleBubble(x, y+1);
}
As far as I know, this is the only solution for this problem, which works whatever weird shape your bubble is. The complexity is not bad either.
This can be optimised further, as it currently checks for chars that are obviously not containing an X any more (because they've just been replaced with Z). This is left as an exercise to the reader :)
(NB: The minesweeper game, among other, is based on that solution)
Here's an algorithm (in pseudocode) that I've used for similar image analysis needs:
regions = Collection<Set<Point>>
foreach (Point p : allBoardLocations)
if (charAtLocation(p) != 'X') continue
foundInRegion = false
for (Set<Point> s : regions)
if (s.contains(p))
foundInRegion=true
break;
if (!foundInRegion)
newRegion = new Set<Point>()
stack = new Stack<Point>()
stack.push(p)
while (!stack.empty())
foreach (Point n : neighboringPoints(stack.pop()))
if (charAtLocation(n) == 'X')
if (!newRegion.contains(n))
newRegion.add(n);
stack.push(n);
Bascially, you maintain a collection of sets of points where each set represents a "bubble" of contiguous points on the board. Scan each location on the board and if it's an 'X' and it is not already in a region then create a new region and a stack containing the location and while there is any item on the stack, visit its neighbors searching for unvisited 'X's, adding them to the new region and stack as discovered.
At the end the you'll have a collection of sets of points, each representing a "bubble".
Related
I posted a question yesterday about how to find the sum of a recursive method
The post is located here: Recursive method to sum x^0 + x^1 + x^2 + ... +x^n
How do i make a recursive method in reverse.
Ex: x^n + x^(n-1) + x^(n-2) + ... + 3 + 2, + 1
I am having a lot of trouble wrapping my brain around recursive methods and a lot of other resources aren't helping me out. Any help is appreciated. Thanks Everyone!
The accepted answer has:
double r = compute(n-1,x)+ (v = Math.pow(x,n));
System.out.print(" + " + v);
If we unwrap that nested assignment, it is:
v = Math.pow(x, n);
double r = compute(n - 1, x) + v;
System.out.print(" + " + v);
To reverse the output order, just swap the compute() and print() calls. We also need to swap the " + " and v in the print statement.
v = Math.pow(x, n);
System.out.print(v + " + ");
double r = compute(n - 1, x) + v;
That's it!
27.0 + 9.0 + 3.0 + 1.0
v = 40.0
Java
int x = 5;
System.out.println(" x + 5 is " + x + 5);//correct
System.out.println("x += 5 is " + x += 5);// why wrong?
Even though, these 2 println is including calculation but why second println is error.Thanks
What you are doing causes an error because the + is seen as an operator to seperate parts of the string. Try placing that part between brackets like:
System.out.println("x += 5 is " + (x += 5));
This might fix it as you exclude the + from the string. Hope this helps you a bit, and that I am correct in my statement.
My professor made us do this in class and I am super confused looking at those outputs.
Does this(x + " " + y + " " + z) mean add all 3 variables if (y > z) ? The output doesn't make any sense to me at all.
public class Practice
{
public static void main (String[] args)
{
int x=10, y=11, z=3;
do
{
System.out.println(x + " " + y + " " + z);
if (y > z)
{
y = y-5;
}
else
{
y = y + 3;
}
x = x - 2;
} while (x > 0);
System.out.println("Bye");
}
}
OUTPUT:
10 11 3
8 6 3
6 1 3
4 4 3
2 -1 3
Bye
System.out.println(x + " " + y + " " + z);
This is not adding x,y and z. Its just appending values in string for printing
your numbers are converted to strings
System.out.println(7 + 5 + " ");
This prints out 12, but in another order
System.out.println(" " + 5 + 7);
it prints out 57. Why is this?
Firstly, this has nothing to do with System.out.println. You'll see exactly the same effect if you use:
String x = 7 + 5 + "";
String y = " " + 5 + 7;
It's got everything to do with associativity. The + operator is left-associative, so the above two statements are equivalent to:
String x = (7 + 5) + "";
String y = (" " + 5) + 7;
Now look at the results of the first expression in each case: 7 + 5 is just 12, as int... whereas " " + 5 is "5" (a string).
Or to break it down further:
int x1 = 7 + 5; // 12 (integer addition)
String x = x1 + ""; // "12" (conversion to string, then string concatenation)
String y1 = " " + 5; // "5" (conversion to string, then string concatenation)
String y = y1 + 7; // "57" (conversion to string, then string concatenation)
Justification: JLS 15.18 (additive operators):
The additive operators have the same precedence and are syntactically left-associative (they group left-to-right).
Easy. System.out.println(7 + 5 + " ") is viewed as a mathematical equation, whereas System.out.println(" " + 5 + 7) whereas having the space beforehand, Java (I'm assuming) views it as a string. Thus 'concatenating' the two.
I have a text file with only +'s and blank spaces. i have to find a target within this file that looks like a spaceship. but the spaceship does not have to be perfect.
thanks in advance
this is the text file with the targets, a smaller version. there is at least 1 target in this.
+ ++ + + + + + + + +++ +
+ ++ + + ++ + + ++ ++ + +
+ + + + ++ + + ++++ ++
+ + ++++ + ++ + ++ +
+ + + + ++ + ++ + +
+ + + + ++ + + + +
+ + ++ + + +++ ++ +++
+ + + + ++ + ++ ++ + ++
+ + + + + ++ + + + + ++ + +
+ ++ + + ++ ++ +++ +
+ ++ + ++ + + + + ++ ++ +
+ + ++ ++ + + + + +
+ + + + + ++ + +
this is the target
+
+
+++
+++++++
++ ++
++ + ++
++ +++ ++
++ + ++
++ ++
+++++++
+++
I have tried Regex pattern reading but the file is too big so i decided to go against that. I do not know any other way to solve this.
I don't think you can or need to do anything really fancy here.
You can start by encoding your patterns. In this case, you could:
Encode every line as a string, and the lines themselves in an array: String[]
Encode every line as a string, and the lines themselves in an List: List<String>
Encode every line as a char[], and the lines themselves in an array: char[][]
Number 3 has the benefit that you can easily index it as a matrix:
char[][] matrix = ...;
char ch = matrix[row][column];
So you have:
char[][] search = new char[searchRows][searchColumns];
char[][] target = new char[targetRows][targetColumns];
You algorithm could be:
For every possible position that target could occur in search, calculate how many characters are equal
The position with the highest amount of equal characters wins
Get a percentage by dividing this amount of equal characters by the total amount of characters in target and you get a percentage
If the percentage is over a certain threshold, it's a match
Step1:
The maximum row or column is the total number of rows or columns in the search pattern minus the rows or columns in the target pattern.
int maxMatch = 0;
int maxMatchRow = -1;
int maxMatchColumn = -1;
for (int row = 0; row <= searchRows - targetRows; row++) {
for (int column = 0; columns <= searchColumns - targetColumns; column++) {
int match = calculateMatch(search, target, row, column);
if (match > maxMatch) {
maxMatchRow = row;
maxMatchColumn = column;
}
}
}
And to calculate matches in method calculateMatch, just add one if the characters in search and target are the same (but add offset row and column when you check search, not when you check target)
I think you should be able to finish it from there.