I've got this problem. My function seems to check outside the borders of my 10x10 board. It's made to return the number of mines around a Tile[row, column] but I get :
minesweeper.js:152 Uncaught TypeError: Cannot read property '0' of undefined
at getAdjacentMines (minesweeper.js:152)
Here is my function, thanks everyone for your time.
function getAdjacentMines(row, col) {
let count = 0;
let minRow = row - 1;
let maxRow = row + 1;
let minCol = col - 1;
let maxCol = col + 1;
if (minRow < 0) minRow = 0;
if (maxRow > board.length) maxRow = board.length - 1;
if (minCol < 0) minCol = 0;
if (maxCol > board.length) maxCol = board.length - 1;
for (row = minRow; row <= maxRow; row++) {
for (col = minCol; col <= maxCol; row++) {
if (board[row][col].boom === true) count++;
}
}
return count;
}
Welcome to Stack overflow!
you seem to have a typo on this section
for (row = minRow; row <= maxRow; row++) {
for (col = minCol; col <= maxCol; row++) {
if (board[row][col].boom === true) count++;
}
}
That needs to be:
// v here
for (col = minCol; col <= maxCol; col++) {
Not:
for (col = minCol; col <= maxCol; row++) {
That means that 'row' will keep increasing until it reaches an index of 10, then it will try to read 'col' (which will be 0) of row 10 (which will be undefined)
Related
So basically I all my code is within a while loop. Within that I have a for loop which checks for a bee in the neighboruging grids. When it detects a bee it calls a function. I need all the functions called by that to wait and only run when the while loop is just about to be finsihed (outside the for loop but inside the while loop).
Here is my code:
Cell.prototype.floodFillEach = function() {
grid[orginalX][orginalY].floodFill();
while(repeat<=7){
for (var xoff = 0-x12; xoff <= 0+x12; xoff++) {
for (var yoff = 0; yoff <= 0+x12; yoff++) {
var i = orginalX + xoff;
var j = orginalY + yoff;
if (i > -1 && i < cols && j > 0 && j < rows) {
var neighbour = grid[i][j];
if (neighbour.marked2) {
console.log("x:"+i+" y:"+j);
neighbour.floodFill(); // everytime this function is called within this while loop i need it to wait untill...
}
}
}
}
// ... here. I need the functions called to wait and run here and after they are done only then proceed with the rest of this while loop
x12++
repeat++
}
}
Store in an array, then call at the end.
Cell.prototype.floodFillEach = function() {
grid[orginalX][orginalY].floodFill();
while(repeat<=7){
const neighboursToFloodFill = [];
for (var xoff = 0-x12; xoff <= 0+x12; xoff++) {
for (var yoff = 0; yoff <= 0+x12; yoff++) {
var i = orginalX + xoff;
var j = orginalY + yoff;
if (i > -1 && i < cols && j > 0 && j < rows) {
var neighbour = grid[i][j];
if (neighbour.marked2) {
console.log("x:"+i+" y:"+j);
neighboursToFloodFill.push(neighbour);
}
}
}
}
for (const neighbour of neighboursToFloodFill) {
neighbour.floodFill();
}
x12++
repeat++
}
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I understand the "Depth-First" maze geerating algorithm but I need a little help implementing it with Javascript.
Maze Generation at Rosetta Code contains many implementations to generate and show a maze, using the simple Depth-first search algorithm:
Code in JavaScript:
function maze(x,y) {
var n=x*y-1;
if (n<0) {alert("illegal maze dimensions");return;}
var horiz=[]; for (var j= 0; j<x+1; j++) horiz[j]= [];
var verti=[]; for (var j= 0; j<y+1; j++) verti[j]= [];
var here= [Math.floor(Math.random()*x), Math.floor(Math.random()*y)];
var path= [here];
var unvisited= [];
for (var j= 0; j<x+2; j++) {
unvisited[j]= [];
for (var k= 0; k<y+1; k++)
unvisited[j].push(j>0 && j<x+1 && k>0 && (j != here[0]+1 || k != here[1]+1));
}
while (0<n) {
var potential= [[here[0]+1, here[1]], [here[0],here[1]+1],
[here[0]-1, here[1]], [here[0],here[1]-1]];
var neighbors= [];
for (var j= 0; j < 4; j++)
if (unvisited[potential[j][0]+1][potential[j][1]+1])
neighbors.push(potential[j]);
if (neighbors.length) {
n= n-1;
next= neighbors[Math.floor(Math.random()*neighbors.length)];
unvisited[next[0]+1][next[1]+1]= false;
if (next[0] == here[0])
horiz[next[0]][(next[1]+here[1]-1)/2]= true;
else
verti[(next[0]+here[0]-1)/2][next[1]]= true;
path.push(here= next);
} else
here= path.pop();
}
return ({x: x, y: y, horiz: horiz, verti: verti});
}
function display(m) {
var text= [];
for (var j= 0; j<m.x*2+1; j++) {
var line= [];
if (0 == j%2)
for (var k=0; k<m.y*4+1; k++)
if (0 == k%4)
line[k]= '+';
else
if (j>0 && m.verti[j/2-1][Math.floor(k/4)])
line[k]= ' ';
else
line[k]= '-';
else
for (var k=0; k<m.y*4+1; k++)
if (0 == k%4)
if (k>0 && m.horiz[(j-1)/2][k/4-1])
line[k]= ' ';
else
line[k]= '|';
else
line[k]= ' ';
if (0 == j) line[1]= line[2]= line[3]= ' ';
if (m.x*2-1 == j) line[4*m.y]= ' ';
text.push(line.join('')+'\r\n');
}
return text.join('');
}
Code in Java:
public int[][] generateMaze() {
int[][] maze = new int[height][width];
// Initialize
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
maze[i][j] = 1;
Random rand = new Random();
// r for row、c for column
// Generate random r
int r = rand.nextInt(height);
while (r % 2 == 0) {
r = rand.nextInt(height);
}
// Generate random c
int c = rand.nextInt(width);
while (c % 2 == 0) {
c = rand.nextInt(width);
}
// Starting cell
maze[r][c] = 0;
// Allocate the maze with recursive method
recursion(r, c);
return maze;
}
public void recursion(int r, int c) {
// 4 random directions
int[] randDirs = generateRandomDirections();
// Examine each direction
for (int i = 0; i < randDirs.length; i++) {
switch(randDirs[i]){
case 1: // Up
// Whether 2 cells up is out or not
if (r - 2 <= 0)
continue;
if (maze[r - 2][c] != 0) {
maze[r-2][c] = 0;
maze[r-1][c] = 0;
recursion(r - 2, c);
}
break;
case 2: // Right
// Whether 2 cells to the right is out or not
if (c + 2 >= width - 1)
continue;
if (maze[r][c + 2] != 0) {
maze[r][c + 2] = 0;
maze[r][c + 1] = 0;
recursion(r, c + 2);
}
break;
case 3: // Down
// Whether 2 cells down is out or not
if (r + 2 >= height - 1)
continue;
if (maze[r + 2][c] != 0) {
maze[r+2][c] = 0;
maze[r+1][c] = 0;
recursion(r + 2, c);
}
break;
case 4: // Left
// Whether 2 cells to the left is out or not
if (c - 2 <= 0)
continue;
if (maze[r][c - 2] != 0) {
maze[r][c - 2] = 0;
maze[r][c - 1] = 0;
recursion(r, c - 2);
}
break;
}
}
}
/**
* Generate an array with random directions 1-4
* #return Array containing 4 directions in random order
*/
public Integer[] generateRandomDirections() {
ArrayList<Integer> randoms = new ArrayList<Integer>();
for (int i = 0; i < 4; i++)
randoms.add(i + 1);
Collections.shuffle(randoms);
return randoms.toArray(new Integer[4]);
}
Source, demo and some more explanations
I'm coding Conways game of life in P5JS, but I got a wierd bug. It seems to "work" but it looks all wrong. I'm not sure if it has t do with finding the neighbors, because when I call the function manually, it works. I even copied a second neighbor-counting function of the internet in there, and it works, too.
Maybe it's a visual glitch, but I'm not sure of that either, because the code looks fine.
/// <reference path="../TSDef/p5.global-mode.d.ts" />
let gridSize = 10;
let arrCurrent = create2dArray(gridSize);
let arrNext = create2dArray(gridSize);
function setup() {
createCanvas(800, 800, WEBGL);
background(0);
stroke(0, 255, 0);
noFill();
initGame();
}
function draw() {
displayCells();
calcNextGen();
}
//Returns a 2D Array
function create2dArray(size) {
let newArray = new Array(size);
for (let i = 0; i < newArray.length; i++) {
newArray[i] = new Array(1);
}
return newArray;
}
//Fills initial array with random values
function initGame() {
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
arrCurrent[x][y] = Math.round((Math.random()));
}
}
}
//Calculates next generation
function calcNextGen() {
for (let x = 0; x < gridSize; x++) {
for (let y = 0; y < gridSize; y++) {
let neighbors = countNeighbors1(arrCurrent, x, y);
let state = arrCurrent[x][y];
//If cell is dead and has exactly 3 neighbors, it starts living
if (state === 0 && neighbors === 3) {
arrNext[x][y] = 1;
}
//If cell lives and has too few or too many neighbors, it dies
else if (state === 1 && (neighbors < 2 || neighbors > 3)) {
arrNext[x][y] = 0;
}
else {
arrNext[x][y] = state;
}
}
}
arrCurrent = arrNext.slice();
}
//Count neighbors
function countNeighbors(x, y) {
return arrCurrent[(x + 1) % gridSize][y] +
arrCurrent[x][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][y] +
arrCurrent[x][(y + gridSize - 1) % gridSize] +
arrCurrent[(x + 1) % gridSize][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][(y + gridSize - 1) % gridSize] +
arrCurrent[(x + 1) % gridSize][(y + gridSize - 1) % gridSize];
}
function countNeighbors1(grid, x, y) {
let sum = 0;
for (let i = -1; i < 2; i++) {
for (let j = -1; j < 2; j++) {
let col = (x + i + gridSize) % gridSize;
let row = (y + j + gridSize) % gridSize;
sum += grid[col][row];
}
}
sum -= grid[x][y];
return sum;
}
function displayCells() {
background(0);
translate(-300, -300, 0);
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
push();
translate(x * 50, y * 50, 0);
if (arrCurrent[x][y] === 1) box(50);
pop();
}
}
}
function logGrid() {
console.log(arrCurrent[0]);
console.log(arrCurrent[1]);
console.log(arrCurrent[2]);
console.log(arrCurrent[3]);
console.log(arrCurrent[4]);
console.log(arrCurrent[5]);
console.log(arrCurrent[6]);
console.log(arrCurrent[7]);
console.log(arrCurrent[8]);
console.log(arrCurrent[9]);
}
I know I'm very close, but I'm banging my head against this one since 2 hours.
Here's a little P5JS Web Editor, you can copy the code over and visually see the problem.
Any help is appreciated - thank you!
arrCurrent = arrNext.slice(); doesn't create a deep copy of the grid, it just creates a shallow copy of the first dimension.
It creates a grid, where columns of arrCurrent refers to the rows of arrNext.
You've to create a completely new grid:
arrCurrent = []
for (let x = 0; x < gridSize; x++)
arrCurrent.push(arrNext[x].slice());
let gridSize = 10;
let arrCurrent = create2dArray(gridSize);
let arrNext = create2dArray(gridSize);
function setup() {
createCanvas(800, 800, WEBGL);
background(0);
stroke(0, 255, 0);
noFill();
initGame();
frameRate(10)
}
function draw() {
displayCells();
calcNextGen();
}
//Returns a 2D Array
function create2dArray(size) {
let newArray = new Array(size);
for (let i = 0; i < newArray.length; i++) {
newArray[i] = new Array(1);
}
return newArray;
}
//Fills initial array with random values
function initGame() {
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
arrCurrent[x][y] = Math.round((Math.random()));
}
}
}
//Calculates next generation
// - A live cell dies if it has fewer than two live neighbors.
// - A live cell with two or three live neighbors lives on to the next generation.
// - A live cell with more than three live neighbors dies.
// - A dead cell will be brought back to live if it has exactly three live neighbors.
function calcNextGen() {
for (let x = 0; x < gridSize; x++) {
for (let y = 0; y < gridSize; y++) {
let neighbors = countNeighbors1(arrCurrent, x, y);
let state = arrCurrent[x][y];
//If cell is dead and has exactly 3 neighbors, it starts living
if (state === 0 && neighbors === 3) {
arrNext[x][y] = 1;
}
//If cell lives and has too few or too many neighbors, it dies
else if (state === 1 && (neighbors < 2 || neighbors > 3)) {
arrNext[x][y] = 0;
}
else {
arrNext[x][y] = state;
}
}
}
arrCurrent = []
for (let x = 0; x < gridSize; x++)
arrCurrent.push(arrNext[x].slice());
}
function countNeighbors1(grid, x, y) {
let sum = 0;
for (let i = -1; i < 2; i++) {
for (let j = -1; j < 2; j++) {
let col = (x + i + gridSize) % gridSize;
let row = (y + j + gridSize) % gridSize;
sum += grid[col][row];
}
}
sum -= grid[x][y];
return sum;
}
function displayCells() {
background(0);
translate(-75, -75, 0);
stroke(128);
box(50*gridSize, 50*gridSize, 50);
translate(-225, -225, 0);
stroke(0, 255, 0);
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
push();
translate(x * 50, y * 50, 0);
if (arrCurrent[x][y] === 1) box(50);
pop();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
Ok i finally got my minesweeper game made just needing one more thing if anyone could help me with. The part when you click on an unnumbered square and it will reveal some of the board. I have been playing around on this for sometime just drawing a blank. This is a simple minesweeper game just in java script. I have everything labeled where things should go to make it easier to find and i will bold it on where my code ive been working on is.
//Global
//store the value of each square
var gaValue = new Array(8)
for (i = 0; i <= 8; i++)
gaValue[i] = new Array(8)
for (i = 0; i <= 8; i++)
{
//loop through each item in those row
for (j = 0; j <= 8 ; j++)
gaValue[i][j] = 0
}
//Store the status of each square
var gaSquare = new Array(8)
for (j = 0; j <= 8; j++)
gaSquare[j] = new Array(8)
for (j = 0; j <= 8; j++)
{
//loop through each item in those row
for (i = 0; i <= 8 ; i++)
gaSquare[i][j] = "C"
}
//Track of whether the game is over or not (starts this with false)
var gbGameOver = false
function vInit()
{
var strHTML
var i
var j
strHTML = "<table style='margin-left:auto;margin-right:auto'>"
strHTML += "<tr><td colspan='8' style='text-align:center'>MineSweeper</td></tr>"
strHTML += "<tr><td colspan='8' style='text-align:center'><input type='button' id='btnNew' value='New Game' onclick='vNewGame()'></td></tr>"
//Loop through the rows to build the table of tiles
for (i = 0; i < 8; i++)
{
strHTML += "<tr>"
for (j = 0; j < 8; j++)
strHTML += '<td><img src="images/sqt0.png" id="square' + i + ', ' + j + '" onclick="vClick(' + i + ', ' + j + ')"/></td>';
strHTML += "<tr>";
}
strHTML += '<tr><td colspan="8" style="text-align:center"><textarea id="taOut" cols="18" rows="10"></textarea></td></tr>'
strHTML += "</table>"
frmGrid.innerHTML = strHTML
//Place bombs
var iBomb = 0
var iRow = Math.floor(Math.random() * 8)
var iCol = Math.floor(Math.random() * 8)
while (iBomb < 8)
{
while (gaValue[iRow][iCol] == 9)
{
iRow = Math.floor(Math.random() * 8)
iCol = Math.floor(Math.random() * 8)
}
gaValue[iRow][iCol] = 9
iBomb++
}
//Calculate clue values around mines
var iMine = 0
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
for (k = (i - 1) ; k <= (i + 1) ; k++)
for (m = (j - 1) ; m <= (j + 1) ; m++)
if (k >= 0 && k <= 9 && j >= 0 && j <= 9)
if (gaValue[k][m] == 9)
iMine++
if (gaValue[i][j] != 9)
gaValue[i][j] = iMine
iMine = 0
}
}
}
//Get the ID of the image I need to change
function vClick(iRow, iCol)
{
var gaSquare = "square" + iRow + ", " + iCol
var strOut = ""
gaSquare[iRow][iCol] = 'o';
document.getElementById(gaSquare).src = "images/" + gaValue[iRow][iCol] + ".png"
if (gaValue[iRow][iCol] == 9)
{
gbGameOver = true
strOut = "Game Over"
vOver()
}
else if (gaValue[iRow][iCol] == 0)
vZero(iRow, iCol)
document.getElementById('taOut').value = strOut
}
//GameOver
function vOver()
{
var i
var j
var strID;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++) {
strID = "square" + i + ", " + j;
document.getElementById(strID).src = "images/" + gaValue[i][j] + ".png"
}
}
**//Clearing area
function vZero(iRow, iCol, i, j) {
for (i = iRow - 1; i <= iRow + 1; i++) {
for (j = iCol - 1; j <= iCol + 1; j++) {
if (k >= 0 && k <= 8 && j >= 0 && j <= 8)
vClick(i, j)
}**
//Start new game
function vNewGame() {
vInit()
}
//no menu on right click
function bIsRightButtonClicked(e) {
var rightclick = false
e = e || window.event
if (e.which) {
rightclick = (e.which == 3)
}
else if (e.button) {
rightclick = (e.button == 2)
}
return rightclick
}
}
}
I believe the main mistake is that you are referring to k inside vZero() when that variable is not defined. You also appear to be missing a closing curly bracket or two on that function.
Try changing that function to as follows:
//Clearing area
function vZero(iRow, iCol) {
for (var i = iRow - 1; i <= iRow + 1; i++) {
for (var j = iCol - 1; j <= iCol + 1; j++) {
if (i >= 0 && i <= 8 && j >= 0 && j <= 8) vClick(i, j);
}
}
}
You'll note that I changed the i and j parameters to be local variables, because they are only used by that function for the purposes of the for loops. You don't need to have them as parameters. Doing so only confuses other developers because that implies that you need to pass a value for them into the function for it to work, whereas you only need to pass in iRow and iCol. Does that make sense?
I'm building a little grid which needs to be generative to 6x6, but it needs to conform to 4 key constraints. Currently it conforms to 3. I can't work out how to manage the fourth.
001001
101101
010110
110010
001101
So what has to happen here is that:
For each row there can't be more than 2 numbers of the same type consecutively
the numbers are picked randomly
for each column there can't be more than 2 numbers of the same type consecutively
there can be a maximum of 3 of each type of number going by column or row
Here is the current code:
// JavaScript Document
$(document).ready(function () {
var grid = new anyGrid(6, 6);
for (var i = 0; i < grid.length; i++) {
var printIt = "";
for (var j = 0; j < grid[0].length; j++) {
printIt += grid[i][j] + " ";
}
console.log(printIt);
}
});
function anyGrid(rows, cols) {
var grid = [];
for (var i = 0; i < rows; i++) {
grid.push(new Array(cols));
}
var row = 0;
for (var col = 0; col - row < cols; col++) {
for (var r = row; r >= 0 && col - r < cols;) {
setBit(grid, r, col - r--);
}
if (row < rows - 1) row++;
}
return grid;
}
function setBit(grid, row, col) {
var vInd = calcVerticalIndicator(grid, row, col);
var hInd = calcHorizontalIndicator(grid, row, col);
if (isPartiallyRestricted(vInd, hInd)) {
grid[row][col] = flip(vInd);
} else if (isFullyRestricted(vInd, hInd)) {
grid[row][col] = vInd;
grid[row - 1][col] = flip(vInd);
} else {
grid[row][col] = Math.abs(vInd) <= 1
? flip(vInd)
: Math.abs(hInd) <= 1 ? flip(hInd) : anyBit();
}
}
function isPartiallyRestricted(vInd, hInd) {
return vInd == hInd;
}
function isFullyRestricted(vInd, hInd) {
return vInd + hInd == 1;
}
function calcVerticalIndicator(grid, row, col) {
return calcIndicator(grid, row - 1, col, row - 2, col, 2);
}
function calcHorizontalIndicator(grid, row, col) {
return calcIndicator(grid, row, col - 1, row, col - 2, 4);
}
function calcIndicator(grid, row1, col1, row2, col2, unrestricted) {
try {
return grid[row1][col1] * grid[row2][col2] + (grid[row1][col1] - grid[row2][col2]) * unrestricted;
} catch (e) {
return unrestricted;
}
}
function anyBit() {
return getRandomInt(0,1);
}
function flip(bit) {
return bit == 0 ? 1 : 0;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
The problem:
Currently it satisfies 3 of the 4 conditions, but what is required is that there are not more than 3 of each number in any row or column. How do I amend my code to make this possible?
example outputs:
0 1 1 0 1 0
1 1 0 0 1 0
1 0 1 1 0 1 (violation of 4 1s)
0 0 1 0 0 1 (violation of 4 0s)
1 1 0 0 1 0
1 1 0 1 1 0 (violation of 4 1s)
^ ^ ^ ^ ^
Violations
0 0 1 0 1 1
0 1 1 0 1 1 (violation of 4 1s)
1 0 0 1 0 0 (violation of 4 0s)
0 0 1 0 0 1
1 1 0 0 1 1 (violation of 4 1s)
1 1 0 1 1 0 (violation of 4 1s)
^ ^ ^
Violations
This seems to work. Not sure if it is exactly what you are looking for since I removed the horizontal and vertical indicators, but seems to be able to generate a random grid with your constraints. I kept track of the number of times a character/bit appeared in row/col through the usedNumbers object.
I tried to keep a simplistic approach (i.e. an if statement for each of your listed constraints)... I tried to make it configurable for rows/cols in the grid along with "available characters" for the grid values (in case you want to expand). Of course, some solutions won't be possible since you might repeat more than 3 characters if the alphabet is too small and the grid too large (e.g. 0,1 bits for a 7x7). Here is the jsfiddle.
(function () {
//Polyfill for Array.indexOf... Needed for older browsers.
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if ( this === undefined || this === null ) {
throw new TypeError( '"this" is null or not defined' );
}
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for (;fromIndex < length; fromIndex++) {
if (this[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
}
//availableBits to be used to determine the "alphabet" of characters to generate.
var availableBits = [0, 1];
var availLen = availableBits.length;
//usedNumbers to keep track of how many were used in rows/cols.
var usedNumbers;
//How many times to try to generate a grid before failing.
var numTries = 30;
var currentTries = 1;
var success = false;
//Grid config
var grid;
var rows = 6;
var cols = 6;
var vThresh = rows/2;
var hThresh = cols/2;
//Impossible to generate a solution due to constraint 4. Pigeon-hole principle.
if (rows / availLen > vThresh || cols / availLen > hTresh) {
throw new Error("Need to shrink the grid size or add availableBits. There will be more than 3 characters in a row or column.");
}
//Try to generate the grid.
while (!success && currentTries <= numTries) {
try {
grid = new anyGrid(rows, cols);
success = true;
}
catch(e) {
currentTries++;
if (currentTries > numTries) {
console.error(e.message);
console.log("Could not generate anygrid after " + numTries + " attempts.");
}
}
}
if (success) {
console.log("Generated anygrid after " + currentTries + " attempts.");
printGrid(grid);
}
function printGrid (grid) {
for (var i = 0; i < grid.length; i++) {
var printIt = "";
for (var j= 0; j < grid[0].length; j++) {
printIt += grid[i][j] + " ";
}
console.log(printIt);
}
}
function anyGrid (rows, cols) {
var grid = [];
//set used numbers
usedNumbers = {vNumbers: new Array(rows), hNumbers: new Array(cols)};
for (var i = 0; i < rows; i++) {
grid.push(new Array(cols));
}
var row = 0;
for (var col = 0; col - row < cols; col++) {
for (var r = row; r >= 0 && col - r < cols;) {
setBit(grid, r, col - r--);
}
if (row < rows - 1) row++;
}
return grid;
};
//Complies to the 4 constraints:
//No 2 numbers consecutively in a row
//No 2 numbers consecutively in a column
//Use random when able
//No more than 3 of a number in a column or row
function setBit (grid, row, col) {
var needRandom = true;
var bit;
//"Clone" the availableBits to be able to remove characters when calling flip.
var availClone = availableBits.slice();
//Constraint 1: First check previous 2 rows
if ((row - 2) >= 0 && grid[row - 2][col] === grid[row - 1][col]) {
//Needs to flip
bit = flip(grid[row - 1][col], availClone);
needRandom = false;
}
//-------------------------------
//Constraint 2: Check previous 2 cols
else if ((col - 2) >= 0 && grid[row][col - 2] === grid[row][col - 1]) {
//Needs to flip
bit = flip(grid[row][col - 1], availClone);
needRandom = false;
}
//---------------------------------
//Constraint 3: Attempt a random
if (needRandom) {
bit = anyBit(availClone);
}
//-------------------------------
//Constraint 4: Flip if constraint 4 is now violated.
var vNumber = usedNumbers.vNumbers[row] || {};
var hNumber = usedNumbers.hNumbers[col] || {};
while (availClone.length && (vNumber[bit] >= vThresh || hNumber[bit] >= hThresh)) {
bit = flip(bit, availClone);
}
//-------------------------------------
//Set grid value and update vNumber and hNumber to be used in further iterations.
grid[row][col] = bit;
vNumber[bit] = vNumber[bit] + 1 || 1;
hNumber[bit] = hNumber[bit] + 1 || 1;
usedNumbers.vNumbers[row] = vNumber;
usedNumbers.hNumbers[col] = hNumber;
//----------------------------------
};
//Removes a bit from availableBits array and looks to return another random bit.
function flip (bit, availableBits) {
var index = availableBits.indexOf(bit);
//Remove bit from the available bits.
availableBits.splice(index, 1);
var length = availableBits.length;
if (length > 0) {
//Try another random character
return anyBit(availableBits);
}
//We cannot construct the grid, no more availble characters/bits
else {
throw new Error("Could not generate any grid for current configurations. Try again or expand the available characters.");
}
};
//Returns a random bit from the availableBits array.
function anyBit (availableBits) {
var length = availableBits.length;
var randomIndex = getRandomInt(0, length - 1);
return availableBits[randomIndex];
};
//Returns a random integer between min and max.
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
}());
Edit: Use vThresh and hThresh and set them to rows/2 and cols/2 respectively. Code and fiddle updated.