Skip to content

Instantly share code, notes, and snippets.

@njvack
Created January 8, 2012 05:45
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 njvack/1577380 to your computer and use it in GitHub Desktop.
Save njvack/1577380 to your computer and use it in GitHub Desktop.
Pan run simulator
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Pan math fun</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"> </script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"> </script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"> </script>
<script type="text/javascript">
function solve_inv(opts) {
return(opts.goal_success_levels/(opts.scale*(opts.start+opts.goal_success_levels)));
}
function solve_logistic(opts) {
return 1/(opts.scale*(1+Math.exp(-(
opts.start+
opts.goal_skip_weight*opts.goal_skip_levels+
opts.goal_success_weight*opts.goal_success_levels))));
}
var model_map = {
'inv': solve_inv,
'logistic': solve_logistic
};
function log_start_prob_to_start_val(prob, scale) {
return -Math.log((1/(prob*scale))-1);
}
function inv_start_prob_to_b(prob, scale) {
return (1/(prob*scale))-1;
}
var start_val_map = {
'inv': inv_start_prob_to_b,
'logistic': log_start_prob_to_start_val
}
function simulate_pan(start_prob, start_fx, lev_fx, success_weight, skip_weight, success_prob, psf, rune_count, max_levels) {
var rune_findings = [];
var start_val = start_fx(start_prob, psf);
var successes = 0;
var skips = 0;
for (var i = 0; i < max_levels; i++) {
var succ = Math.random() <= success_prob;
if (succ) {
successes += 1;
} else {
skips += 1;
}
var opts = {
'start': start_val,
'goal_skip_levels': skips,
'goal_skip_weight': skip_weight,
'goal_success_levels': successes,
'goal_success_weight': success_weight,
'scale': psf};
var level_prob = lev_fx(opts);
var found = Math.random() < level_prob;
if (found) {
rune_findings.push(i);
}
if (rune_findings.length >= rune_count) {
return rune_findings;
}
}
while (rune_findings.length < rune_count) {
rune_findings.push(max_levels);
}
return rune_findings;
}
function pan_monte_carlo(start_prob, start_fx, lev_fx, success_weight, skip_weight, success_prob, psf, rune_count, max_levels, iters) {
var pan_runs = [];
for (var i = 0; i < iters; i++) {
pan_runs.push(simulate_pan(start_prob, start_fx, lev_fx, success_weight, skip_weight, success_prob, psf, rune_count, max_levels));
}
return pan_runs
}
var canvas;
$(document).ready(function() {
var color_scale = d3.scale.ordinal()
.domain([0,1,2,3])
.range([
'rgb(186, 228, 188)',
'rgb(123, 204, 196)',
'rgb(67, 162, 202)',
'rgb(8, 104, 172)'])
var h = 150;
var w = 450;
canvas = d3.select('#histograms').append('svg')
.attr('width', w+'px')
.attr('height', h+'px');
$('#compute').click(function(event) {
var model = $('#model').val();
var start_fx = start_val_map[model];
var model_fx = model_map[model];
console.log(model);
var start_prob = parseFloat($('#start_prob').val());
var success_weight = parseFloat($('#success_weight').val());
var skip_weight = parseFloat($('#skip_weight').val());
var success_prob = parseFloat($('#success_prob').val());
var psf = parseFloat($('#psf').val());
var start_val = start_fx(start_prob, psf);
var max_levels = parseInt($('#max_levels').val(), 10);
var iters = parseInt($('#iters').val(), 10);
var res = $('#prob_results');
res.empty();
var labels = $('<tr></tr>').appendTo(res);
labels.append("<th>level</th>");
var results = $('<tr></tr>').appendTo(res);
results.append("<th>rune probability</th>");
$('#rune_stats').empty();
var stats_header = $('<tr></tr>').appendTo($('#rune_stats'));
stats_header.append('<td>rune</td><td>mode</td>');
stats_header.append('<td>min</td><td>25%</td><td>50%</td><td>75%</td><td>max</td>');
// let's run our monte carlo!
var rune_count = 4;
var run_results = pan_monte_carlo(start_prob, start_fx, model_fx,
success_weight, skip_weight, success_prob, psf, rune_count, max_levels, iters);
var histograms = [];
var max_y = 0;
for (var i = 0; i < rune_count; i++) {
var vals = run_results.map(function(e) {return e[i];});
var vals_sorted = vals.slice().sort(d3.ascending);
var hist = d3.layout.histogram()
.bins(max_levels)
.range([0,max_levels])(vals);
var yvals = hist.map(function(d) {return d.y;});
var this_max_y = d3.max(yvals);
var max_y_idx = yvals.indexOf(this_max_y);
max_y = d3.max([max_y, this_max_y]);
histograms.push(hist);
var stats_row = $('<tr></tr>').appendTo($('#rune_stats'));
stats_row.append('<td>'+(i+1)+'</td><td>'+hist[max_y_idx].x+'</td>');
stats_row.append('<td>'+d3.quantile(vals_sorted, 0)+'</td>');
stats_row.append('<td>'+d3.quantile(vals_sorted, 0.25)+'</td>');
stats_row.append('<td>'+d3.quantile(vals_sorted, 0.5)+'</td>');
stats_row.append('<td>'+d3.quantile(vals_sorted, 0.75)+'</td>');
stats_row.append('<td>'+d3.quantile(vals_sorted, 1)+'</td>');
}
// And draw it.
canvas.selectAll('g').remove();
var x_scale = d3.scale.linear()
.range([0,w])
.domain([0, max_levels]);
var bar_width = w/max_levels;
var y_scale = d3.scale.linear()
.range([0, h])
.domain([0, max_y]);
for (var i = histograms.length-1; i >= 0; i--) {
var hist = histograms[i];
var chp = canvas.append('g');
chp.selectAll('rect')
.data(hist)
.enter().append('rect')
.attr('x', function(d) { return x_scale(d.x);})
.attr('width', bar_width)
.attr('y', function(d) { return h-y_scale(d.y); })
.attr('height', function(d) {return y_scale(d.y); })
.style('fill', color_scale(i))
.style('opacity', 0.7);
}
})
});
</script>
</head>
<body>
<div>
<label for="model">Model</label>
<select id="model" name="model">
<option value="logistic">logistic: 1/m(e^-(b0+b1*succ+b2*skip))</option>
<option value="inv">inverse: x/m(x+b0)</option>
</select>
</div>
<div>
<label for="start_prob">Start probability (drives b0)</label>
<input name="start_prob" id="start_prob" value="0.015" />
</div>
<div>
<label for="success_weight">Goal success weight (b1)</label>
<input name="success_weight" id="success_weight" value="0.1" />
(Ignored for inverse model)
</div>
<div>
<label for="skip_weight">Goal skip weight (b2)</label>
<input name="skip_weight" id="skip_weight" value="0.1" />
(Ignored for inverse model)
</div>
<div>
<label for="success_prob">Success probability</label>
<input name="success_prob" id="success_prob" value="1" />
(Use 1 for inverse model)
</div>
<div>
<label for="psf">Scale factor (m)</label>
<input name="psf" id="psf" value="2" />
</div>
<div>
<label for="max_levels">Max levels</label>
<input name="max_levels" id="max_levels" value="150" />
</div>
<div>
<label for="iters">Simulation iterations</label>
<input name="iters" id="iters" value="10000" />
</div>
<div>
<input id="compute" type="button" value="Compute!" />
</div>
<div id="histograms">
</div>
<table id="rune_stats">
</table>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment