shuffling cards with Javascript and inconsistent array values? - javascript

I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.

The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}

while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.

Related

Selecting elements with regular javascript within a set of coordinate in vanilla javascript?

Get element at specified position - JavaScript
This question only answers for a particular x and y point, but I would like to select all elements within a rectangle defined by 4 x and y points and be then able to only select elements with a certain classname, the classname could be "selectable". Is there a way to do this in regular javascript? What's the most efficient way to do this?
I was thinking about calling document.elementsFromPoint(x, y) repeatedly, but because the x and y arguments are not integers, I would have to call it an infinite number of times.
I think i made an answer. Though I haven't tested it much.
First a description of the program. The program recursively goes through the dom starting from the body element and tests to see if it is entirely inside the query rectangle. My code assumes that elements wont be found outside of there parents, but if you don't want to assume this delete the marked lines.
// first some helper functions
function isRecPartialyInside(inside, outside){
if((between(inside.xmin, outside.xmin,outside.xmax) || between(inside.xmax, outside.xmin,outside.xmax ) && (between(inside.ymin, outside.ymin,outside.ymax) || between(inside.ymax, outside.ymin,outside.ymax )))){
return true;
}
return isRecFullyInside(outside, inside);
}
function between(x, min, max){
return x > min && x < max;
}
function clientRecToRec(clientRec){
return {xmin: clientRec.x, xmax:clientRec.right, ymin: clientRec.y, ymax: clientRec.bottom};
}
function isRecFullyInside(possiblyInside, possiblyOutside){
return (possiblyInside.xmin > possiblyOutside.xmin && possiblyInside.xmax < possiblyOutside.xmax && possiblyInside.ymin > possiblyOutside.ymin && possiblyInside.ymax < possiblyOutside.ymax);
}
function isClientRecFullyInside(clientRec, rec){
let crec = clientRecToRec(clientRec);
return isRecFullyInside(crec, rec);
}
function isClientRecPartiallyInside(clientRec,rec) {
let crec = clientRecToRec(clientRec);
return isRecPartialyInside(crec, rec);
}
// here is the real deal
function elementsFromBox(x1,x2,y1,y2){
let output = [];
let boundsRec = {xmin:x1,xmax:x2,ymin:y1,ymax:y2};
let curr = document.body;
let stack = [];
let frame = {index:0,list:[curr]};
stack.push(frame);
while(stack.length > 0){
let currFrame = stack.at(-1);
if(currFrame.index >= currFrame.list.length){
stack.pop();
continue;
}
let currEl = currFrame.list[currFrame.index];
currFrame.index +=1;
// Check if the element is fully inside. If so report
if(isClientRecFullyInside(currEl.getBoundingClientRect(), boundsRec)){
output.push(currEl);
}
if(isClientRecPartiallyInside(currEl.getBoundingClientRect(), boundsRec)){ // del
stack.push({index:0,list:currEl.children});
continue;
} // del
}
return output;
}

2D visibility algorithm not works well for overlapped case

I find the visibility algorithm in one great website
Since it is quiet long ago article, I am not sure that the author will give the answer.
As you can see, the interactive playground(try it yourself) does not work well in right upper side, especially overlapping edges.
// Pseudocode of main algorithm
var endpoints; # list of endpoints, sorted by angle
var open = []; # list of walls the sweep line intersects
loop over endpoints:
remember which wall is nearest
add any walls that BEGIN at this endpoint to 'walls'
remove any walls that END at this endpoint from 'walls'
figure out which wall is now nearest
if the nearest wall changed:
fill the current triangle and begin a new one
Here is also part of working JavaScript code
// Main algorithm with working Javascript code
export const calculateVisibility = (origin, endpoints) => {
let openSegments = [];
let output = [];
let beginAngle = 0;
endpoints.sort(endpointCompare);
for(let pass = 0; pass < 2; pass += 1) {
for (let i = 0; i < endpoints.length; i += 1) {
let endpoint = endpoints[i];
let openSegment = openSegments[0];
if (endpoint.beginsSegment) {
let index = 0
let segment = openSegments[index];
while (segment && segmentInFrontOf(endpoint.segment, segment, origin)) {
index += 1;
segment = openSegments[index]
}
if (!segment) {
openSegments.push(endpoint.segment);
} else {
openSegments.splice(index, 0, endpoint.segment);
}
} else {
let index = openSegments.indexOf(endpoint.segment)
if (index > -1) openSegments.splice(index, 1);
}
if (openSegment !== openSegments[0]) {
if (pass === 1) {
let trianglePoints = getTrianglePoints(origin, beginAngle, endpoint.angle, openSegment);
output.push(trianglePoints);
}
beginAngle = endpoint.angle;
}
}
}
return output;
};
In my opinion, it happens since the algorithm thinks right edge is shown whole part when it is the closest edge although it can be hidden from whole edges.
I think I can modify the algorithm to use the current closest edge and newer current closest edge but not sure...
How should I fix it?

this.functionName is not a function error

I am writing a simulation for bunny survival in a meadow and have to detect the minimal plant growth rate for the bunny to survive. I decided to go with OOP. Hence, tried to design my "classes" in js. I haven't done much OOP in JS, so I am stuck. I don't understand why I keep getting "this.checkElementExists" is not a function.
I tried to follow OOP that was shown in Mozilla MDN for JS and here I am stuck. I updated to ES6 classes.
class Meadow{
constructor(){
this.grid = this.makeGrid();
//console.log(this.grid);
}
makeGrid(){
let grid = new Array(30);
for(var i=0; i < 30; i++){
grid[i] = new Array(30).fill(null);
}
return grid;
}
checkElementExists(coordinates){
if(this.grid[coordinates[0]][coordinates[1]] != null){
return true;
}else{
return false;
}
}
growAPlant(timeRate){
if(timeRate == null){
clearInterval(this.growAPlant);
}
let plant = new Plant();
let coord = plant.generateCoordinateInMeadow();
//console.log(coord);
// add a plant to the 2d array, but check if the that spot is free
// otherwise use the generateCOordinate in the meadow function
//console.log(this.grid[coord[0]][coord[1]]);
//var that = this;
var ans = checkElementExists(coord).bind(this);
console.log(ans);
while(!checkElementExists(coord)){
coord = plant.generateCoordinateInMeadow();
}
//console.log(coord);
//console.log(this.grid[coord[0]] == undefined);
this.grid[coord[0]][coord[1]] = plant;
//console.log(this.grid);
}
}
class Simulation{
constructor(){
this.passRateArray = []; // this array will be used to plot the data
this.failureRateArray = []; // array that will hold failure growth rates
this.timeToEnergyData = []; // an example would be [{0: 1000, 1: 999, 2: 998, ....., 10000: 0}]
this.rateToEnergyTimeData = {};
this.timeCounter = 100; // 10000
this.growthTimeRate = 1000; // 1 second
this.gap = 0.05;
this.meadow = new Meadow();
this.bunny = new Bunny();
}
timeToEnergyDataPopulator(currTime, energy){
var relation = {currTime : energy};
this.timeToEnergyData.push(relation);
}
simulation(){
// HERE I MAKE A CALL TO MEADOW.GROWAPLANT
setInterval(this.meadow.growAPlant.bind(this.meadow), this.growthTimeRate);
//meadow.growAPlant(this.growthTimeRate);
let bunnyMove = this.bunny.move();
// not enough energy, bunny did not survive
if(bunnyMove == false){
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy());
let rate = this.growthTimeRate / 1000;
this.rateToEnergyTimeData = {rate : this.timeToEnergyData};
// add the failure rate to the failureRateArray
this.failureRateArray.push(this.growthTimeRate);
// increase the rate of plant growth
if(this.passRateArray.length < 1){
this.growthTimeRate = this.growthTimeRate + this.growthTimeRate * 0.5;
}else{
let lastSurvivalRate = this.passRateArray[this.passRateArray.length - 1];
this.growthTimeRate = lastSurvivalRate - ((lastSurvivalRate - this.growthTimeRate)*0.5);
}
// stop the meadow from growing a plant
meadow.growAPlant(null);
// stop the simulation
clearInterval(this.simulation);
}
while(!this.meadow.checkValidBunnyMove(bunnyMove).bind(this.meadow)){
bunnyMove = bunny.move();
}
if(meadow.checkIfBunnyEats(bunnyMove)){
// since bunny made still a move, -1 energy
bunny.decreaseEnergyByOne();
// and since the meadow at that coordinate had food, we add +10 to energy via eatPlant method
bunny.eatPlant();
// track the time to energy data
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy);
}else{
// no food, -1 energy
bunny.decreaseEnergyByOne();
// track the time to energy data
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy);
}
// decrement the timeCounter
this.timeCounter -= 1;
if(this.timeCounter <= 0){
this.timeToEnergyDataPopulator(this.timeCounter, bunny.getBunnyEnergy());
let rate = this.growthTimeRate / 1000;
this.rateToEnergyTimeData = {rate : this.timeToEnergyData};
this.passRateArray.push(this.growthTimeRate);
// bunny survived, adjust the growth rate
if(this.failureRateArray.length < 1){
this.growthTimeRate = this.growthTimeRate - (this.growthTimeRate * 0.5);
}else{
let lastFailureRate = this.failureRateArray[this.failureRateArray.length - 1];
this.growthTimeRate = this.growthTimeRate - ((this.growthTimeRate - lastFailureRate) * 0.5);
}
clearInterval(this.simulation);
}
}
runner(){
while(this.passRateArray[this.passRateArray.length - 1] - this.failureRateArray[this.failureRateArray.length - 1] > this.gap || this.passRateArray.length == 0 || this.failureRateArray.length == 0){
setInterval(this.simulation(), 1000);
}
console.log("The minimum plant regeneration rate required to sustain the bunny for 10000 units of time is " +
this.growthTimeRate + " regenerations/unit time");
}
}
Errors that I get:
1) simulation.js:62 Uncaught TypeError: this.meadow.checkValidBunnyMove is not a function
at Simulation.simulation (simulation.js:62)
at Simulation.runner (simulation.js:101)
at (index):24
2) meadow.js:1 Uncaught SyntaxError: Identifier 'Meadow' has already been declared
at VM16689 meadow.js:1
3) VM16689 meadow.js:37 Uncaught ReferenceError: checkElementExists is not defined
at Meadow.growAPlant (VM16689 meadow.js:37)
My question is why the number 1 and 3 errors persist?
clearInterval(this.growAPlant);
This clearInterval isn’t correct, because you need pass the return value of setInterval to it, not a function. It does helpfully imply that you have a setInterval(someMeadow.growAPlant, …) somewhere, though, and that’s where the issue is. When you reference a function without calling it – like when you pass it to setInterval – the object it belonged to doesn’t come with it. Then, when the timer fires, it calls the function without a this value.
In JavaScript, the value of this inside a non-arrow function is determined entirely by how the function is called, not where it’s declared. You can read about how this works in various other questions and pieces of documentation. Fixing the problem involves giving growAPlant the correct this somehow, either by:
placing a reference to it in a containing scope (i.e. moving your var that = this out one level and using that instead of this throughout)
wrapping the function in one that’ll preserve the correct value, as in
setInterval(someMeadow.growAPlant.bind(someMeadow), …);
(Function.prototype.bind) or
setInterval(function () {
someMeadow.growAPlant();
}, …);
(the someMeadow.growAPlant reference is now part of a call, so someMeadow becomes the this value for the call)
changing it into an arrow function, which doesn’t have its own this and uses the one from the containing scope
Only (2) will work when you convert to the simplest form of an ES6 class, so it’s the approach I recommend.
Explanation
First, this always refers to the first parent function. In your case it is:
this.growAPlant = function(timeRate){
//content
var that = this; // this is growAPlant
}
And
this.checkElementExists = function(coordinates){ }
Is accessible with Meadow object. However, your var that is referring to this.growAPlant = function(timeRate) not Meadow.
Solution
Create that in the beginning
function Meadow(){
var that = this;
that.growAPlant = function(timeRate){
}
that.checkElementExists = function(coordinates){
if(this.grid[coordinates[0]][coordinates[1]] != null){
return true;
}else{
return false;
}
};
return that;
}

How could I modify the array elements recursively with backtracking?

I wrote a kind of N-Queens algorithm, handling only the vertical and horizontal threats detection. Thus, it's rather a N-Towers solutions finder.
To do this, I use recursion. It's a well-known algorithm. For each square of the chessboard, I place a tower. For each placed tower, I try to place another tower (this is the recursive call). If there is not any remaining tower to place, it means the program has found a solution and the recursive level has to return. If all the chessboard has been crossed with remaining tower(s) to place, it means the program didn't find a solution and the recursive level has to return.
My recursive function has two parameters : the number of towers which have to be placed and the chessboard (an array of array of string ; the string equals "T" to indicate a tower has been placed in this chessboard's square and "-" to indicate the square is empty).
The problem
My algorithm seems to find all the solutions and displays them as chessboards, using the "-" (and, if it worked well, "T") notation. This notation is explained above.
However, even if the number of solutions seems to be correct, the displayed solutions/chessboards contain only "-".
I think I don't pass my array of array (ie. : the chessboard) correctly in my recursive call.
Illustration of this problem
For 2 towers and a chessboard of 2*2 squares, two solutions are found and it's normal. But there are only "-" and no "T" appears... That's the problem. Indeed :
--
--
Code : focus on my recursive function
/**
* RECURSIVE FUNCTION. If there are still towers to place, this function tries to place them. If not, it means a
* solution has been found : it's stored in an array (external to this function).
* If this function can't place a tower, nothing happens.
* Else, it places it and makes the recursive call.
* Each recursion level does this for each next (to the placed tower) chessboard's squares.
* #param number_of_left_towers how many remaining towers to place there are (if 0, it means a solution has been
* found)
* #param array_array_chessboard the chessboard
* #returns {Number} the return is not important
*/
function placeTower(number_of_left_towers, array_array_chessboard) {
if (number_of_left_towers == 0) {
return solutions.push(array_array_chessboard);
}
for (var current_x = 0; current_x < number_of_lines; current_x++) {
for (var current_y = 0; current_y < number_of_columns; current_y++) {
if (array_array_chessboard[current_x][current_y] == "-" && canBePlaced(array_array_chessboard, current_x, current_y)) {
array_array_chessboard[current_x][current_y] = "T";
placeTower(number_of_left_towers - 1, array_array_chessboard);
array_array_chessboard[current_x][current_y] = "-";
}
}
}
}
Code : JSFiddle with all the source
https://jsfiddle.net/btcj6uzp/
You can also find the same code below :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Recursive algorithm of the N-Towers</title>
</head>
<body>
<script type="text/javascript">
/**
* Finds all the solutions to the N-Towers algorithm.
*
* #param number_of_towers number of towers to try to place in the chessboard
* #param number_of_lines chessboard's ones
* #param number_of_columns chessboard's ones
* #returns {nTowersSolutions} array containing all the solutions
*/
function nTowersSolutions(number_of_towers, number_of_lines, number_of_columns) {
/*
NB
"T" = "Tower" = presence of a tower in this square of the chessboard
"-" = "nothing" = no tower in this square of the chessboard
(used for both solutions displaying and finding)
*/
var solutions = [];
var array_array_chessboard = []; // Represents the chessboard
for(var i = 0; i < number_of_lines; i++) {
array_array_chessboard[i] = new Array(number_of_columns);
for(var j = 0; j < number_of_columns; j++) {
array_array_chessboard[i][j] = "-"; // We initialize the chessboard with "-"
}
}
/**
* Uses HTML to display the found solutions, in the Web page
*/
this.displaySolutions = function() {
var body = document.body;
solutions.forEach((array_array_chessboard) => {
array_array_chessboard.forEach(function(array_chessboard) {
array_chessboard.forEach((square) => {
body.innerHTML += square; // New cell
});
body.innerHTML += "<br />"; // New line
});
body.innerHTML += "<br /><br />"; // New solution
});
};
/**
* RECURSIVE FUNCTION. If there are still towers to place, this function tries to place them. If not, it means a
* solution has been found : it's stored in an array (external to this function).
* If this function can't place a tower, nothing happens.
* Else, it places it and makes the recursive call.
* Each recursion level does this for each next (to the placed tower) chessboard's squares.
* #param number_of_left_towers how many remaining towers to place there are (if 0, it means a solution has been
* found)
* #param array_array_chessboard the chessboard
* #returns {Number} the return is not important
*/
function placeTower(number_of_left_towers, array_array_chessboard) {
if (number_of_left_towers == 0) {
return solutions.push(array_array_chessboard);
}
for (var current_x = 0; current_x < number_of_lines; current_x++) {
for (var current_y = 0; current_y < number_of_columns; current_y++) {
if (array_array_chessboard[current_x][current_y] == "-" && canBePlaced(array_array_chessboard, current_x, current_y)) {
array_array_chessboard[current_x][current_y] = "T";
placeTower(number_of_left_towers - 1, array_array_chessboard);
array_array_chessboard[current_x][current_y] = "-";
}
}
}
}
/**
* Can this tower be placed ?
* #param array_array_chessboard
* #param new_x
* #param new_y
* #returns {boolean}
*/
function canBePlaced(array_array_chessboard, new_x, new_y) {
for(var i = 0; i < array_array_chessboard.length; i++) {
for(var z = 0; z < array_array_chessboard[i].length; z++) {
if(array_array_chessboard[i][z] == "T"
&& (
new_x == z || new_y == i // Horizontal and vertical checks
)
) {
return false;
}
}
}
return true;
}
placeTower(number_of_towers, array_array_chessboard);
return this;
}
// <!-- CHANGE THESE PARAMETERS' VALUE TO TEST -->
nTowersSolutions(2, 2, 2).displaySolutions();
</script>
</body>
</html>
Your problem is most likely that there is only one (two dimentional) array, which is global, so in the end your solutions are all pointing to the same array which will be the last state it had before our recursive function completely returned.
array_array_chessboard[current_x][current_y] = "T";
placeTower(number_of_left_towers - 1, array_array_chessboard);
array_array_chessboard[current_x][current_y] = "-";
If I understand the above correctly, you are (looping over all positions, ish)
1) assigning T to a location
2) solving all boards with T in that location
3) assigning "-" to the previous location
so in the end you have an array full of "-", and all solutions point to this same array
Try replacing
return solutions.push(array_array_chessboard);
by
return solutions.push(JSON.decode(JSON.encode(array_array_chessboard)));
The above will make a deep copy of your solution, and while it might not be the utmost efficient way to make a deep copy it is a simple one. If your algorithm needs to be really fast you might want to look in a faster way to clone your solution.
Though I can't guarantee that this will work since I can't run your fiddle
(also for readability I suggest you write your return like so:)
solutions.push(JSON.parse(JSON.stringify(array_array_chessboard)));
return;
EDIT: why to use JSON.parse+stringify over Array::from:
if you simply do
solutions.push(Array.from(array_array_chessboard));
The second dimention will still reference the same arrays, and that is where your string data is stored after all.
to demonstrate (note that you need to polyfill the Array.from in IE, or simply try on a different browser):
var arr1 = ["a"];
var arr2 = ["b"];
var metaArr = [arr1, arr2];
console.log(metaArr[0][0], metaArr[1][0]); // "a b"
var metaArrClone = Array.from(metaArr);
var metaArrClone[0][0] = "c";
console.log(metaArrClone[0][0]); // "c"
console.log(metaArr[0][0]); // "c"
var metaArrClone2 = JSON.parse(JSON.stringify(metaArr));
console.log(metaArrClone2[0][0]); // "c"
metaArrClone2[0][0] = "d";
console.log(metaArrClone2[0][0]); // "d"
console.log(metaArr[0][0]); // "c"
You do not need to keep the solutions outside your recursive function. Could be better if you keep the solutions inside your recursive function and than return all the solutions, so you do not need to worry about the state outside the function.
Of course if you have to use the finded solutions before that the recursive function returns (maybe you have a big cheesboard) your solution could be better. Or you could use a generator.
Could be also good keep this kind of logic separate from the ui so first focus on the solution and then try to draw the result in the browser or where you want, or do the opposite.
You can start from the code below, but please check if it actually finds all the solutions before use it.
'use strict'
/* Finds all the solutions to the N-Towers algorithm.
*
* #param number_of_towers number of towers to try to place in the chessboard
* #param number_of_lines chessboard's ones
* #param number_of_columns chessboard's ones
* #returns {nTowersSolutions} array containing all the solutions
* "Tower" = presence of a tower in this square of the chessboard
* "Nothing" = no tower in this square of the chessboard
* "Blocked" = the cell is blocked
*/
function nTowersSolutions(number_of_towers, number_of_lines, number_of_columns) {
var chessboard = _initChessboard(number_of_lines, number_of_columns);
var solutions = _findAllSolution(chessboard, number_of_towers);
return solutions;
}
// nuber, * -> array
var _newArrFromLenAndElement = function(length, element) {
return Array.apply(null, Array(length)).map(function(){ return element; });
};
// number, number -> cheesboard
var _initChessboard = function(number_of_lines, number_of_columns) {
var oneColumnChessboard = _newArrFromLenAndElement(number_of_lines, []);
var chessboard = oneColumnChessboard.map(function() {
var line = _newArrFromLenAndElement(number_of_columns, 'Nothing');
return line;
});
return chessboard;
};
// chessboard, line_index, column_index -> chessboard
var _placeTower = function(chessboard, line_index, column_index) {
var ff = chessboard.map(function(line, index) {
if (index === line_index) {
return line.map(function() { return 'Blocked'; });
}
else {
return line.map(function(x, index){
if(index===column_index){return'Blocked';}
else{return x;}
});
}
});
ff[line_index][column_index] = 'Tower';
return ff;
};
// array[][] -> array[]
var _flatten = function(arr) {
return [].concat.apply([], arr);
};
// *, array -> boolean
var _isInArray = function(value, array) {
return array.indexOf(value) > -1;
};
// cheesboard, numberm number -> array
// this could be a bruteforce recursive solution at your problem ( actually you have to check if
// it is correct )
// we need _lines_done for don't look for solutions already finded (if you have tried all the
// pattern with a tower in the first line you don't want try to place a tower in the first line)
var _findAllSolution = function(chessboard, number_of_towers, _lines_done, _deep) {
_lines_done = _lines_done || [];
_deep = _deep || 0;
if (number_of_towers === 0){
return chessboard;
}
//for all the cells in the ceesboard try to place a tower
var solutions = chessboard.map(function(line, line_index) {
var solution = line.map(function(cell, column_index) {
if (_isInArray(line_index, _lines_done)) {
return 'alreadyVisitedLine';
}
else if (cell === 'Nothing') {
var fulfilledChessboard = _placeTower(chessboard, line_index, column_index);
if (line_index > 0 && _deep === 0 && !(_isInArray(line_index - 1, _lines_done))) {
_lines_done.push(line_index - 1);
}
return _findAllSolution(fulfilledChessboard, number_of_towers -1, _lines_done, _deep + 1);
}
else {
return 'DeadEnd!';
}
});
return _flatten(solution);
});
var flatSolutions = _flatten(solutions);
//you should .filter the solutions
return _flatten(solutions);
};
var h = nTowersSolutions(2,2,2)
console.log(h)

Optimize looping through 2 arrays javascript canvas game

I'm working on my first javascript canvas game, and I wonder is there a better way for comparing collisons between objects in 2 arrays. For example i have an array with rockets, and array with enemies, the code is working, but i think when arrays length becomes much larger it will have effect on the performance. Example 100 rockets through 100 enemies is 10000 iterations per frame
for (i in rockets){
rockets[i].x+=projectile_speed;
for (j in enemies){
if(collision(rockets[i], enemies[j])){
enemies[j].health-=5;
sound_hit[hit_counter-1].play();
hit_counter--;
if (hit_counter==0){
hit_counter=5;
}
rockets.splice(i,1);
if (enemies[j].health <= 0) {
score += enemies[j].score;
sound_explode[Math.floor(Math.random()*25)].play();
enemies[j].isDead = true;
}
} else if(rockets[i].x >= width){
rockets.splice(i,1);
}
}
}
If you want to test every rocket on every player its not really possible to do differently, without knowing more about position of players and rockets.
If you keep the collision function fast, this should though be no problem at all.
I can only think of two easy improvements on this:
when a collision is found use continue since looping over the rest of players should not be necessary (unless players is allowed to collide)
instead of splice'ing the rockets array multiple times, build a new one, excluding all "dead" rockets.
You should also consider using forEach, map and filter to make the code a bit easier to read:
rockets = rockets.filter(function(rocket) {
rocket.x+=projectile_speed;
if(rocket.x >= width) {
return false;
}
var enemy = enemies.find(function(enemy) { return collision(rocket, enemy) });
if(enemy) {
enemy.health-=5;
sound_hit[--hit_counter].play();
if (hit_counter==0){
hit_counter=5;
}
if (enemy.health <= 0) {
score += enemy.score;
sound_explode[Math.floor(Math.random()*25)].play();
enemy.isDead = true;
}
return false;
}
return true;
});
What you could try to do is to reduce the number of tests by grouping the enemies and rockets, so that you only have to test the elements in the same group.
Here is a simple implementation to show what I mean, this only partitions in X-direction, because your rockets only seem to travel horizontally:
var groupWidth = 100; // do some experiments to find a suitable value
var rocketGroups = [];
var enemyGroups = [];
// initalize groups, not shown (array of array of rocket/enemy),
// but here are some other function examples...
function addToGroups(element, groups) {
groups[element.x / groupWidth].push(element);
}
function move(element, groups, distance) {
if (element.x / groupWidth != (element.x + distance) / groupWidth) {
// remove element from the old group and put it in the new one
}
element.x += distance;
}
// Note: this is only to show the idea, see comments about length
function checkCollisions() {
var i,j,k;
for (i = 0; i < rocketGroups.length; i++) {
for (j = 0; j < rocketGroups[i].length; j++) {
for (k = 0; k < enemyGroups[i].length; k++) {
// only compares elements in the same group
checkPossibleCollision(rocketGroups[i], enemyGroups[i], j, k);
}
}
}
}

Categories