Recursive Method StackOverflow Error - Minesweeper - recursion
I am writing the recursive method for a minesweeper game and I am encountering a stackOverflow error in the recursive method that clears out empty spaces, The error does not occur when checking for 3 of the surrounding spaces but only when checking all eight. Can you please help identify the issue?
The stack trace is :
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at java.awt.Component.firePropertyChange(Component.java:8419)
at javax.swing.AbstractButton.setText(AbstractButton.java:306)
at Minesweeper.Minesweeper.showTile(Minesweeper.java:105)
at Minesweeper.Minesweeper.clearEmpty(Minesweeper.java:137)
at Minesweeper.Minesweeper.clearEmpty(Minesweeper.java:177)
The class:
public class Minesweeper implements ActionListener {
JFrame frame = new JFrame("Minesweeper");
JButton reset = new JButton("Reset");
JButton solve = new JButton("Solve");
JToggleButton[][] buttons = new JToggleButton[20][20];
int[][] counts = new int [20][20];
Container grid = new Container();
final int MINE = 10;
public static void main(String[] args)
{
new Minesweeper();
}
public Minesweeper()
{
frame.setSize(600, 600);
frame.setLayout(new BorderLayout());
frame.add(reset, BorderLayout.NORTH);
frame.add(solve, BorderLayout.SOUTH);
reset.addActionListener(this);
solve.addActionListener(this);
grid.setLayout(new GridLayout(20, 20));
for (int r = 0; r < buttons.length; r++) {
for (int c = 0; c < buttons[0].length; c++) {
buttons[r][c] = new JToggleButton();
buttons[r][c].addActionListener(this);
grid.add(buttons[r][c]);
buttons[r][c].setSize(frame.getWidth() / 20, frame.getHeight() / 22);
}
}
frame.add(grid,BorderLayout.CENTER);
addRandomMines();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void addRandomMines()
{
ArrayList<Integer> mineList = new ArrayList<Integer>();
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++){
mineList.add((x*100)+y);
}
}
counts = new int[20][20];
for (int i = 0; i < 30; i++) {
int choice = (int)(Math.random()*mineList.size());
counts[mineList.get(choice)/100][mineList.get(choice)%100] = MINE;
mineList.remove(choice);
}
for (int x = 0; x < counts.length; x++) {
for (int y = 0; y < counts[0].length; y++){
if (counts[x][y]!=MINE) {
int mineCount = 0;
if (x > 0 && y > 0 && counts[x - 1][y - 1] == MINE)
mineCount++;
if (y > 0 && counts[x][y - 1] == MINE)
mineCount++;
if (x > 0 && counts[x - 1][y] == MINE)
mineCount++;
if (x < counts.length - 1 && counts[x + 1][y] == MINE)
mineCount++;
if (y < counts.length - 1 && counts[x][y + 1] == MINE)
mineCount++;
if (x < counts.length - 1 && y < counts.length - 1 && counts[x + 1][y + 1] == MINE)
mineCount++;
if (x > 0 && y < counts.length - 1 && counts[x - 1][y + 1] == MINE)
mineCount++;
if (x < counts.length - 1 && y > 0 && counts[x + 1][y - 1] == MINE)
mineCount++;
counts[x][y] = mineCount;
}
}
}
}
public void showTile(int r, int c)
{
if (counts[r][c] == 0) {
buttons[r][c].setText("");
buttons[r][c].setSelected(true);
}
else if (counts[r][c]==MINE) {
buttons[r][c].setForeground(Color.red);
buttons[r][c].setText("X");
buttons[r][c].setSelected(true);
}
else {
buttons[r][c].setText(counts[r][c] + "");
if (counts[r][c]==1)
buttons[r][c].setForeground(Color.blue);
else if (counts[r][c]==2)
buttons[r][c].setForeground(Color.magenta);
else if (counts[r][c]==3)
buttons[r][c].setForeground(Color.green);
buttons[r][c].setSelected(true);
}
}
public void lostGame() {
for (int x = 0; x < buttons.length; x++) {
for (int y = 0; y < buttons[0].length; y++) {
if (counts[x][y]==MINE) {
showTile(x, y);
}
}
}
}
public void clearEmpty(ArrayList<Integer> toClear)
{
if (toClear.size()==0){
return;
}
else {
int x = toClear.get(0)/100;
int y = toClear.get(0)%100;
toClear.remove(0);
if (counts[x][y]==0) {
if (x > 0 && y > 0) {
showTile(x-1,y-1);
if (counts[x-1][y-1]==0)
toClear.add((x-1)*100 + (y-1));
}
if (y > 0) {
showTile(x,y-1);
if (counts[x][y-1]==0)
toClear.add(x*100 + (y-1));
}
if (x <counts.length-1 && y > 0) {
showTile(x+1,y-1);
if (counts[x+1][y-1]==0)
toClear.add((x+1)*100 + (y-1));
}
if (x > 0) {
showTile(x-1,y);
if (counts[x-1][y]==0)
toClear.add((x-1)*100 + y);
}
if (x <counts.length-1 && y > 0) {
showTile(x+1,y);
if (counts[x+1][y]==0)
toClear.add((x+1)*100 + y);
}
if (x > 0 && y < counts[0].length-1) {
showTile(x-1,y+1);
if (counts[x-1][y+1]==0)
toClear.add((x-1)*100 + (y+1));
}
if (y < counts[0].length-1) {
showTile(x,y+1);
if (counts[x][y+1]==0)
toClear.add(x*100 + (y+1));
}
if (x <counts.length-1 && y < counts[0].length-1) {
showTile(x+1,y+1);
if (counts[x+1][y+1]==0)
toClear.add((x+1)*100 + (y+1));
}
}
clearEmpty(toClear);
}
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(reset)) {
for (int r = 0; r < buttons.length; r++) {
for (int c = 0; c < buttons[0].length; c++) {
buttons[r][c].setSelected(false);
buttons[r][c].setText("");
}
}
addRandomMines();
} else if (event.getSource().equals(solve)) {
} else {
for (int r = 0; r < buttons.length; r++) {
for (int c = 0; c < buttons[0].length; c++) {
if (event.getSource().equals(buttons[r][c])) {
if (counts[r][c] == MINE) {
showTile(r, c);
lostGame();
}
else if (counts[r][c] == 0) {
ArrayList<Integer> toClear = new ArrayList<Integer>();
toClear.add(r*100+c);
clearEmpty(toClear);
}
else {
showTile(r, c);
}
}
}
}
}
}
}
I think that you're using the wrong algorithm...
Try to use an iterative instead of an recursive approach.
As User404 already mentioned your current algorithm keeps the list growing...
For your implementation: you have 400 tiles. Assuming (worst case) all tiles are empty you call your method clearEmpty() once. You will find out that all 8 neighbors are empty so you add this 8 neighbors to the list while only removing the first one. Now you pass the array to the method again (2nd call) and will find 8 neighbors for the first entry again. So your 3rd call will have a list with 15 tiles.
Real problem
This way you will never come to an end as you never check if the current checked tile is already cleared but you only add to the list more than you will ever remove.
Solution
At least you should check if the tile you want to add to the list is already cleared or is already in the list.
You example is a clear example why recursive algorithms should be used with care as the termination is difficult sometimes and also you have to take care that no work is done multiple times.
Related
Why do I get a StackOverFlow error on the first recursive call?
Below is the code I am referring to, and the first recursive call # checkDirections(grid, i - 1, j) is giving me a StackOverFlow error. I understand that this means the code is not hitting the base case, but I do not understand why. class Solution { public int orangesRotting(int[][] grid) { int rowLength = grid.length; int colLength = grid[0].length; int minMinutes = 0; for (int i = 0; i < rowLength; i++) { for (int j = 0; j < colLength; j++) { if (grid[i][j] == 2) { checkDirections(grid, i, j); } } } return minMinutes; } public void checkDirections(int[][] grid, int i, int j) { if ((i < 0 || i > grid.length || j < 0 || j > grid[0].length) || grid[i][j] == 0) { return; } else if (grid[i][j] == 1) { grid[i][j] = 2; return; } //check left checkDirections(grid, i - 1, j); //check right checkDirections(grid, i + 1, j); //check up checkDirections(grid, i, j - 1); //check down checkDirections(grid, i, j + 1); } }
Explanation for crashing at n = 16
I am having a trouble in completing a homework of mine in C.The task is given an integer n print all the binary numbers with the length n that do not have 2 consecutive zeros int them and that includes leading zeros, note that at least one of the functions has to be recursive. Here is an example if n is 4 then the binary number 10 is treated 0010 and therefore wont be printed because it has 2 leading zeros. My problem is that my code crashes if n = 16 and I do not know why even though I have done a lot of debugging. Here is my code, Thanks for any help here. void binaries_n_digits_no_00(int n) { if(n < 0) { return; } print_binaries_n_digits_no_00(0,n); } void print_binaries_n_digits_no_00(long int current_binary_index,int n) { int num_of_leading_zeros; if((current_binary_index > (power(2,n) - 1)) || n == 0) { return; } num_of_leading_zeros = n - binary_num_length(current_binary_index); if((binary_not_contain_00(current_binary_index) == 1) && (num_of_leading_zeros == 1 || num_of_leading_zeros == 0)){ if(current_binary_index == 0 && n == 1) { printf("\n0"); } else if(num_of_leading_zeros == 1) { if(current_binary_index != 0) { printf("\n0"); print_binary(current_binary_index); } } else{ printf("\n"); print_binary(current_binary_index); } } print_binaries_n_digits_no_00(current_binary_index+1,n); } int binary_not_contain_00(long int num) { if(num/2 == 0) { return 1; } if(((num%2) == 0) && (((num/2) % 2) == 0)) { return 0; } return binary_not_contain_00(num/2); } void print_binary(long int num) { if(num > 1) { print_binary(num/2); } printf("%d",num%2); } int binary_num_length(long int num) { if(num <= 1) { return 1; } else{ return (1 + binary_num_length(num/2)); } } long int power(int m, int n) { if(n == 0) { return 1; } return m * power(m,n-1); }
Alternative to System.Web.Security.Membership.GeneratePassword in aspnetcore (netcoreapp1.0)
Is there any alternative to System.Web.Security.Membership.GeneratePassword in AspNetCore (netcoreapp1.0). The easiest way would be to just use a Guid.NewGuid().ToString("n") which is long enough to be worthy of a password but it's not fully random.
Here's a class/method, based on the source of Membership.GeneratePassword of that works on .NET Core: public static class Password { private static readonly char[] Punctuations = "!##$%^&*()_-+=[{]};:>|./?".ToCharArray(); public static string Generate(int length, int numberOfNonAlphanumericCharacters) { if (length < 1 || length > 128) { throw new ArgumentException(nameof(length)); } if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0) { throw new ArgumentException(nameof(numberOfNonAlphanumericCharacters)); } using (var rng = RandomNumberGenerator.Create()) { var byteBuffer = new byte[length]; rng.GetBytes(byteBuffer); var count = 0; var characterBuffer = new char[length]; for (var iter = 0; iter < length; iter++) { var i = byteBuffer[iter] % 87; if (i < 10) { characterBuffer[iter] = (char)('0' + i); } else if (i < 36) { characterBuffer[iter] = (char)('A' + i - 10); } else if (i < 62) { characterBuffer[iter] = (char)('a' + i - 36); } else { characterBuffer[iter] = Punctuations[i - 62]; count++; } } if (count >= numberOfNonAlphanumericCharacters) { return new string(characterBuffer); } int j; var rand = new Random(); for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++) { int k; do { k = rand.Next(0, length); } while (!char.IsLetterOrDigit(characterBuffer[k])); characterBuffer[k] = Punctuations[rand.Next(0, Punctuations.Length)]; } return new string(characterBuffer); } } } I've omitted the do...while loop over the CrossSiteScriptingValidation.IsDangerousString. You can add that back in yourself if you need it. You use it like this: var password = Password.Generate(32, 12); Also, make sure you reference System.Security.Cryptography.Algorithms.
System.Random doesn't provide enough entropy when used for security reasons. https://cwe.mitre.org/data/definitions/331.html Why use the C# class System.Random at all instead of System.Security.Cryptography.RandomNumberGenerator? Please see the example below for a more secure version of #khellang version public static class Password { private static readonly char[] Punctuations = "!##$%^&*()_-+[{]}:>|/?".ToCharArray(); public static string Generate(int length, int numberOfNonAlphanumericCharacters) { if (length < 1 || length > 128) { throw new ArgumentException("length"); } if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0) { throw new ArgumentException("numberOfNonAlphanumericCharacters"); } using (var rng = RandomNumberGenerator.Create()) { var byteBuffer = new byte[length]; rng.GetBytes(byteBuffer); var count = 0; var characterBuffer = new char[length]; for (var iter = 0; iter < length; iter++) { var i = byteBuffer[iter] % 87; if (i < 10) { characterBuffer[iter] = (char)('0' + i); } else if (i < 36) { characterBuffer[iter] = (char)('A' + i - 10); } else if (i < 62) { characterBuffer[iter] = (char)('a' + i - 36); } else { characterBuffer[iter] = Punctuations[GetRandomInt(rng, Punctuations.Length)]; count++; } } if (count >= numberOfNonAlphanumericCharacters) { return new string(characterBuffer); } int j; for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++) { int k; do { k = GetRandomInt(rng, length); } while (!char.IsLetterOrDigit(characterBuffer[k])); characterBuffer[k] = Punctuations[GetRandomInt(rng, Punctuations.Length)]; } return new string(characterBuffer); } } private static int GetRandomInt(RandomNumberGenerator randomGenerator) { var buffer = new byte[4]; randomGenerator.GetBytes(buffer); return BitConverter.ToInt32(buffer); } private static int GetRandomInt(RandomNumberGenerator randomGenerator, int maxInput) { return Math.Abs(GetRandomInt(randomGenerator) % maxInput); } }
Arduino Classifying EMG signal by ANN
Currently I'm trying to replicate this wonderful project https://www.youtube.com/watch?v=u8pwmzUVx48, though with more minimum hardware, in which for the classification i'm using a neural network embbeded inside the arduino uno which had been trained offline in more stronger PC. The code of the classifier is here #include <math.h> #include <Wire.h> double dataMagnitude[100]; double feature[7]; double feat_hid[10]; double output[4]; int classi; const double w_hid[8][10] = { {18.9336670380822,11.9186055569093,-5.40114594311576,-21.1410711100689,-49.7798510124792,-14.8215222433047,-34.7308138535581,118.348601654702,-13.6275407707706,-11.6090487951753}, {-21.4994463865001,72.0672467700052,4.05328299052883,21.4875491464005,51.1604296852446,-15.8459578543758,6.30350750703905,-152.565733734759,12.8648040583067,13.8199895673734}, {8.66214515599865,-200.488705071393,-5.86973559011113,-23.4805286444925,-1.11016147795671,-3.04686605995311,-93.4143749063794,-73.8925691072615,-6.18427236042285,-10.9872535411407}, {-12.317892447947,-37.2154526807162,3.83978412266769,2.12866508710205,-11.9740446238331,10.2198116665101,41.9369083083022,63.2036147993661,1.40768362243561,15.4978881385563}, {7.58319670243546,161.895072542918,-2.3654701067923,1.91708846557259,-2.87224127392426,-16.5850302581614,45.2372430254377,34.255971511768,-2.30234070310014,-7.8952356779332}, {0.603971580989218,128.0244079393,0.628535912033608,-1.25788426737745,-0.204480047424961,-41.3372322514891,3.26860448943873,4.163893213914,0.443532051478394,-0.276136697942473}, {-3.63944154925129,18.3802540188974,0.975095285922393,-0.326187652485656,1.25521855841132,39.4877166809573,-15.3542772116519,-14.9546824078715,0.965532742621277,3.72386985716534}, {5.93321854314305,12.673690719929,-3.36580252980446,-21.2089241183081,-10.8980644878121,-7.29095431091201,-30.2240843969778,-2.48980394834853,-5.4167647620581,-5.68671825319015} }, w_out[11][4] = { {1.07887052819228,-21.9615926704441,105.450147012522,-84.5674248703326}, {0.0344508533215184,0.551350792323934,-0.96380329372866,0.37800164808339}, {-99.251397621058,23.1671754381478,7.53147169676884,68.5527504861813}, {-5.0354869957171,4.36918523413481,0.408725687040089,0.257576074543764}, {-27.4478368365984,7.00659524306471,1.74370945871769,18.6975321348269}, {-0.213736004615516,-0.784795805217531,0.0732416484342244,0.925290161399087}, {8.62052547929066,-45.9408034639393,116.959261953552,-79.6389839689755}, {-8.5317661802465,45.4251911929737,-117.146523754862,80.2530987422069}, {127.053878005091,-29.4397580015468,-9.33919798608733,-88.2749220175082}, {1.11869995358251,-21.5111648695486,105.002356379838,-84.6098914639344}, {-5.81786935588552,3.78305066207264,0.11556429335063,-0.0807455995360207} }; double S, Sig, Sigma, Sigma1, Sigma2, MAV, RMS, VAR, SDT, WL, ZC, SSC, Xn, accum, accumi; char analogpin = 0, N=100; void setup() { /* add setup code here */ Serial.begin(9600); Wire.begin(); } void loop() { do{ //data acqusition for( int i = 0; i<100;i++) { Xn = (analogRead(analogpin))-510; dataMagnitude[i]=Xn; delayMicroseconds(830); // Serial.println(dataMagnitude[i]); } S = 0; for (size_t i = 0; i < N; i++) { S = S + dataMagnitude[i]; } Sig = 0; Sigma = 0; Sigma1 = 0; Sigma2 = 0; for (size_t i = 0; i < N; i++) { Sig = Sig + abs(dataMagnitude[i]); Sigma = Sigma + pow(dataMagnitude[i], 2); Sigma1 = Sigma1 + pow((dataMagnitude[i] - S / N), 2); } for (size_t i = 0; i < N - 1; i++) { Sigma2 = Sigma2 + abs(dataMagnitude[i + 1] - dataMagnitude[i]); } //featureextract MAV = (((1 / (double)N)*Sig-27.67)*2/(272.02-27.67))-1; RMS = (sqrt((1 / (double)N)*Sigma)-34.91002721)*2/(284.1419012-34.91002721)-1; VAR = (((1 / (double)(N))*Sigma1)-698.4139)*2/(52178.5691-698.4139)-1; SDT = (sqrt((1 / (double)(N)) * Sigma1)-26.42752164)*2/(228.4262881-26.42752164)-1; WL = (Sigma2-1621)*2/(11273-1621)-1; //ZC = 0; for (size_t i = 0; i < N - 1; i++) { if (dataMagnitude[i] == 0 && dataMagnitude[i] != dataMagnitude[i + 1]) { ZC++; } } ZC = (ZC-0)*2/(39-0)-1; //SSC = 0; for (size_t i = 0; i < N - 2; i++) { if (dataMagnitude[i]>dataMagnitude[i + 1] && dataMagnitude[i + 1]<dataMagnitude[i + 2]) { SSC++; } else if (dataMagnitude[i]<dataMagnitude[i + 1] && dataMagnitude[i + 1]>dataMagnitude[i + 2]) { SSC++; } } SSC = (SSC-48)*2/(78-48)-1; double feature[] = { MAV, RMS, VAR, SDT, WL, ZC, SSC }; //neural network construction //first-hidden layer for (int i = 0; i < 10; i++) { accum = w_hid[7][i]; for (int j = 0; j < 7; j++) { accum += (feature[j] * w_hid[j][i]); } feat_hid[i] = tanh(accum); } for (int i = 0; i < 4; i++) { accumi = w_out[10][i]; for (int j = 0; j < 10; j++) { accumi += (feat_hid[j] * w_out[j][i]); } output[i] = round(accumi); } //Classify the output if (output[0] == 1 && output[1] < 1 && output[2] < 1 && output[3] < 1) { classi = 1; } else if (output[0] < 1 && output[1] == 1 && output[2] < 1 && output[3] < 1) { classi = 2; } else if (output[0] < 1 && output[1] < 1 && output[2] == 1 && output[3] < 1) { classi = 3; } else if (output[0] < 1 && output[1] < 1 && output[2] < 1 && output[3] == 1) { classi = 4; } else { classi = 0; } Wire.beginTransmission(5); Wire.write(classi); //Wire.write(int(output[2])); Wire.endTransmission(); Serial.println("wew"); Serial.println(output[0]); Serial.println(output[1]); Serial.println(output[2]); Serial.println(output[3]); //Serial.println(classi); //Serial.println(feature[1]); //Serial.println(feature[2]); //Serial.println(feature[3]); //Serial.println(feature[4]); //Serial.println(feature[5]); //Serial.println(feature[6]); for (size_t i = 0; i < 10; i++) { feat_hid[i] = 0; } for (size_t i = 0; i < 4; i++) { output[i] = 0; } }while(analogRead(analogpin)>0); } But the real time implementation is rather dissatisfying, in which the output always mismatched, though by offline implementation is quite better. My assumption is that the data capturing when online is rather bad, or maybe because any other things? Thanks for any feedback
How did you train the network offline? If you have used Matlab to train the network (i.e. to identify the weight and bias values) and then implement the same network on Arduino. In such case we have problems with number precision. Matlab by default works on 64 bit precision whereas Arduino works on 32 bit. Difference in the way the numbers are represented creates the Error.
Changing method to be completely recursive
void merge(List<E> l, int lower, int upper) { ArrayList<E> array = new ArrayList<E>(); for (int i = lower; i <= upper; i++) array.add(list.get(i)); int front= 0; int front2= (array.size() + 1) / 2; for (int i = lower; i <= upper; i++) { if (front2 >= array.size() || (first < ((array.size() + 1) / 2) && (array.get(first).compareTo(array.get(second)) <= 0))) { l.set(i, array.get(front)); front++; }// end if else { l.set(i, array.get(front2)); front2++; } } } This is my method. I want to change it to be completely recursive(I don't want to use for loops), but I simply don't see how. Is there a way to make this recursive or avoid using loops?