6

I'm trying to predictably shuffle javascript arrays the same way each time the webpage is loaded.

I can shuffle the arrays randomly, but every time i reload the page it's a different sequence.

I'd like it to shuffle the arrays the same way every time the page loads. There are many arrays and they are part of a procedurally generated world.

Protopop
  • 169
  • 6
  • 1
    Store it in local storage – adeneo Jan 31 '15 at 21:03
  • Write a not so random script for it. – Mouser Jan 31 '15 at 21:05
  • Thank you adeneo, but the user can erase local storage as part of the world's options. So i'd like to have some hard code that runs the same each time. – Protopop Jan 31 '15 at 21:07
  • Aren't procedural generators usually initialized with a seed value so that the random generator produces the same series of values each time? – loganfsmyth Jan 31 '15 at 21:09
  • Thank you - Yes they are, but I am having trouble applying the world generator to javascript arrays. I will take a another look and see if I can figure it out. – Protopop Jan 31 '15 at 21:14

3 Answers3

9

Chance.js worked perfectly. Thank you Billy Moon.

My Example:

<script type="text/javascript" src="assets/js/chance.js"></script>

var chance1 = new Chance(124); // you can choose a seed here, i chose 124
console.log(chance1.shuffle(['alpha', 'bravo', 'charlie', 'delta', 'echo']));
// Array [ "alpha", "delta", "echo", "charlie", "bravo" ]

As long as you set the seed with new Chance(xxx) you get the same result every time.

Protopop
  • 169
  • 6
6

Take a look at chancejs.com's seed function.

Billy Moon
  • 57,113
  • 24
  • 136
  • 237
  • Thank you Billy Moon. Chance.js worked perfectly. var chance1 = new Chance(124); // you can choose a seed here, i chose 124 console.log(chance1.shuffle(['alpha', 'bravo', 'charlie', 'delta', 'echo'])); // Array [ "alpha", "delta", "echo", "charlie", "bravo" ] – Protopop Jan 31 '15 at 21:25
  • @Protopop yeah - chance.js is very smart, and is great for many other things too - especially for repeatability in testing for example. – Billy Moon Jan 31 '15 at 21:28
4

In order to shuffle an array in a seemingly random and predetermined way, you can break the problem into two parts.

1. Generate pseudo random numbers

You could use a different PRNG, but the Xorshift is very simple, fast to both initialise and step through, and evenly distributed.

This function takes an integer as a seed value, and returns a random function that always returns the same floating point values in the range 0 to 1.

const xor = seed => {
  const baseSeeds = [123456789, 362436069, 521288629, 88675123]

  let [x, y, z, w] = baseSeeds

  const random = () => {
    const t = x ^ (x << 11)
    ;[x, y, z] = [y, z, w]
    w = w ^ (w >> 19) ^ (t ^ (t >> 8))
    return w / 0x7fffffff
  }

  ;[x, y, z, w] = baseSeeds.map(i => i + seed)
  ;[x, y, z, w] = [0, 0, 0, 0].map(() => Math.round(random() * 1e16))

  return random
}

2. Shuffle using configurable random function

The Fisher Yates shuffle is an efficient shuffle algorithm with even distribution.

const shuffle = (array, random = Math.random) => {
  let m = array.length
  let t
  let i

  while (m) {
    i = Math.floor(random() * m--)
    t = array[m]
    array[m] = array[i]
    array[i] = t
  }

  return array
}

Putting it together

// passing an xor with the same seed produces same order of output array
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(1))) // [ 3, 4, 2, 6, 7, 1, 8, 9, 5 ]
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(1))) // [ 3, 4, 2, 6, 7, 1, 8, 9, 5 ]

// changing the seed passed to the xor function changes the output
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(2))) // [ 4, 2, 6, 9, 7, 3, 8, 1, 5 ]
Billy Moon
  • 57,113
  • 24
  • 136
  • 237