THE WHOLE SANDBOX HELL YEAH

main
Nicky Case 7 years ago
parent f918fe2275
commit 0ff92634df

@ -13,4 +13,6 @@ MINOR SHTUFF
- Word box class less annoying
- Refactoring, ugh
- Draw: Pavlov, TF2T, Random
- a better handwritten font, with REAL bold & italics???
- a better handwritten font, with REAL bold & italics???
- rename Tit For Tat, so it FEELS nicer??? also block peeps who already know the answer... // Copycat Copykitten
- rename all of 'em, really?

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -74,6 +74,10 @@ body{
.button{
z-index: 0;
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
.button #background{
position: absolute;
@ -177,6 +181,44 @@ body{
.incdec > .incdec_control[arrow=down]{ bottom:-35px; background-position:-40px 0px; }
.incdec > .incdec_control[arrow=down]:hover{ background-position:-60px 0px; }
.slider{
position: absolute;
height: 40px;
}
.slider > div{
position: absolute;
}
.slider > .slider_bg{
top:0;
width:100%; height:30px;
background: url(../assets/slider_bg.png);
background-size: auto 100%;
}
.slider > .slider_knob{
top:0;
width:30px; height:30px;
background: url(../assets/slider_knob.png);
background-size: 100% 100%;
}
.sandbox_pop{
position: absolute;
}
.sandbox_pop>div{
position: absolute;
}
.sandbox_pop_icon{
width: 40px;
height: 40px;
background: url(../assets/sandbox_hats.png);
background-size: auto 100%;
}
.sandbox_pop_label{
font-size: 18px;
width: 150px;
top: 8px;
left: 50px;
}
/*************************/
/***** SLIDE SELECT ******/

@ -32,6 +32,7 @@
<script src="js/core/TextBox.js"></script>
<script src="js/core/Words.js"></script>
<script src="js/core/IncDecNumber.js"></script>
<script src="js/core/Slider.js"></script>
<!-- Simulations -->
<script src="js/sims/PD.js"></script>

@ -0,0 +1,113 @@
/**************************************
{
x:0, y:0, width:433,
min:1, max:25, step:1,
message: "rules/turns"
}
**************************************/
function Slider(config){
var self = this;
self.id = config.id;
// Create DOM
var dom = document.createElement("div");
dom.className = "slider";
dom.style.left = config.x+"px";
dom.style.top = config.y+"px";
dom.style.width = config.width+"px";
self.dom = dom;
// Background
var bg = document.createElement("div");
bg.className = "slider_bg";
dom.appendChild(bg);
// Knob
var knob = document.createElement("div");
knob.className = "slider_knob";
dom.appendChild(knob);
// Set value
self.value = 0;
var _paramToValue = function(param){
var value = config.min + (config.max-config.min)*param;
value = Math.round(value/config.step)*config.step;
return value;
};
var _valueToParam = function(value){
var param = (value-config.min)/(config.max-config.min); // to (0-1)
return param;
};
self.setParam = function(param){
// Bounds
var value = config.min + (config.max-config.min)*param;
value = Math.round(value/config.step)*config.step;
self.value = value;
// DOM
knob.style.left = self.value*config.width-15;
};
self.setValue = function(value){
// Set
self.value = value;
// DOM with param
var param = _valueToParam(self.value);
knob.style.left = param*(config.width-30);
};
if(config.message) subscribe(config.message, self.setValue);
// Mouse events
var _isDragging = false;
var _offsetX = 0;
var _mouseToParam = function(event){
// Mouse to Param to Value
var param = (event.clientX - _offsetX - dom.getBoundingClientRect().left - 8)/(config.width-30);
if(param<0) param=0;
if(param>1) param=1;
var value = _paramToValue(param);
// Publish these changes! (only if ACTUALLY changed)
if(self.value != value){
if(config.message) publish(config.message, [value]);
if(config.onchange) config.onchange(value);
}
};
dom.addEventListener("mousedown",function(event){
if(config.onselect) config.onselect();
_mouseToParam(event);
_isDragging = true;
_offsetX = 0;
},false);
knob.addEventListener("mousedown",function(event){
_isDragging = true;
if(config.onselect) config.onselect();
_offsetX = event.clientX - knob.getBoundingClientRect().left;
},false);
window.addEventListener("mousemove",function(event){
if(_isDragging) _mouseToParam(event);
},false);
window.addEventListener("mouseup",function(){
_isDragging = false;
},false);
////////////////////////////////////////
// Add...
self.add = function(INSTANT){
return _add(self, INSTANT);
};
// Remove...
self.remove = function(INSTANT){
return _remove(self, INSTANT);
};
}

@ -30,6 +30,9 @@ subscribe("pd/defaultPayoffs", function(){
});
PD.NOISE = 0;
subscribe("rules/noise",function(value){
PD.NOISE = value;
});
PD.getPayoffs = function(move1, move2){
var payoffs = PD.PAYOFFS;

@ -97,6 +97,193 @@ function SandboxUI(config){
// PAGE 0: POPULATION ///////////////////
/////////////////////////////////////////
var page = pages[0];
// Labels
page.appendChild(_makeLabel("sandbox_population", {x:0, y:0, w:433}));
// Create an icon, label, and slider... that all interact with each other.
var _makePopulationControl = function(x, y, peepID, defaultValue){
// DOM
var popDOM = document.createElement("div");
popDOM.className = "sandbox_pop";
popDOM.style.left = x;
popDOM.style.top = y;
page.appendChild(popDOM);
// Message
var message = "sandbox/pop/"+peepID;
// Icon
var popIcon = document.createElement("div");
popIcon.className = "sandbox_pop_icon";
popIcon.style.backgroundPosition = (-PEEP_METADATA[peepID].frame*40)+"px 0px";
popDOM.appendChild(popIcon);
// Label: Name
var popName = document.createElement("div");
popName.className = "sandbox_pop_label";
popName.innerHTML = Words.get("label_short_"+peepID).toUpperCase();
popName.style.color = PEEP_METADATA[peepID].color;
popDOM.appendChild(popName);
// Label: Amount
var popAmount = document.createElement("div");
popAmount.className = "sandbox_pop_label";
popAmount.style.textAlign = "right";
popAmount.style.color = PEEP_METADATA[peepID].color;
popDOM.appendChild(popAmount);
subscribe(message, function(value){
popAmount.innerHTML = value;
});
// Slider
(function(peepID){
var popSlider = new Slider({
x:0, y:35, width:200,
min:0, max:25, step:1,
message: message,
onselect: function(){
_anchorPopulation(peepID);
},
onchange: function(value){
_adjustPopulation(peepID, value);
}
});
popDOM.appendChild(popSlider.dom);
})(peepID);
// Default value!
publish(message, [defaultValue]);
};
var xDiff = 220;
var yDiff = 80;
var yOff = 40;
_makePopulationControl( 0, yOff+0, "tft", 5);
_makePopulationControl(xDiff, yOff+0, "all_d", 5);
_makePopulationControl( 0, yOff+yDiff, "all_c", 15);
_makePopulationControl(xDiff, yOff+yDiff, "grudge", 0);
_makePopulationControl( 0, yOff+yDiff*2, "prober", 0);
_makePopulationControl(xDiff, yOff+yDiff*2, "tf2t", 0);
_makePopulationControl( 0, yOff+yDiff*3, "pavlov", 0);
_makePopulationControl(xDiff, yOff+yDiff*3, "random", 0);
// Adjust the WHOLE population...
/******************************
Adjust by SCALING. (and in the edge case of "all zero", scale equally)
Round to integers. (if above or below 25 in total, keep adding/subtracting 1 down the line)
******************************/
var _population;
var _remainder;
var _anchoredIndex;
var _anchorPopulation = function(peepID){
// Which index should be anchored?
_anchoredIndex = Tournament.INITIAL_AGENTS.findIndex(function(config){
return config.strategy==peepID;
});
var initValue = Tournament.INITIAL_AGENTS[_anchoredIndex].count;
// SPECIAL CASE: THIS IS ALREADY FULL
if(initValue==25){
// Pretend it was 1 for all seven others, 25-7 for this.
_population = [];
for(var i=0; i<Tournament.INITIAL_AGENTS.length; i++){
if(i==_anchoredIndex){
_population.push(18);
}else{
_population.push(1);
}
}
// Remainder is 7
_remainder = 7;
}else{
// Create array of all initial agents...
_population = [];
for(var i=0; i<Tournament.INITIAL_AGENTS.length; i++){
var conf = Tournament.INITIAL_AGENTS[i];
_population.push(conf.count);
}
// Remainder sum of those NOT anchored (25-anchor.count)
_remainder = 25-initValue;
}
};
var _adjustPopulation = function(peepID, value){
// Change the anchored one
Tournament.INITIAL_AGENTS.find(function(config){
return config.strategy==peepID;
}).count = value;
// What's the scale for the rest of 'em?
var newRemainder = 25-value;
var scale = newRemainder/_remainder;
// Adjust everyone to scale, ROUNDING.
var total = 0;
for(var i=0; i<Tournament.INITIAL_AGENTS.length; i++){
// do NOT adjust anchor.
var conf = Tournament.INITIAL_AGENTS[i];
if(conf.strategy==peepID) continue;
var initCount = _population[i];
var newCount = Math.round(initCount*scale);
conf.count = newCount;
// Count total!
total += newCount;
}
total += value; // total
// Difference...
var diff = 25-total;
// If negative, remove one starting from BOTTOM, skipping anchor.
if(diff<0){
for(var i=Tournament.INITIAL_AGENTS.length-1; i>=0 && diff<0; i--){
// do NOT adjust anchor.
var conf = Tournament.INITIAL_AGENTS[i];
if(conf.strategy==peepID) continue;
conf.count--; // REMOVE
diff++; // yay
}
}
// If positive, add one starting from top, skipping anchor.
if(diff>0){
for(var i=0; i<Tournament.INITIAL_AGENTS.length && diff>0; i++){
// do NOT adjust anchor.
var conf = Tournament.INITIAL_AGENTS[i];
if(conf.strategy==peepID) continue;
conf.count++; // ADD
diff--; // yay
}
}
// NOW adjust UI
for(var i=0; i<Tournament.INITIAL_AGENTS.length; i++){
// do NOT adjust anchor.
var conf = Tournament.INITIAL_AGENTS[i];
if(conf.strategy==peepID) continue;
publish("sandbox/pop/"+conf.strategy, [conf.count]);
}
// Reset!
publish("tournament/reset");
};
/////////////////////////////////////////
// PAGE 1: PAYOFFS //////////////////////
/////////////////////////////////////////
@ -154,10 +341,56 @@ function SandboxUI(config){
var page = pages[2];
// Labels
page.appendChild(_makeLabel("sandbox_rules_1", {x:0, y:0, w:433}));
page.appendChild(_makeLabel("sandbox_rules_2", {x:0, y:100, w:433}));
page.appendChild(_makeLabel("sandbox_rules_3", {x:0, y:225, w:433}));
// Rule: Number of turns (1 to 50)
var rule_turns = _makeLabel("sandbox_rules_1", {x:0, y:0, w:433});
var slider_turns = new Slider({
x:0, y:35, width:430,
min:1, max:50, step:1,
message: "rules/turns"
});
subscribe("rules/turns",function(value){
var words = (value==1) ? Words.get("sandbox_rules_1_single") : Words.get("sandbox_rules_1"); // plural?
words = words.replace(/\[N\]/g, value+""); // replace [N] with the number value
rule_turns.innerHTML = words;
});
page.appendChild(rule_turns);
page.appendChild(slider_turns.dom);
// Rule: Eliminate/Reproduce how many? (1 to 12)
var rule_evolution = _makeLabel("sandbox_rules_2", {x:0, y:100, w:433});
var slider_evolution = new Slider({
x:0, y:165, width:430,
min:1, max:12, step:1,
message: "rules/evolution"
});
subscribe("rules/evolution",function(value){
var words = (value==1) ? Words.get("sandbox_rules_2_single") : Words.get("sandbox_rules_2"); // plural?
words = words.replace(/\[N\]/g, value+""); // replace [N] with the number value
rule_evolution.innerHTML = words;
});
page.appendChild(rule_evolution);
page.appendChild(slider_evolution.dom);
// Rule: Noise (0% to 50%)
var rule_noise = _makeLabel("sandbox_rules_3", {x:0, y:225, w:433});
var slider_noise = new Slider({
x:0, y:290, width:430,
min:0.00, max:0.50, step:0.01,
message: "rules/noise"
});
subscribe("rules/noise",function(value){
value = Math.round(value*100);
var words = Words.get("sandbox_rules_3");
words = words.replace(/\[N\]/g, value+""); // replace [N] with the number value
rule_noise.innerHTML = words;
});
page.appendChild(rule_noise);
page.appendChild(slider_noise.dom);
// DEFAULTS
publish("rules/turns", [10]);
publish("rules/evolution", [5]);
publish("rules/noise", [0.05]);
/////////////////////////////////////////
// Add & Remove Object //////////////////

@ -1,22 +1,23 @@
Tournament.SELECTION = 5;
subscribe("rules/evolution",function(value){
Tournament.SELECTION = value;
});
Tournament.NUM_TURNS = 10;
subscribe("rules/turns",function(value){
Tournament.NUM_TURNS = value;
});
// CREATE A RING OF AGENTS
/*Tournament.AGENTS = [
{strategy:"all_c", count:15},
{strategy:"all_d", count:5},
{strategy:"grudge", count:0},
{strategy:"tft", count:5},
];*/
Tournament.INITIAL_AGENTS = [
{strategy:"all_c", count:15},
{strategy:"all_d", count:5},
{strategy:"tft", count:5},
//{strategy:"grudge", count:3},
//{strategy:"prober", count:6},
//{strategy:"tf2t", count:8},
//{strategy:"pavlov", count:3},
//{strategy:"random", count:3}
{strategy:"all_d", count:5},
{strategy:"all_c", count:15},
{strategy:"grudge", count:0},
{strategy:"prober", count:0},
{strategy:"tf2t", count:0},
{strategy:"pavlov", count:0},
{strategy:"random", count:0}
];
// OH THAT'S SO COOL. Mostly C: Pavlov wins, Mostly D: tit for two tats wins (with 5% mistake!)
@ -29,15 +30,15 @@ Tournament.INITIAL_AGENTS = [
Loader.addToManifest(Loader.manifest,{
tournament_peep: "assets/tournament_peep.json"
});
var PEEP_GRAPHICS = {
tft: {frame:0},
all_d: {frame:1},
all_c: {frame:2},
grudge: {frame:3},
prober: {frame:4},
tf2t: {frame:5},
pavlov: {frame:6},
random: {frame:7}
var PEEP_METADATA = {
tft: {frame:0, color:"#4089DD"},
all_d: {frame:1, color:"#52537F"},
all_c: {frame:2, color:"#FF75FF"},
grudge: {frame:3, color:"#C4A401"},
prober: {frame:4, color:"#CC984C"},
tf2t: {frame:5, color:"#88A8CE"},
pavlov: {frame:6, color:"#86C448"},
random: {frame:7, color:"#FF5E5E"}
};
function Tournament(config){
@ -492,7 +493,7 @@ function TournamentAgent(config){
// Body!
var body = _makeMovieClip("tournament_peep");
body.gotoAndStop(PEEP_GRAPHICS[config.strategy].frame);
body.gotoAndStop(PEEP_METADATA[config.strategy].frame);
body.scale.set(0.5);
body.anchor.x = 0.5;
body.anchor.y = 0.75;
@ -513,10 +514,7 @@ function TournamentAgent(config){
};
self.updateScore();
scoreText.visible = false;
/*subscribe("tournament/play",function(){
scoreText.visible = false;
});*/
subscribe("tournament/reproduce",function(){
var _handle = subscribe("tournament/reproduce",function(){
scoreText.visible = false;
});
@ -581,6 +579,9 @@ function TournamentAgent(config){
// AND remove self from tournament
self.tournament.actuallyRemoveAgent(self);
// Unsub
unsubscribe(_handle);
};
}

@ -3,7 +3,7 @@
<!-- - - - - - - - - - - - - - - - - -->
<p id="sandbox_population">
Start the simulation with this distribution of players:
Start off with this distribution of players:
</p>
<p id="sandbox_payoffs">
@ -45,6 +45,9 @@ In each round of a one-on-one game, there's a [N]% chance a player makes a mista
<p id="label_tft">
Tit For Tat
</p>
<p id="label_short_tft">
copycat
</p>
<p id="desc_tft">
I Cooperate on the first round.
Then, I just do whatever you did the last round.
@ -55,6 +58,9 @@ but if you Cooperate, I'll forgive you immediately!
<p id="label_all_d">
Always Cheat
</p>
<p id="label_short_all_d">
sinner
</p>
<p id="desc_all_d">
Ain't I a stinker?
</p>
@ -62,6 +68,9 @@ Ain't I a stinker?
<p id="label_all_c">
Always Cooperate
</p>
<p id="label_short_all_c">
saint
</p>
<p id="desc_all_c">
💖 💖 💖
</p>
@ -69,6 +78,9 @@ Always Cooperate
<p id="label_grudge">
Grudger
</p>
<p id="label_short_grudge">
grudger
</p>
<p id="desc_grudge">
I'll always Cooperate... until you Cheat me even once.
Then, I'll <i>always</i> Cheat you back. NO FORGIVENESS.
@ -77,6 +89,9 @@ Then, I'll <i>always</i> Cheat you back. NO FORGIVENESS.
<p id="label_prober">
Prober
</p>
<p id="label_short_prober">
detective
</p>
<p id="desc_prober">
First: I analyze you.
I start: Cooperate, Cheat, Cooperate, Cooperate.
@ -88,6 +103,9 @@ My dear Watson: elementary.
<p id="label_tf2t">
Tit For Two Tats
</p>
<p id="label_short_tf2t">
copykitten
</p>
<p id="desc_tf2t">
I Cooperate on the first round.
After that, if you Cheat me... well, I'll forgive you once.
@ -98,6 +116,9 @@ However, if you Cheat me twice in a row, <i>then</i> I'll Cheat back.
<p id="label_pavlov">
Pavlov's Dog
</p>
<p id="label_short_pavlov">
pavlov
</p>
<p id="desc_pavlov">
I Cooperate on the first round.
After that, if you Cooperated in the previous round,
@ -109,6 +130,9 @@ I'll do the <i>opposite</i> of what I did last time (even if it was a mistake).
<p id="label_random">
Lol So Random
</p>
<p id="label_short_random">
random
</p>
<p id="desc_random">
monkey tacos! robot ninja bacon pirate!
i randomly play Cheat or Cooperate coz lol i'm so random

Loading…
Cancel
Save