Lukas Laudrain
Lukas Laudrain

Reputation: 425

JS Random Walk Randomness

I have this script in JS that implement a random walk with lerp, the problem is that with a pseudo-perfect randomness / distribution, my points always go to the top left. Can someone explain that ?
I know javascript randomness isn't perfect since it's based on the computer clock and calculations, however, with a certain amount of time, every points should stay around the center, because every probability cancels its opposite.

const canvas = document.querySelector('#myCanvas');
const ctx = canvas.getContext('2d');

const width = canvas.clientWidth;
const height = canvas.clientHeight;

canvas.width = width;
canvas.height = height;

// Amount of points
const AMOUNT = 15;

// Coordinate of the center of the screen
const centerX = width / 2;
const centerY = height / 2;

// Size of a point
const SIZE = 8;

// List containing all the point (placed by default at the center)
let points = Array.from({ length: AMOUNT }, () => [centerX, centerY]);
let pointsEnd = Array.from({ length: AMOUNT }, () => [centerX, centerY]);

// Keep count of the random distribution
let count = Array.from(new Array(4).keys());

function lerp(start, end, speed) {
  return start + (end - start) * speed;
}

function render() {
  // Clear the canvas
  ctx.fillStyle = 'rgba(255, 255, 255, 1)';
  ctx.fillRect(0, 0, width, height);

  // Loop through all the points
  for (let i = 0; i < AMOUNT; i++) {
    const point = points[i];
    const pointEnd = pointsEnd[i];

    // Draw the point
    ctx.fillStyle = 'black';
    ctx.fillRect(pointEnd[0] - SIZE / 2, pointEnd[1] - SIZE / 2, SIZE, SIZE);

    // Generate the random number
    const n = Math.floor(Math.random() * 4);
    count[n]++;

    // Get the sum of iterations
    const sum = count.reduce((a, b) => a + b);
    // Show the distribution of randomness
    console.log(count.map((val, i) => i + ':' + Math.round((val / sum) * 100) + '%').join(' | '));

    // Change coordinates depending on randomness
    if (n == 2) point[0] += 1;
    if (n == 3) pointEnd[0] -= 1;
    if (n == 1) point[1] += 1;
    if (n == 0) pointEnd[1] -= 1;
  }
}

setInterval(() => {
  requestAnimationFrame(render);
}, 1000 / 60);
<canvas id="myCanvas"></canvas>

Upvotes: 1

Views: 173

Answers (1)

skyline3000
skyline3000

Reputation: 7913

The issue is how you are changing the coordinates based on randomness:

if (n == 2) point[0] += 1;
if (n == 3) pointEnd[0] -= 1;
if (n == 1) point[1] += 1;
if (n == 0) pointEnd[1] -= 1;

This doesn't account for all the ways the point can change, you need to adjust the point and pointEnd in each case:

if (n === 0) {
    point[0] += 1;
    pointEnd[0] += 1;
} else if (n === 1) {
    point[0] -= 1;
    pointEnd[0] -= 1;
} else if (n === 2) {
    point[1] += 1;
    pointEnd[1] += 1;
} else if (n === 3) {
    point[1] -= 1;
    pointEnd[1] -= 1;
}

const canvas = document.querySelector('#myCanvas');
const ctx = canvas.getContext('2d');

const width = canvas.clientWidth;
const height = canvas.clientHeight;

canvas.width = width;
canvas.height = height;

// Amount of points
const AMOUNT = 15;

// Coordinate of the center of the screen
const centerX = width / 2;
const centerY = height / 2;

// Size of a point
const SIZE = 8;

// List containing all the point (placed by default at the center)
let points = Array.from({ length: AMOUNT }, () => [centerX, centerY]);
let pointsEnd = Array.from({ length: AMOUNT }, () => [centerX, centerY]);

// Keep count of the random distribution
let count = Array.from(new Array(4).keys());

function lerp(start, end, speed) {
  return start + (end - start) * speed;
}

function render() {
  // Clear the canvas
  ctx.fillStyle = 'rgba(255, 255, 255, 1)';
  ctx.fillRect(0, 0, width, height);

  // Loop through all the points
  for (let i = 0; i < AMOUNT; i++) {
    const point = points[i];
    const pointEnd = pointsEnd[i];

    // Draw the point
    ctx.fillStyle = 'black';
    ctx.fillRect(pointEnd[0] - SIZE / 2, pointEnd[1] - SIZE / 2, SIZE, SIZE);

    // Generate the random number
    const n = Math.floor(Math.random() * 4);
    console.log(n);
    count[n]++;

    // Get the sum of iterations
    const sum = count.reduce((a, b) => a + b);
    // Show the distribution of randomness
    console.log(count.map((val, i) => i + ':' + Math.round((val / sum) * 100) + '%').join(' | '));

    // Change coordinates depending on randomness
    if (n === 0) {
        point[0] += 1;
        pointEnd[0] += 1;
    } else if (n === 1) {
        point[0] -= 1;
        pointEnd[0] -= 1;
    } else if (n === 2) {
        point[1] += 1;
        pointEnd[1] += 1;
    } else if (n === 3) {
        point[1] -= 1;
        pointEnd[1] -= 1;
    }
    /*
    if (n == 2) point[0] += 1;
    if (n == 3) pointEnd[0] -= 1;
    if (n == 1) point[1] += 1;
    if (n == 0) pointEnd[1] -= 1;
    */
  }
}

setInterval(() => {
  requestAnimationFrame(render);
}, 1000 / 60);
<canvas id="myCanvas"></canvas>

Upvotes: 1

Related Questions