Skip to content

Instantly share code, notes, and snippets.

@JustinMorgan
Last active February 22, 2016 19:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JustinMorgan/3989752bdfd579291cca to your computer and use it in GitHub Desktop.
Save JustinMorgan/3989752bdfd579291cca to your computer and use it in GitHub Desktop.
Shuffle Visualizer: Visual sanity-check for randomness in deck-shuffling algorithms (JS and CoffeeScript versions)
#Usage:
#1. Declare your shuffle() function. No parameters necessary, since `deck` is a global variable.
#2. Run `test()`.
deckSize = 52
deck = [0...deckSize]
rand = (min = 0, max = deckSize) ->
Math.floor(Math.random() * (max - min)) + min
swap = (a, b) ->
if a isnt b then [deck[a], deck[b]] = [deck[b], deck[a]]
testCount = 10000
log2 = (x) ->
Math.log(x) / Math.LN2
test = (document = document) ->
plot do () ->
for [1..testCount]
deck = [0...deckSize]
do shuffle
for i in [0...deckSize]
if deck.indexOf(i) is -1 then throw "#{i} is not in the deck"
if deck.length isnt deckSize then throw "deck length is #{deck.length}; should be #{deckSize}"
deck
plot = (runs) ->
values = ((0 for [0...deckSize]) for [0...deckSize])
for run in runs
for card, index in run
values[card][index]++
#rescale the values to the 0..255 range
maxValue = values.reduce((prev, arr) ->
Math.max(prev, Math.max.apply(null, arr))
, 0)
proportion = 255 / maxValue
#create a table
table = document.createElement "table"
document.getElementsByTagName("body")[0].appendChild(table)
#top row
row = document.createElement "tr"
table.appendChild row
#column headers
for k in [0...deckSize]
th = document.createElement "th"
th.appendChild document.createTextNode k
row.appendChild th
for i in [0...deckSize]
#current row
row = document.createElement "tr"
table.appendChild row
#row header
th = document.createElement "th"
th.appendChild document.createTextNode i
row.appendChild th
for v in values[i]
#calculate brigtness value
j = Math.floor(v * proportion)
#add table cell
td = document.createElement 'td'
td.style.cssText = "background-color:rgb(#{j},#{j},#{j});"
row.appendChild td
//Usage:
//1. Declare your shuffle() function. No parameters necessary, since `deck` is a global variable.
//2. Run `test()`.
var deck, deckSize, l, log2, plot, rand, results, swap, test, testCount;
deckSize = 52;
deck = (function() {
results = [];
for (var l = 0; 0 <= deckSize ? l < deckSize : l > deckSize; 0 <= deckSize ? l++ : l--){ results.push(l); }
return results;
}).apply(this);
rand = function(min, max) {
if (min == null) {
min = 0;
}
if (max == null) {
max = deckSize;
}
return Math.floor(Math.random() * (max - min)) + min;
};
swap = function(a, b) {
var ref;
if (a !== b) {
return ref = [deck[b], deck[a]], deck[a] = ref[0], deck[b] = ref[1], ref;
}
};
testCount = 10000;
log2 = function(x) {
return Math.log(x) / Math.LN2;
};
test = function(document) {
if (document == null) {
document = document;
}
return plot((function() {
var i, m, n, o, ref, ref1, results1, results2;
results1 = [];
for (m = 1, ref = testCount; 1 <= ref ? m <= ref : m >= ref; 1 <= ref ? m++ : m--) {
deck = (function() {
results2 = [];
for (var n = 0; 0 <= deckSize ? n < deckSize : n > deckSize; 0 <= deckSize ? n++ : n--){ results2.push(n); }
return results2;
}).apply(this);
shuffle();
for (i = o = 0, ref1 = deckSize; 0 <= ref1 ? o < ref1 : o > ref1; i = 0 <= ref1 ? ++o : --o) {
if (deck.indexOf(i) === -1) {
throw i + " is not in the deck";
}
if (deck.length !== deckSize) {
throw "deck length is " + deck.length + "; should be " + deckSize;
}
}
results1.push(deck);
}
return results1;
})());
};
plot = function(runs) {
var card, i, index, j, k, len, len1, m, maxValue, n, o, p, proportion, ref, ref1, results1, row, run, table, td, th, v, values;
values = (function() {
var m, ref, results1;
results1 = [];
for (m = 0, ref = deckSize; 0 <= ref ? m < ref : m > ref; 0 <= ref ? m++ : m--) {
results1.push((function() {
var n, ref1, results2;
results2 = [];
for (n = 0, ref1 = deckSize; 0 <= ref1 ? n < ref1 : n > ref1; 0 <= ref1 ? n++ : n--) {
results2.push(0);
}
return results2;
})());
}
return results1;
})();
for (m = 0, len = runs.length; m < len; m++) {
run = runs[m];
for (index = n = 0, len1 = run.length; n < len1; index = ++n) {
card = run[index];
values[card][index]++;
}
}
maxValue = values.reduce(function(prev, arr) {
return Math.max(prev, Math.max.apply(null, arr));
}, 0);
proportion = 255 / maxValue;
table = document.createElement("table");
document.getElementsByTagName("body")[0].appendChild(table);
row = document.createElement("tr");
table.appendChild(row);
for (k = o = 0, ref = deckSize; 0 <= ref ? o < ref : o > ref; k = 0 <= ref ? ++o : --o) {
th = document.createElement("th");
th.appendChild(document.createTextNode(k));
row.appendChild(th);
}
results1 = [];
for (i = p = 0, ref1 = deckSize; 0 <= ref1 ? p < ref1 : p > ref1; i = 0 <= ref1 ? ++p : --p) {
row = document.createElement("tr");
table.appendChild(row);
th = document.createElement("th");
th.appendChild(document.createTextNode(i));
row.appendChild(th);
results1.push((function() {
var len2, q, ref2, results2;
ref2 = values[i];
results2 = [];
for (q = 0, len2 = ref2.length; q < len2; q++) {
v = ref2[q];
j = Math.floor(v * proportion);
td = document.createElement('td');
td.style.cssText = "background-color:rgb(" + j + "," + j + "," + j + ");";
results2.push(row.appendChild(td));
}
return results2;
})());
}
return results1;
};
// ---
// generated by coffee-script 1.9.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment