Created
October 19, 2015 20:39
-
-
Save JustinMorgan/4b630446a43f28eb5559 to your computer and use it in GitHub Desktop.
Shuffle Visualizer: Visual sanity-check for randomness in deck-shuffling algorithms (C#/ASP.NET WebForms version)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TestShuffle.aspx.cs" Inherits="TestShuffle" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head runat="server"> | |
<title>Test shuffle</title> | |
<style> | |
body { | |
font-family:Verdana; | |
font-size:9px; | |
} | |
td { | |
font-size:1px; | |
width:12px; | |
height:12px; | |
} | |
</style> | |
</head> | |
<body> | |
<form id="form1" runat="server"> | |
<div> | |
<asp:Literal ID="ltGraph" runat="server"></asp:Literal> | |
</div> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
using System.Web.UI; | |
using System.Web.UI.WebControls; | |
using System.Text; | |
using System.Diagnostics; | |
public partial class TestShuffle : System.Web.UI.Page | |
{ | |
protected int[] initDeck() | |
{ | |
int[] deck = new int[52]; | |
for (int i = 0; i < 52; i++) | |
deck[i] = i; | |
return deck; | |
} | |
protected void test(int[] deck, Action<int[]> shuffle) | |
{ | |
shuffle(deck); | |
//error checking: check for illegal cards | |
var outOfBounds = (from card in deck | |
where card < 0 || card > 51 | |
select card).Distinct(); | |
if (outOfBounds.Count() > 0) | |
{ | |
string errors = string.Join(", ", outOfBounds.Select(r => r.ToString()).ToArray()); | |
throw new Exception("Deck contains illegal cards: " + errors); | |
} | |
//error checking: check for duplicates | |
var failCount = (from card in deck | |
where deck.Count(c => c == card) != 1 | |
orderby card | |
select card).Distinct(); | |
if (failCount.Count() > 0) | |
{ | |
string errors = string.Join(", ", failCount.Select(r => r.ToString()).ToArray()); | |
throw new Exception("Deck doesn't contain exactly 1 of each card: " + errors); | |
} | |
} | |
int[,] hits { get; set; } | |
void initHits() | |
{ | |
hits = new int[52, 52]; | |
for (int i = 0; i < 52; i++) | |
{ | |
for (int j = 0; j < 52; j++) | |
{ | |
hits[i, j] = 0; | |
} | |
} | |
} | |
void plotHits(int[] deck) | |
{ | |
for (int i = 0; i < deck.Length; i++) | |
{ | |
hits[i, deck[i]]++; | |
} | |
} | |
string hitsTable(bool grayscale = false) | |
{ | |
StringBuilder sbTop = new StringBuilder("<table border='0' cellpadding='0' cellspacing='0'><tr><th> </th>"); | |
StringBuilder sbRows = new StringBuilder(); | |
for (int i = 0; i < 52; i++) | |
{ | |
sbTop.AppendFormat("<th>{0}</th>", i); | |
sbRows.AppendFormat("<tr><th>{0}</th>", i); | |
for (int j = 0; j < 52; j++) | |
{ | |
if (grayscale) | |
{ | |
var amplitude = hits[i, j] % 256; | |
sbRows.AppendFormat("<td style='background-color:rgb({0},{0},{0});'> </td>", amplitude); | |
} | |
else | |
{ | |
var amplitude = (int)Math.Pow(hits[i, j], 3); | |
sbRows.AppendFormat("<td style='background-color:#{0:x6};'> </td>", amplitude % 0x1000000); | |
} | |
} | |
sbRows.AppendLine("</tr>"); | |
} | |
sbTop.AppendLine("</tr>"); | |
return sbTop.AppendLine(sbRows.ToString()).AppendLine("</table>").ToString(); | |
} | |
protected void Page_Load(object sender, EventArgs e) | |
{ | |
StringBuilder sb = new StringBuilder(); | |
var methods = new Action<int[]>[] { Shuffle.shuffle /*, Shuffle.shuffle2, etc. */ }; | |
int reps = 19000; | |
foreach (Action<int[]> method in methods) | |
{ | |
initHits(); | |
for (int i = 0; i < reps; i++) | |
{ | |
var deck = initDeck(); | |
test(deck, method); | |
plotHits(deck); | |
} | |
sb.AppendFormat(@"<div style='float:left;padding-left:10px;'><h2>{0}:</h2> {1}<br/></div>", method.Method.Name, hitsTable(true)); | |
} | |
ltGraph.Text = sb.ToString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment