Skip to content

Instantly share code, notes, and snippets.

@JustinMorgan
Created October 19, 2015 20:39
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/4b630446a43f28eb5559 to your computer and use it in GitHub Desktop.
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)
<%@ 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>
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>&nbsp;</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});'>&nbsp;</td>", amplitude);
}
else
{
var amplitude = (int)Math.Pow(hits[i, j], 3);
sbRows.AppendFormat("<td style='background-color:#{0:x6};'>&nbsp;</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