How to fill a one-dimensional array grid with various blocks - javascript

I have a grid stored in a one-dimensional array. The grid can be of variable size. I now have different blocks (2x2,2x3,2x4,3x3,3x4,3x5, 4x4 and so forth).
How do I fill the grid in now? It can have unused spaces left in the end but what is the fastest way here without looping through the array over and over trying out sizes?
For instance with this 5x5 grid.
var g1 = [
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
]

You could use a backtracking algorithm to solve this type of problem:
function process(var blockstate) {
if(gridIsFullOfBlocks(blockstate))
return true;
if(canPlaceBlock(blockstate)) {
blockstate = placeLargestBlock(blockstate);
return process(blockstate);
}
return false;
}
function fillBlocks(int size) {
var blockstate = new Array(size);
if(process(blockstate))
displayBlockstate(blockstate);
else
displayError(blockstate);
}

Related

Javascript Multidimensional Array complete column getting set

I am trying to make a game in Javascript. The game board is intialized to a zero-filled 2-D array. However, when I am setting the value of a single point, the complete column is getting set with that value. I think this is some issue with the way I am initializing the Array.
Method 1
# initialization
gameState = Array(6).fill(Array(7).fill(0))
# later in the game
gameState[2][4] = 1
# results in complete 4th index column to be assigned the value 1, like so -
0: (7) [0, 0, 0, 0, 1, 0, 0]
1: (7) [0, 0, 0, 0, 1, 0, 0]
2: (7) [0, 0, 0, 0, 1, 0, 0]
3: (7) [0, 0, 0, 0, 1, 0, 0]
4: (7) [0, 0, 0, 0, 1, 0, 0]
5: (7) [0, 0, 0, 0, 1, 0, 0]
Method 2
# initialization
let gameState = [];
for (let i=0; i<MAX_ROWS; i++) {
let row = []
for (let j=0; j<MAX_COLUMNS; j++) {
row.push(0)
}
gameState.push(row);
}
# again similar assignment
gameState[2][4] = 1
# results in correct state of the array
0: (7) [0, 0, 0, 0, 0, 0, 0]
1: (7) [0, 0, 0, 0, 0, 0, 0]
2: (7) [0, 0, 0, 0, 1, 0, 0]
3: (7) [0, 0, 0, 0, 0, 0, 0]
4: (7) [0, 0, 0, 0, 0, 0, 0]
5: (7) [0, 0, 0, 0, 0, 0, 0]
Can someone please explain what I am doing wrong here?
Your problem is pretty simple.
Array(6).fill(Array(7).fill(0))
Let's explain what this does.
Array(6)
creates a holey array with space for 6 items.
.fill(...)
will fill up these 6 holes with what ever you put in as argument.
Now comes the issue..
In Javascript, the arguments are evaluated before the execution of the function is run.
This means (in this exact case where .fill(...) is only ran once) your code is exactly the same as:
const innerArray = [0,0,0,0,0,0,0];
gameState = Array(6).fill(innerArray);
This means it fills the outer array with exactly the same array instance 6 times.
What you want is to create separate arrays each time. Just do this instead:
gameState = [...Array(6)].map(() => [...Array(7)].map(() => 0))

2D array replacing values according conditions

I got a 10 x 10 array with the following values:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
In this 2D array, I have to randomly choose six values of 0, either horizontally or vertically (also random) and replace them with the value 6.
I've done this with the following code:
function tekenGrootstSchip(bord) {
rand = Math.floor((Math.random() * 10));
rand2 = Math.floor((Math.random() * 10));
directie = Math.floor((Math.random() * 2));
counter = 0;
if (directie == 0) {
for(y = rand2; y < 10; y++) {
if(counter < 6) {
bord[rand][y] = 6;
counter++;
}
}
for(y = rand2; y > 0; y--) {
if(counter < 6) {
bord[rand][y] = 6;
counter++;
}
}
} else {
for(x = rand; x < 10; x++) {
if(counter < 6) {
bord[x][rand2] = 6;
counter++;
}
}
for(x = rand; x > 0; x--) {
if(counter < 6) {
bord[x][rand2] = 6;
counter++;
}
}
}
}
After doing this for the value 6, I also have to do this for the value 4. The rules for value 4 are a bit different however. You can't place a 4 on a 6, neither can you place a 4 next to a 6. And the value 4 only takes four places (so 4x1, while a 6 is 6x1)
So if my randomly generated direction is horizontal, my bord[x-1][y], bord[x][y] and bord[x+1][y], with x and y initialized at the random value, with y going up to y+1, y+2, y+3, y+4 (4x1). All these values have to be checked against == 0, if true, replacing the zeroes with fours can be initialized otherwise not. If so, I have to generate a new [x][y] and check these conditions again until I can change four zeroes in my 2D array succesfully.
Although I have a general idea of implementing this, I would have a bug that, if for instance one null-value would be replaced with a four, but the one next to it can't be replaced since it's next to a != 0 value, I would be stuck with a "illegal" four.
If anyone could help me out in the right direction I would appreciate it.
feasibility
We can (trivially) show that whatever the 6 configuration chosen, we can always put the 4s.
Indeed, the maximal cover area of a 6 is 9 (a 3x3 square)
000
060
000
(we can't put a 4 in the border surrounding the 6).
So a bound for the maximal 6 configuration covered area is 6*9=54
000000xxxx
060060xxxx
000000xxxx
000000xxxx
060060xxxx
000000xxxx
000000xxxx
060060xxxx
000000xxxx
xxxxxxxxxx
and we can put as many 4 in the squares containing 'x' (which is way more than 4)
6-generation
Let's assume the board is indiced as follow:
0 1 2 3 ... 9
10 11 12 ... 19
...
90... 99
let x towards the bottom and y to the right
A square holding value id can be found at (x,y)=((id - id%10)/10, id%10) and reciprocally
id:(x,y)->x*10+y
So we will only consider numbers between 0 and 99 (since we can find back their associated position(x,y) in the grid)
I will use the copy-pasted below getRandomInt taken shamelessly from mdn
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
Finally we can draw between 0 and 99. If number already exists then discard it, otherwise take it
let indices = new Set
while(indices.size < 6){
let idx = getRandomInt(0,99)
if(!indices.has(idx)){
indices.add(idx)
}
}
return [...indices]
4-generation
We can apply the same stragy as before: draw a random number between 0 and 99 and discard it as long as it is not valid, until 4 valid positions are found
build the invalid set due to the 6 positionned
let invalids = new Set
indices.forEach(idx=>{
let [x,y] = [(idx - idx%10)/10, idx%10]
//add the adjacent squares
for(let i = -1; i<=1; ++i){
for(let j = -1; j<= 1; ++j){
if( 0<=x+i<10 && 0 <= y+j < 10){//if the cell in the board....
invalids.add( (x+i)*10 + y+j )
}
}
}
})
draw except from the invalid set
let out = []
while(out.length < 4){
let idx = getRandomInt(0,99)
if(!invalids.has(idx)){
invalids.add(idx)
out.push(idx)
}
}
return out
That approach may be not that efficient in worst case: we would have about 54% probability of drawing an invalid number!
We can thus consider an array of only the valid numbers, and draw from it
let valids = Array(100).fill(0).reduce((oks, x,i)=>{
if(invalids.has(i)) return oks
return oks.push(i),oks
},[])
//take numbers from that array
let out = []
for(let i = 0; i<4; ++i){
let idx = getRandomInt(0,valids.length)
//notice that here we take the elem from valids
//not just the idx from getRandomInt
out.push(valids[idx])
//and we takeout the element from valids
valids.splice(idx, 1);
}
return out

Creating rotation matrix

I need to create a rotation function that will be used to rotate items around, it nearly works apart from when trying to do -sin.
There doesnt seem to be a function that allows this.
Matrix.createRotation = function (rotation) {
return new Matrix(Math.cos(rotation), Math.sin(rotation), 0,
Math.sin(rotation), Math.cos(rotation), 0, 0, 0, 1);
};
You have to negate the result of Math.sin(rotation) as -Math.sin(rotation):
Matrix.createRotation = function (rotation)
{
return new Matrix(
Math.cos(rotation), -Math.sin(rotation), 0,
Math.sin(rotation), Math.cos(rotation), 0,
0, 0, 1
);
};
Note that -Math.sin(rotation) is faster than (-1)*Math.sin(rotation).

Get index on click with jquery

I am trying to develop a 2 player checkers game, for my college, but I am stuck at getting the index of the 2D array when I click on the piece.
I divided my HTML code in:
table - the game board
row - each row is the height of the array
cell - each cell is a piece and the width of the array
Then I setted a default array to start the game:
var board = [
[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2],
[2, 0, 2, 0, 2, 0, 2, 0],
];
Where the:
- 0 - empty cell
- 1 - Player 1 pieces
- 2 - Player 2 pieces
To get the position I am using this code
function getPosition() {
$('.row').on('click', function() {
console.log( $('.row').index(this) );
});
$('.cell').on('click', function() {
console.log( $('.cell').index(this) );
});
}
Get the height array position which should be between 0 and 7 are ok, but the cell from the row should be between 0 and 7 too, but using this I am getting from 0 to 63, using this parameters I have no idea how to start the next comparisons of the game.
Here is the code from codepen
http://codepen.io/michaelthompson/pen/jVdrav
In each instance you can simply use $(this).index() which will return the index within the element's siblings
But since clicking on a row always means clicking a cell you could combine them and do
$('.cell').on('click', function() {
var $cell = $(this),
columnIndex = $cell.index(),
rowIndex = $cell.parent().index();
});
What $('.cell').index(this) is doing is taking the whole collection of the class within the page and that's why you are getting 0-63

How to place values in a multidim array - like 6 queens on a chessboard - javascript

I have an array that looks like this
board = [
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0]
];
Is there any way, just by using 2 for loops, like this
for(var i = 0; i < 7; i++){
for(var j = 0; j < 7; j++){
//actions here
}
}
to place 6 values of 1, first value of 1 to replace the first 0 in the array and the next 5 to be placed using the same rules for placing a queen on a chessboard. The result should look like this
board = [
[1, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 1, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 1 ,0, 0, 0],
[0, 1, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 1, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 1, 0],
[0, 0, 0, 0, 0 ,0, 0, 0],
[0, 0, 0, 0, 0 ,0, 0, 0]
];
I'm new to javascript and I have no clue on how to start this. Could someone please give me some hints.
board[xCord][yCord] = ValueToInsert;
I may have misread your question, so here is some more info.
You can "push" another array into a 1d array.
myArr.push([]);
Then you push the value like this:
myArr[Xcord].push(YValue);
You need to make a function to check whether placing a queen on xy coordinates is valid. The function would check whether there is a queen in that row, column or diagonals. You can also make those three sub-functions of the main function.
Then, as you go through the loop, use that function for each field and if it returns true, place the queen. If false, move on.
Of course, there will be many solutions in the end, so you might want to keep/store all of them in an array.
Also, think of ways to optimize the algorithm: for example, if you set a queen in a row, there's no need to check any other fields in that row, etc.
And when you decide to really go advanced with your skills, try using recursive functions to generate the solutions.

Categories

Resources