Created
January 8, 2012 05:45
-
-
Save njvack/1577380 to your computer and use it in GitHub Desktop.
Pan run simulator
This file contains hidden or 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
<!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