Randomising arrays to form an highly entropic grid - javascript

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'));
}

Related

generate random numbers in specific range and using specific numbers?

How to generate random numbers in specific range and using specific numbers?
Example
given numbers [7,8];
given range [100-900];
output must be one of them 777, 787, 788, 878, 877, 888 etc...
Help me
const randomGenerateNumber = (maxRange:number, minRange:number, numbers:number[]) => {
//...what should i do in there??? Help me? Any Idea?
}
I think you don't want random numbers. It seems that you want a set of numbers based on some rules. Random means something else.
If I understand well your question you want to generate all possible numbers containing only a set of digits from a range of numbers. Is this an accurate description?
If so, this is similar with what you want: Generate random numbers only with specific digits
Edit:
You are right, so you want only one number.
In javascript you could do something like this:
I edited the algorithm to take into account min and max in probably the most lazy way. I didn't take into account cases where numbers can't be generated, it will return undefined.
There are so many ways to do this. Your algorithm can work too and maybe more efficient but it seems to have an issue with 0, it will generate numbers with 0 even if it's not in the digits array.
function randomGenerateNumber(minRange, maxRange, digits){
noTries = 0;
while(noTries++ < 100000)
{
var num = 0;
//get a random number from your range
len = Math.floor(Math.random() * (maxRange - minRange) + minRange);
//get the lenght of that random number
len = len.toString().length;
//generate a number with that length using only your set of digits
while(len--)
{
num = num * 10 + digits[Math.floor(Math.random() * digits.length)];
}
if(num >= minRange && num<= maxRange)
{
return num;
break;
}
}
}
//your testing cases
console.log(randomGenerateNumber(100,900,[7,8]))
console.log(randomGenerateNumber(299,300,[1,2,3,4,5,6,7,8,9]));
i did it. Is there any improvement. Little bit messy.
const getRandomNumber = (min: number, max: number, numbers: number[]): number => {
if (numbers.length === 9) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
let result = '';
//split maxDigits 100 => [1, 0, 0]
const maxDigits = max
.toString()
.split('')
.map(i => parseInt(i, 10));
//split minDigits 100 => [1, 0, 0]
const minDigits = min
.toString()
.split('')
.map(i => parseInt(i, 10));
//length of random number [minDigit, maxDigit] inclusive
const randomDigit = Math.floor(Math.random() * (maxDigits.length - minDigits.length + 1) + minDigits.length);
let alreadyHigh = false;
let alreadyLow = false;
let equal = true;
//4 conditions
//1. minDigits.length === maxDigits.length
//2. randomDigit === minDigits.length
//3. randomDigit === maxDigits.length
//4. randomDigit > minDigits.length && randomDigit < maxDigits.length
for (let i = 0; i < randomDigit; i++) {
const numbersToUse = i === 0 ? numbers : [...numbers, 0];
let availableNumbers = [];
if (minDigits.length === maxDigits.length) {
if (equal) {
for (let k = 0; k < numbersToUse.length; k++) {
if (minDigits[i] > maxDigits[i]) {
if (numbersToUse[k] >= 0 && numbersToUse[k] <= maxDigits[i]) {
availableNumbers.push(numbersToUse[k]);
}
} else if (numbersToUse[k] >= minDigits[i] && numbersToUse[k] <= maxDigits[i]) {
availableNumbers.push(numbersToUse[k]);
} else {
availableNumbers.push(maxDigits[i]);
}
}
} else {
if (!alreadyHigh) {
for (let k = 0; k < numbersToUse.length; k++) {
if (numbersToUse[k] >= minDigits[i]) {
availableNumbers.push(numbersToUse[k]);
}
}
} else {
availableNumbers = numbersToUse;
}
}
} else if (randomDigit === minDigits.length) {
if (!alreadyHigh) {
for (let k = 0; k < numbersToUse.length; k++) {
if (numbersToUse[k] >= minDigits[i]) {
availableNumbers.push(numbersToUse[k]);
}
}
} else {
availableNumbers = numbersToUse;
}
} else if (randomDigit === maxDigits.length) {
if (!alreadyLow) {
for (let k = 0; k < numbersToUse.length; k++) {
if (numbersToUse[k] <= maxDigits[i]) {
availableNumbers.push(numbersToUse[k]);
}
}
} else {
availableNumbers = numbersToUse;
}
} else {
availableNumbers = numbersToUse;
}
availableNumbers = [...new Set(availableNumbers)];
const randomIndex = Math.floor(Math.random() * availableNumbers.length);
result += `${availableNumbers[randomIndex]}`;
alreadyHigh = !alreadyHigh ? availableNumbers[randomIndex] > minDigits[i] : true;
alreadyLow = !alreadyLow ? availableNumbers[randomIndex] < maxDigits[i] : true;
equal = equal ? availableNumbers[randomIndex] === maxDigits[i] : false;
}
return parseInt(result, 10);
};

How to solve a Box Claw mover so it spread all boxes evenly on all available stacks?

I'm trying to solve a Robotic Claw lever puzzle, which should spread all boxes across all available stacks evenly.
The problem is that when it reaches the last box, it moves LEFT and then RIGHT, looping infinitely.:
function solve(clawPos, boxes, boxInClaw) {
// Calculate a maximum number of boxes per stack
let max = (boxes.reduce((a, b) => a + b) + ~~boxInClaw) / boxes.length
// Current stack number
const current = boxes[clawPos]
// Helpers
const bigger = current > max
const lower = current < max
const equal = current === max
const last = clawPos === boxes.length - 1
// Command to return for a claw
let command = ''
// Actions for claw
const R = () => command = 'RIGHT'
const L = () => command = 'LEFT'
const U = () => command = 'PICK'
const D = () => command = 'PLACE'
// Automatically select where to move the claw
const autoDirection = () => {
const value = boxes[clawPos]
const left = boxes.slice(0, clawPos)
const right = boxes.slice(clawPos, -1)
const target = max - 1
if (boxInClaw) {
if (left.includes(target)) L()
else if (right.includes(target)) R()
} else {
R()
}
}
autoDirection()
if (boxInClaw) {
if (lower) D()
} else {
if (bigger) U()
}
return command;
}
I've tried to many different approaches to make it dynamic, is there any smarter approach to know at which direction it should go?
Here is a direct link (please, DO NOT submit): https://www.codingame.eu/evaluate/18917274?id=427696529803c1cd24e9258b89d01a98a72126e
This is my solution to that:
function createStack(length, totalBoxes) {
const boxPerStack = Math.floor(totalBoxes / length);
let newStack = new Array(length).fill(boxPerStack);
const remainder = totalBoxes % length;
if (remainder !== 0) {
for (let i = 0; i < remainder; i++) {
newStack[i]++;
}
}
return newStack;
}
function solve(clawPos, boxes, boxInClaw) {
// Write your code here
const totalBoxes = boxes.reduce((prev, acc) => prev + acc);
let targetPos;
if (boxInClaw) {
const targetStack = createStack(boxes.length, totalBoxes + 1);
// Move to place
for (let i = 0; i < boxes.length; i++) {
if (boxes[i] < targetStack[i]) {
targetPos = i;
break;
}
}
if (clawPos === targetPos) return 'PLACE';
else if (clawPos < targetPos) return 'RIGHT';
else return 'LEFT';
} else {
const targetStack = createStack(boxes.length, totalBoxes);
// Move to pick
for (let i = 0; i < boxes.length; i++) {
if (boxes[i] > targetStack[i]) {
targetPos = i;
break;
}
}
if (clawPos === targetPos) return 'PICK';
else if (clawPos < targetPos) return 'RIGHT';
else return 'LEFT';
}
return '';
}

Separating Axis Theorem Implementation always returning true. What am I doing wrong?

I'm trying to implement collision detection for concave polygons in Javascript/p5.js using the Separating Axis Theorem. I've been following the following tutorial on how to use it: http://www.dyn4j.org/2010/01/sat/
However, my check is always returning true, no matter the positioning of the two polygons. Here's my code:
function SAT(shape1, shape2)
{
let axes1 = getAxes(shape1);
let axes2 = getAxes(shape2);
let colliding = true;
for (let i = 0; i < axes1.length; i++)
{
let axis = axes1[i];
let p1 = shape1.project(axis);
let p2 = shape2.project(axis);
if (!p1.overlap(p2)) colliding = false;
}
for (let i = 0; i < axes2.length; i++)
{
let axis = axes2[i];
let p1 = shape1.project(axis);
let p2 = shape2.project(axis);
if (!p1.overlap(p2)) colliding = false;
}
return colliding;
}
function getAxes(shape)
{
let axes = [];
for (let i = 0; i < shape.vertices.length; i++)
{
let p1 = shape.vertices[i];
let p2 = shape.vertices[i + 1 == shape.vertices.length ? 0 : i + 1];
let edge = p1.sub(p2);
let normal = createVector(-edge.y, edge.x);
axes[i] = normal;
}
return axes;
}
class Projection
{
constructor(min, max)
{
this.min = min;
this.max = max;
}
overlap(other)
{
if (this.max < other.min || other.max < this.min) return false;
else return true;
}
}
class PolygonCollider extends Component
{
constructor(gameObject)
{
super(gameObject);
this.untransformedVertices = [];
this.vertices = [];
...
}
setVerts(verts)
{
if (verts && verts.length > 2)
{
this.untransformedVertices = verts;
this.vertices = this.transform.getTransformedPoints(verts);
return this;
}
else return false;
}
project(axis)
{
let min = axis.dot(this.vertices[0]);
let max = min;
for (let i = 1; i < this.vertices.length; i++)
{
let p = axis.dot(this.vertices[i]);
if (p < min) min = p;
else if (p > max) max = p;
}
return new Projection(min, max);
}
update()
{
this.vertices = this.transform.getTransformedPoints(this.untransformedVertices);
}
...
}
Vertices are transformed with the following function, using a defined scale, rotation and position:
getTransformedPoints(points)
{
let transformed = [];
for (let i = 0; i < points.length; i++)
{
let rX = ((this.scale.x * points[i].x) * Math.cos(this.rotation)) - ((this.scale.y * points[i].y) * Math.sin(this.rotation));
let rY = ((this.scale.x * points[i].x) * Math.sin(this.rotation)) + ((this.scale.y * points[i].y) * Math.cos(this.rotation));
transformed[i] = createVector(rX + this.position.x, rY + this.position.y);
}
return transformed;
}
The SAT method is always returning true. I believe I'm checking for the overlap incorrectly, but I can't figure out what exactly I'm doing wrong.
So, it turns out the issue with my implementation lied with p5.js, a library I am using in this case.
In the getAxes method, I was subtracting p1 from p2 using p5's built in p5.Vector.sub function. This didn't have the desired effect. I'm not sure, but I believe the issue was that it wasn't creating a new vector that was the difference of the equation. I fixed this simply by creating the new vector myself as such: createVector(p2.x - p1.x, p2.y - p1.y);

Javascript while loop (Card deck simulation)

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) );

Create staircase from symbols using javascript

I want to output the staircase from the symbols "#". It should look like this:
but all I achieve is this:
What should I do to get right output?
var n = 6;
var rows=[];
var cols=[];
for(i=n-1;i>=0;i--) {
rows=[];
for(j=0;j<n;j++) {
if(j >= i) {
rows[j] = "#";
} else {
rows[j] = "";
}
}
cols.push(rows);
cols.splice(0, cols.length - 1);
console.log(cols.join(","));
}
Think of it as a coordinate system, loop through y and x and add the needed symbols.
Remember to increase max x with current y if you want it dual sided.
function ladder(size, dualSided, empty, occupied) {
if (dualSided === void 0) { dualSided = true; }
if (empty === void 0) { empty = " "; }
if (occupied === void 0) { occupied = "▲"; }
var str = "";
for (var y = 0; y < size; y++) {
for (var x = 0; x < size + y; x++) {
if (dualSided != true && x == size) {
break;
}
if (x >= size - y - 1) {
str += occupied;
}
else {
str += empty;
}
}
str += "\n";
}
return str;
}
console.log(ladder(20, false));
Ok, try this(last 3 lines);
cols.push(rows.join(""));
cols.splice(0, cols.length - 1);
console.log(cols.join(""));
The issue is you are pushing array(row) in cols, where row array itself contains comma. If you do cols.push(rows.join("")) , all comma will be removed.
Simple solution using Array.from
Live demo check below.
function staircase(n) {
let arr = Array.from({ length: n }).fill(0);
arr.map((v,i) => {
let dummyArr = Array.from({ length: i+1 }).fill('#');
let spaceArr = Array.from({ length: arr.length - (i+1) }).fill(' ');
console.log(`${spaceArr.join('')}${dummyArr.join('')}`);
});
}
staircase(6);
try this simple solution
for (let i = 1; i <= n; i++) {
console.log("#".repeat(i).padStart(n));
}

Categories

Resources