I am attempting to make a 5x5 grid using arrays with the following limitations
Should not exceed more than 4 check marks per grid
Should not have 2 consecutive check marks
This is what I have come up with so far, I would appreciate if someone could help me figure out how would I achieve the latter condition
let emoji = {
0: '✅',
1: '❓',
}
let grid = []
let checkmarks = 0
for (let i = 0; i < 5; i++) {
let row = []
for (let j = 0; j < 5; j++) {
let random = crypto.randomInt(0, 1000) % 2
if (random == 0) {
if(checkmarks < 4) {
row.push(emoji[0])
checkmarks++
}
else {
row.push(emoji[1])
}
} else {
row.push(emoji[1])
}
}
grid.push(row)
}
I am attempting to make it as random as possible.
I'm posting this answer because the accepted answer doesn't seem to produce a consistent result. I agree with most of the approach, but result just wasn't always returning 4 checkmarks (because it seems to reset after each iteration, which can increase the maximum number of loops needed).
But ultimately, the idea is to fill the 5x5 array with the ❓ character first, randomly select a location, verify the surrounding blocks are not ✅, and then place a ✅ if these conditions are met. If not, I instead just select a new position but keep the existing results until the needed number of ✅ have been set.
let grid = [],
rows = 5,
cols = 5,
maxChecks = 4,
totalChecks = 0,
emoji = {
0: '✅',
1: '❓',
};
const _RandomChecks = () => {
grid = [];
totalChecks = 0;
for(let i = 0; i < rows; i++) {
grid[i] = [];
for(let j = 0; j < cols; j++) {
grid[i] = [...grid[i], emoji[1]];
}
}
while(totalChecks < maxChecks) {
let rndRow = parseInt(crypto.randomUUID().replace(/[^0-9]/g, "").substr(-8)) % rows,
rndCol = parseInt(crypto.randomUUID().replace(/[^0-9]/g, "").substr(-8)) % cols,
valid = (grid[rndRow][rndCol] == emoji[1]) ? true : false;
if(grid[rndRow-1]?.[rndCol] && valid) valid = (grid[rndRow-1]?.[rndCol] == emoji[1]) ? true : false;
if(grid[rndRow+1]?.[rndCol] && valid) valid = (grid[rndRow+1]?.[rndCol] == emoji[1]) ? true : false;
if(grid[rndRow][rndCol-1] && valid) valid = (grid[rndRow][rndCol-1] == emoji[1]) ? true : false;
if(grid[rndRow][rndCol+1] && valid) valid = (grid[rndRow][rndCol+1] == emoji[1]) ? true : false;
if(valid) {
grid[rndRow][rndCol] = emoji[0];
totalChecks++;
}
}
console.log(grid.map(row => row.join('')).join('\n'));
}
_RandomChecks();
Instead of randomly determining if a cell should be a checkmark I would rather randomly find cells that should be a checkmark.
Your current solution decreases the chance of getting a checkmark with each cell.
Created some example code for you:
const emojis = ['✅', '❓']
const size = 5
const checkmarks = []
for (let i = 0; i < 4; i += 1) {
while (true) {
// get random x and y
const x = Math.random() * size | 0
const y = Math.random() * size | 0
// check if x and y are far enough from existing checkmarks
const areNeighbours = checkmarks.some(c => {
if (c.x === x) {
return Math.abs(c.y - y) <= 1
}
if (c.y === y) {
return Math.abs(c.x - x) <= 1
}
return false
})
if (!areNeighbours) {
checkmarks.push({
x,
y
})
break
}
}
}
const grid = []
for (let y = 0; y < size; y += 1) {
grid.push([])
for (let x = 0; x < size; x += 1) {
const checkmark = checkmarks.find(c => c.x === x && c.y === y)
grid[y][x] = checkmark ? emojis[0] : emojis[1]
}
}
console.log(grid.map(row => row.join('')).join('\n'))
Imagine a 5x5 board initially filled by ❓.
Next you toss 4 coins at once, each coin will landed in one cell, head or tail.
If head, place a ✅ in the cell.
Now check if non-consecutive ✅ condition is met. If not start over.
Solution:
const emojis = ['✅', '❓'];
function randomInt(min, max) {
return min + Math.floor(Math.random() * (max - min));
}
function tossCoins(checkmarkLimit, size) {
const positions = Array.from({ length: checkmarkLimit }, () => {
const pos = randomInt(0, size * size);
const tail = Math.random() > 0.5;
if (tail) return null;
const x = pos % 5;
const y = (pos - x) / 5;
return [x, y];
})
return positions.filter(Boolean);
}
function checkNonConsecutive(positions) {
for (let i = 0; i < positions.length; i++) {
const p = positions[i];
for (let j = 0; j < positions.length; j++) {
if (i == j) continue;
const o = positions[j];
const distance = Math.abs(p[0] - o[0]) + Math.abs(p[1] - o[1])
if (distance <= 1) {
return false;
}
}
}
return true;
}
function main() {
const checkmarkLimit = 4;
const size = 5;
const grid = Array.from({ length: size }, () => Array.from({ length: size }, () => emojis[1]));
let positions = tossCoins(checkmarkLimit, size);
while (!checkNonConsecutive(positions)) {
positions = tossCoins(checkmarkLimit, size);
}
positions.forEach(([x, y]) => {
grid[y][x] = emojis[0];
});
return grid;
}
for (let n=0; n < 10; n++) {
console.log('round: ' + n);
console.log(main().map(row => row.join('')).join('\n'));
}
What I'd like to do is take an SVG shape drawn by an enclosed path (in this case, a region of a map) and reduce the number of points to create a simpler shape.
I've tried implementing the Ramer-Douglas-Peucker algorithm to reduce the number of points. For example, here's a fiddle using the simplify.js library:
https://jsfiddle.net/0t3n8762/
After reading about the issue, if I understood it correctly, it seems the algorithm isn't really designed to work on enclosed shapes but open ended paths. I tried splitting each path into two (so there are two lines that together make the entire shape) and running the algorithm on each before recombining them, though the results seem essentially identical:
https://jsfiddle.net/caqwL3t7/
It may be (and indeed is quite likely) that I'm just not grasping how the algorithm is supposed to work and am implementing it incorrectly. Or perhaps that I should be trying a completely different method altogether.
coords = pathToCoords($("#JP-01").attr("d"));
for (var i = 0; i < coords.length; i++) {
newCoords[i] = simplify(coords[i], 2);
}
newPath = coordsToPath(newCoords);
$("#JP-01").attr("d", newPath);
What I would want to produce is a simpler path that still retains the overall shape of the original, drawn with fewer points. The actual result is a distorted shape that shares little in common with the original.
As Paul pointed out in the comments, you haven't considered about l and v command in a path.
I made a snippet to show how to achieve your goal but it won't work in all the cases (I guess) and it still needs to be improved.
Kindly find the snippet and comments below.
$("#simplify-1").on("click", function(){
var coords;
var newCoords = [];
var newPath;
var coordsObject;
// get the coordinates from a given path
function pathToCoords(path) {
// save each path individually (.i.e islands are stored separately)
var data = path.match(/(?<=m).*?(?=z)/igs);
var coordsArray = [];
var objectArray = [];
var objectArrayA = [];
var objectArrayB = [];
var objectContainer = [];
// split each pair of coordinates into their own arrays
for (var i = 0; i < data.length; i++) {
// should remove anything between h or v and l instead?
data[i] = data[i].split(/[LlHhVv]/);
coordsArray[i] = [];
for (var j = 0; j < data[i].length; j++) {
coordsArray[i].push(data[i][j].split(",").map(Number));
}
}
// convert each pair of coordinates into an object of x and y
for (var i = 0; i < coordsArray.length; i++) {
objectArray[i] = [];
for (var j = 0; j < coordsArray[i].length; j++) {
objectArray[i].push({
x: coordsArray[i][j][0],
y: coordsArray[i][j][1]
});
}
// split each array of coordinates in half
var halfway = Math.floor(objectArray[i].length / 2);
objectArrayB[i] = JSON.parse(JSON.stringify(objectArray[i]));;
objectArrayA[i] = objectArrayB[i].splice(0, halfway);
}
objectContainer = [objectArrayA, objectArrayB];
return objectContainer;
}
// convert the coordinates back into a string for the path
function coordsToPath(objectContainer) {
var objectArray = [];
var coordsArray = [];
var data;
// recombine the two objectArrays
for (var i = 0; i < objectContainer[0].length; i++) {
objectArray[i] = objectContainer[0][i].concat(objectContainer[1][i])
}
for (var i = 0; i < objectArray.length; i++) {
coordsArray[i] = [];
// take the X and Y values from the objectArray and strip the unwanted information
for (var j = 0; j < objectArray[i].length; j++) {
if (j == 0) {
// add 'M' in front of the first entry
coordsArray[i].push("M" + Object.values(objectArray[i][j]));
} else if (j == objectArray[i].length - 1) {
// add 'z' to the end of the last entry
coordsArray[i].push("l" + Object.values(objectArray[i][j]) + "z");
} else {
// add 'l' in front of each coordinate pair
coordsArray[i].push("l" + Object.values(objectArray[i][j]));
}
}
coordsArray[i] = coordsArray[i].toString();
}
// put everything back into a single valid SVG path string
data = coordsArray.join("");
return data;
}
// -- simplify.js -- //
/*
(c) 2017, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/
(function() {
'use strict';
// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1.x - p2.x,
dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {
var x = p1.x,
y = p1.y,
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format
// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0],
newPoints = [prevPoint],
point;
for (var i = 1, len = points.length; i < len; i++) {
point = points[i];
if (getSqDist(point, prevPoint) > sqTolerance) {
newPoints.push(point);
prevPoint = point;
}
}
if (prevPoint !== point) newPoints.push(point);
return newPoints;
}
function simplifyDPStep(points, first, last, sqTolerance, simplified) {
var maxSqDist = sqTolerance,
index;
for (var i = first + 1; i < last; i++) {
var sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
simplified.push(points[index]);
if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
}
}
// simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
var last = points.length - 1;
var simplified = [points[0]];
simplifyDPStep(points, 0, last, sqTolerance, simplified);
simplified.push(points[last]);
return simplified;
}
// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {
if (points.length <= 2) return points;
var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
// export as AMD module / Node module / browser or worker variable
if (typeof define === 'function' && define.amd) define(function() {
return simplify;
});
else if (typeof module !== 'undefined') {
module.exports = simplify;
module.exports.default = simplify;
} else if (typeof self !== 'undefined') self.simplify = simplify;
else window.simplify = simplify;
})();
// -- end simplify.js -- //
coords = pathToCoords($("#OriginalJP-01").attr("d"));
for (var i = 0; i < coords.length; i++) {
newCoords[i] = [];
for (var j = 0; j < coords[i].length; j++) {
newCoords[i][j] = simplify(coords[i][j], 1);
}
}
newPath = coordsToPath(newCoords);
$("#JP-01").attr("d", newPath);
});
$("#simplify-2").on("click", function(){
let d = $("#OriginalJP-01").attr("d");
let coordsArray = [];
let data = d.match(/(?<=m).*?(?=z)/igs);
// split each pair of coordinates into the array as an object {x, y}
for (var i = 0; i < data.length; i++) {
let ca = coordsArray[i] = [];
// split data[i] into each coordinate text
let matches = data[i].match(/((\w?-?\d+(\.\d+)?)(,-?\d+(\.\d+)?)?)/g);
for(let j=0;j<matches.length;j++){
let x, y,
text = matches[j],
// split with comma and convert it to a number
temp = text.split(",").map(v=>+v.replace(/^[^\-\d]/g,""));
switch(text[0]){
default:
case "L": // absolute
x = temp[0];
y = temp[1];
break;
case "l": // relative
x = ca[j-1].x + temp[0];
y = ca[j-1].y + temp[1];
break;
case "V": // absolute
x = ca[j-1].x;
y = temp[0];
break;
case "v": // relative
x = ca[j-1].x;
y = ca[j-1].y + temp[0];
break;
case "H": // absolute
x = temp[0];
y = ca[j-1].y;
break;
case "h": // relative
x = ca[j-1].x + temp[0];
y = ca[j-1].y;
break;
}
x = +x.toFixed(2);
y = +y.toFixed(2);
ca.push({x, y});
}
}
let mArray = [];
// calculate the slopes
for(let i=0;i<coordsArray.length;i++){
mArray[i] = [];
for(let j=0;j<coordsArray[i].length-1;j++){
let {x, y} = coordsArray[i][j], // current point's x and y
{x: nx, y: ny} = coordsArray[i][j+1], // next point's x and y
dy = (ny - y);
if(dy === 0) // to check if the denominator is legal or not
// in your case, it would not enter here
mArray[i].push(Infinity);
else
mArray[i].push((nx - x) / dy);
}
}
let abandonFactor = +$("#abandonFactor").val();
let newCoordsArray = [];
for(let i=0;i<mArray.length;i++){
let na = newCoordsArray[i] = [];
// calculate the abandonRate base on the amount of the original points
let abandonRate = coordsArray[i].length * abandonFactor;
for(let j=0;j<mArray[i].length-1;j++){
let m = mArray[i][j], // this slope
nm = mArray[i][j+1]; // next slope
let diffRate = Math.abs((m - nm) / m); // calculate the changes of the slope
// check if the diffRate is greater than abandonRate
// or the sign of m not equals the sign of nm
// you can try out removing the "sign check part" and see what would happen ;)
if(diffRate >= abandonRate || (Math.sign(m) !== Math.sign(nm))){
na.push(coordsArray[i][j]);
}
}
}
let newPath = [];
// create new path
for(let i=0;i<newCoordsArray.length;i++){
let temp = [];
for(let j=0;j<newCoordsArray[i].length;j++){
let {x, y} = newCoordsArray[i][j];
let p = `${x},${y}`;
temp.push(p);
}
newPath.push("M" + temp.join("L") + "z");
}
$("#JP-01").attr("d", newPath.join(""));
}).click();
$("#abandonFactor").on("change", function(){
$("#abandonFactor_text").text(`Abandon factor: ${this.value}`);
$("#simplify-2").click();
});
div {
width: 50%;
}
.original {
float: left;
}
.simplified {
float: right;
}
.map {
box-sizing: border-box;
width: 100%;
padding: 0.5rem;
text-shadow: 1px 1px white;
cursor: grab;
}
.land {
fill: lightgreen;
fill-opacity: 1;
stroke: white;
stroke-opacity: 1;
stroke-width: 0.5;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="original">
<svg id="OriginalMap" viewBox="324.55999755859375 0 126.44989013671875 111.65999603271484" class="map" xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap">
<g>
<path id="OriginalJP-01" title="Hokkaido" class="land" d="
M344.04,71.29l2.71,-2.13l0.04,-0.59l-1.96,-3.17l-1.38,-0.99l-0.7,-1.16l0.68,-2.54l-0.27,-0.48l1.64,0.11l0.6,-0.42l0.21,-0.54l0.53,-0.21l2.75,2.66l1.93,0.87l1.13,1.28l2.48,-0.47l0.17,-0.42l1.02,-0.32l0.72,0.02l-0.14,1.17l3.04,1.39l2.2,-1.22l1.97,-2.15l0.99,-1.6l0.1,-2.35l-1.38,-2.76l0.54,-1.94L363,51.89l-0.45,-2.27l1.02,-2.1l1.47,-0.89l0.76,-0.05l2.03,-1.32l0.93,-1.89l0.41,-1.95l-0.35,-7.86l0.5,-0.32l1.64,-3.44l0.21,-2.86l0.38,-0.79l0.13,-1.6l-0.3,-3.27l-0.77,-3.84l-3.04,-7.32l-0.31,-1.16l0.07,-0.99l0.86,-1.27l0.79,-1.95l-0.41,-1.4l0.15,-1.28l0.75,0.77l0.13,0.52l0.39,0.16l1.76,-0.28l1.35,-0.94l0.55,-1.99L374.52,0l0.47,0.33l0.56,1.29l0.59,0.34l0.63,1.47l2.32,1.7l2.21,3.12l4.95,5.56l3.91,7.23l2.58,2.55l2.44,3.1l5.58,4.4l1.57,0.89l0.33,0.95l1.26,0.96l3.7,1.94l3.24,1.28l5.88,1.69l2.79,0.53l0.44,-0.31l0.29,0.77l-0.06,1.28l0.87,1.55l0.58,0.48l3.21,1.01l3.41,0.21l2.57,-0.32l2.28,-1.99l1.54,-1.92l2.18,-2.04l1.83,-1.26l0.66,-1.45l1.1,-1.16l0.78,-1.38l0.46,0.03l0.45,2.23l-1.21,2.49l-0.91,0.92l-0.6,2.4l-1.73,2.13l-0.94,2.12l0.1,0.83L436.06,48l0.22,1.06l1.35,2.58l1.49,1.97l1.52,5.23l1.49,1.96l-1.16,-0.53l-0.19,-0.53l-1.14,-0.22l-0.24,0.37l0.21,1.26l0.86,-0.67l0.47,0.4l-0.29,0.55l0.37,0.39l0.4,-0.28l2.55,0.62l2.09,-1.7l0.32,-0.6l1.11,-0.71l0.24,-0.5l3.28,0.2l-0.19,0.5l-1.17,0.91l-1.73,0.31l-1.87,1.03l-0.8,2.93l-0.77,-0.36l-1.24,-0.05l-2.71,0.51l-1.51,0.9l-1.26,-0.21l-0.62,0.6l-0.14,1.15l-1.6,2.15l-1.02,0.49l-1.9,-0.1l-0.9,-0.79l0.03,-0.53l0.33,-0.31l-0.55,-0.37l-0.73,0.1l-1.19,1.73l-0.02,0.41l1.15,1.13l-2.01,-0.12l-1.15,-0.35l-3.58,-0.02l-0.69,-0.15l-1.58,-1.27l-3.06,0.54l-2.43,1.18l-3.4,2.53l-5.27,5.27l-4.08,5.8l-1.53,3.31l-0.28,1.67l0.55,1.44l-0.82,3.88l-0.91,1.53l0,1.61l-3.7,-3.72l-4.85,-2.02l-8.09,-4.45l-2.38,-1.67l-1.84,-2.05l-3.21,-0.94l-2.21,-2.05l-1.82,-1.2l-1.83,-0.53l-2.38,-0.04l-3.17,1.04l-2.08,1.29l-2.68,2.28l-1.59,0.89l-2.12,2.45l-0.34,0.75l-1.07,-0.4l-0.33,-0.55l0.38,-0.29l0.59,0.64l0.12,-0.81l-1.35,-0.41l-0.87,-2.37l-0.79,-0.4l-1.07,-1.12l-0.12,-0.58l-1.27,-1.23l-1.24,-0.09l-1.25,0.53l-0.97,-0.59l-1.21,0.03l-1.68,1.81l-1.44,2.75l-0.71,2.55l0.28,1.94l2.32,1.2l2.74,2.35l0.75,0.14l1.37,-0.53l2.04,0.31l1.23,2.21l1.63,1.22l1.08,1.88l3.15,1.36l0.55,0.89l0.79,0.59l-0.61,0.61l-0.95,0.01l-1.1,1.3l-1.66,0.66l-0.86,-0.79l-2.78,-0.91l-0.93,0.16l-0.28,0.62l-0.41,0.1l-0.18,-0.61l0.68,-0.45l-0.2,-0.67l-0.59,-0.37l-0.86,0.08l-0.41,0.33l-0.43,1.68l-1.44,1.06l-1.29,0.1l-0.35,0.33l-0.31,3.94l-0.4,0.48l-0.94,0.03l-1.96,0.87l-0.9,1.89l-0.39,0.32l-1.02,-0.8l-1.14,0.22l-0.9,-0.71l-1.08,-2.72l-0.09,-0.86l0.41,-1.77l1.35,-3.03v-1.05l0.77,0.08l0.29,-0.59l0.31,-2.7l-0.4,-1.98l-1.9,-2.94l-0.67,-0.36l-1.3,-0.12L334.29,91l-0.85,-0.88l-1.14,-0.41l-0.39,-0.73l-0.19,-1.39l0.31,-1.12l0.94,-1.03l0.33,-0.95l0.03,-2.65l-0.49,-2.23l0.92,-1.45l1.12,-0.62l2.28,-0.06l0.61,-0.91l1.39,-0.73l0.85,-1.92l0.82,0.57l0.56,0.97l0.89,-0.23l0.09,-1.39l0.82,-0.84L344.04,71.29z
M358.76,9.86l-0.09,-1.23l1.28,-1.25l2.07,1.21l0.51,1.81l-1.81,1.49l-1.37,-1.07L358.76,9.86z
M326.88,91.03l-0.29,2.22l-0.39,0.63l-0.65,0.23l-0.02,0.36l-0.61,-0.54l0.06,-1.71l-0.42,-1.11l0.74,-1.09l1.91,-0.38l0.46,-0.52l0.03,0.64L326.88,91.03z
M357.23,4.25l-0.26,2.59l-0.39,0.13L355.7,4l0.22,-1.31l-0.61,-0.34l0.09,-0.73l0.65,0.85l0.66,-0.15l-0.02,-0.59L357.03,2l0.35,0.78L357.23,4.25z"/>
</g>
</svg> Original
</div>
<div class="simplified">
<svg id="Map" viewBox="324.55999755859375 0 126.44989013671875 111.65999603271484" class="map" xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap">
<g>
<path id="JP-01" title="Hokkaido" class="land" d=""/>
</g>
</svg> Simplified
<button id="simplify-1">Your method</button>
<button id="simplify-2">New method</button>
<input type="range" min="0.0001" max="0.1" value="0.01" step="0.0001" class="slider" id="abandonFactor">
<div id="abandonFactor_text">Abandon factor: 0.01</div>
</div>
I am having an issue with the following code that simulates a card deck.
The deck is created properly (1 array containing 4 arrays (suits) containing 13 elements each (face values)) and when I use the G.test(); function it is correctly pulling 13 random cards but then returns 39x "Empty" (A total of 52).
I hate to ask for help, but I have left the problem overnight and then some and I still cannot find the reason that this is happening. I appreciate any and all insight that can be offered.
var G = {};
G.cards = [[], [], [], []];
G.newCard = function(v) { //currently a useless function, tried a few things
return v;
};
G.deck = {
n: function() { //new deck
var x; var list = [];
list.push(G.newCard("A"));
for (x = 2; x <= 10; x += 1) {
list.push(G.newCard(x.toString()));
}
list.push(G.newCard("J"), G.newCard("Q"), G.newCard("K"));
for (x = 0; x < G.cards.length; x += 1) {
G.cards[x] = list;
}
},
d: function() { //random card - returns suit & value
var s; var c; var v; var drawn = false; var n;
s = random(0, G.cards.length);
c = random(0, G.cards[s].length);
n = 0;
while (!drawn) {
if (G.cards[s].length > 0) {
if (G.cards[s][c]) {
v = G.cards[s].splice(c, 1);
drawn = true;
} else {
c = random(0, G.cards[s].length);
}
} else {
s = (s + 1 >= G.cards.length) ? 0 : s + 1;
n += 1;
console.log(s);
if (n >= G.cards.length) {
console.log(n);
return "Empty";
}
}
}
return {s: s, v: v[0]};
},
}; //G.deck
G.test = function() {
var x; var v;
G.deck.n();
for (x = 0; x < 52; x += 1) {
v = G.deck.d();
console.log(v);
}
};
Replace
for (x = 0; x < G.cards.length; x += 1) {
G.cards[x] = list;
}
with
for (x = 0; x < G.cards.length; x += 1) {
G.cards[x] = list.slice();
}
as this prevents all elements of G.cards[x] binding to the same (single) array instance.
If all elements bind to the same instance, mutating one element equals mutating all elements. list.slice() creates a new copy of list and thus a new array instance to prevent the aforementioned issue.
I won't go through your code, but I built a code that will do what you wanted. I only built this for one deck and not multiple deck play. There are two functions, one will generate the deck, and the other will drawn cards from the deck, bases on how many hands you need and how many cards you wanted for each hand. One a card is drawn, it will not be re-drawn. I might publish a short article for how a card dealing program work or similar in the short future at http://kevinhng86.iblog.website.
function random(min, max){
return Math.floor(Math.random() * (max - min)) + min;
}
function deckGenerate(){
var output = [];
var face = {1: "A", 11: "J", 12: "Q", 13: "K"};
// Heart Space Diamond & Club;
var suit = ["H", "S", "D", "C"];
// Delimiter between card identification and suit identification.
var d = "-";
for(i = 0; i < 4; i++){
output[i] = [];
for(ind = 0; ind < 13; ind++ ){
card = (ind + 1);
output[i][ind] = (card > 10) || (card === 1)? face[card] + d + suit[i] : card.toString() + d + suit[i];
}
}
return output;
}
function randomCard(deck, hand, card){
var output = [];
var randS = 0;
var randC = 0;
if( hand * card > 52 ) throw("Too many card, I built this for one deck only");
for(i = 0; i < hand; i++){
output[i] = [];
for(ind = 0; ind < card; ind++){
randS = random(0, deck.length);
randC = random(0, deck[randS].length);
output[i][ind] = deck[randS][randC];
deck[randS].splice(randC,1);
if(deck[randS].length === 0) deck.splice(randS,1);
}
}
document.write( JSON.stringify(deck, null, 2) );
return output;
}
var deck = deckGenerate()
document.write( JSON.stringify(deck, null, 2) );
document.write("<br><br>");
var randomhands = randomCard(deck, 5, 8);
document.write("<br><br>");
document.write("<br><br>");
document.write( JSON.stringify(randomhands, null, 2) );