payoffs and playing sim

This commit is contained in:
Nicky Case 2017-06-28 16:28:15 -04:00
parent e8c18fdbe9
commit f918fe2275
12 changed files with 312 additions and 45 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/sandbox_incdec.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

@ -129,6 +129,12 @@ body{
background: url(../assets/sandbox_tabs.png);
width:500px; height:470px;
background-size: auto 100%;
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none;
cursor: default;
}
#sandbox_tabs > div{
position: absolute;
@ -144,6 +150,33 @@ body{
left: 33px;
top: 80px;
}
.incdec{
width: 0; height: 0;
position: absolute;
}
.incdec > div{
position: absolute;
}
.incdec > .incdec_num{
width:50px; height:50px;
font-size: 25px;
text-align: center;
top: -16px;
left: -25px;
cursor: default;
}
.incdec > .incdec_control{
left:-10px;
width:20px; height:20px;
background: url(../assets/sandbox_incdec.png);
background-size: auto 100%;
cursor: pointer;
}
.incdec > .incdec_control[arrow=up]{ top:-35px; background-position:0px 0px; }
.incdec > .incdec_control[arrow=up]:hover{ background-position:-20px 0px; }
.incdec > .incdec_control[arrow=down]{ bottom:-35px; background-position:-40px 0px; }
.incdec > .incdec_control[arrow=down]:hover{ background-position:-60px 0px; }
/*************************/
/***** SLIDE SELECT ******/

View file

@ -22,7 +22,7 @@
<script src="js/lib/pixi.min.js"></script>
<script>var createjs = window;</script>
<script src="js/lib/tweenjs-0.6.2.min.js"></script>
<script>Ticker.framerate=60;</script>
<script>Ticker.framerate=60; Ticker.paused=true;</script>
<!-- Core Engine -->
<script src="js/core/Loader.js"></script>
@ -31,6 +31,7 @@
<script src="js/core/Button.js"></script>
<script src="js/core/TextBox.js"></script>
<script src="js/core/Words.js"></script>
<script src="js/core/IncDecNumber.js"></script>
<!-- Simulations -->
<script src="js/sims/PD.js"></script>

View file

@ -25,9 +25,12 @@ function Button(config){
button.style.left = config.x+"px";
button.style.top = config.y+"px";
config.upperCase = (config.upperCase===undefined) ? true : config.upperCase;
var words = Words.get(config.text_id);
self.setText = function(text_id){
var words = Words.get(text_id);
if(config.upperCase) words=words.toUpperCase();
text.innerHTML = words;
};
self.setText(config.text_id);
// On hover...
hitbox.onmouseover = function(){
@ -39,7 +42,10 @@ function Button(config){
// On click...
hitbox.onclick = function(){
if(self.active) publish(config.message);
if(self.active){
if(config.onclick) config.onclick();
if(config.message) publish(config.message);
}
};
// Activate/Deactivate

81
js/core/IncDecNumber.js Normal file
View file

@ -0,0 +1,81 @@
/*****************************
{
x:x, y:y, max:5, min:-5,
value: PD.PAYOFFS_DEFAULT[letter],
onchange: function(value){
publish("pd/editPayoffs/"+letter,[value]);
}
}
*****************************/
function IncDecNumber(config){
var self = this;
self.id = config.id;
// Properties
self.value = config.value;
// Create DOM
var dom = document.createElement("div");
dom.className = "incdec";
dom.style.left = config.x+"px";
dom.style.top = config.y+"px";
self.dom = dom;
// Number
var num = document.createElement("div");
num.className = "incdec_num";
dom.appendChild(num);
self.setValue = function(value){
// Bounds
if(value>config.max) value=config.max;
if(value<config.min) value=config.min;
// Value & UI
self.value = value;
num.innerHTML = self.value;
};
self.setValue(config.value);
// Two buttons
var up = document.createElement("div");
up.className = "incdec_control";
up.setAttribute("arrow","up");
up.onclick = function(){
self.setValue(self.value+1);
self.onchange(self.value);
};
dom.appendChild(up);
var down = document.createElement("div");
down.className = "incdec_control";
down.setAttribute("arrow","down");
down.onclick = function(){
self.setValue(self.value-1);
self.onchange(self.value);
};
dom.appendChild(down);
// On Change...
self.onchange = function(value){
config.onchange(value);
};
///////////////////////////////////////
///////////////////////////////////////
// Add...
self.add = function(INSTANT){
return _addFade(self, INSTANT);
};
// Remove...
self.remove = function(INSTANT){
return _removeFade(self, INSTANT);
};
}

View file

@ -47,15 +47,33 @@ var _removeFade = function(self, INSTANT){
};
// Make Label
var _makeLabel = function(wordID, x, y, width, height){
var _makeLabel = function(wordID, config){
var dom = document.createElement("div");
dom.className = "label";
if(x!==undefined) dom.style.left = x+"px";
if(y!==undefined) dom.style.top = y+"px";
if(width!==undefined) dom.style.width = width+"px";
if(height!==undefined) dom.style.height = height+"px";
dom.innerHTML = Words.get(wordID);
config = config || {};
if(config.x!==undefined) dom.style.left = config.x+"px";
if(config.y!==undefined) dom.style.top = config.y+"px";
if(config.w!==undefined) dom.style.width = config.w+"px";
if(config.h!==undefined) dom.style.height = config.h+"px";
if(config.rotation!==undefined) dom.style.transform = "rotate("+config.rotation+"deg)";
if(config.align!==undefined) dom.style.textAlign = config.align;
if(config.color!==undefined) dom.style.color = config.color;
return dom;
};
// Tween
var Tween_get = function(target){
return Tween.get(target, {useTicks:true});
}
var _s = function(seconds){
return Math.ceil(Ticker.framerate*seconds); // converts seconds to ticks
};
/*******

View file

@ -9,7 +9,25 @@ PD.PAYOFFS_DEFAULT = {
T: 3 // temptation: you put no coin, got 3 coins anyway
};
PD.PAYOFFS = PD.PAYOFFS_DEFAULT;
PD.PAYOFFS = JSON.parse(JSON.stringify(PD.PAYOFFS_DEFAULT));
subscribe("pd/editPayoffs", function(payoffs){
PD.PAYOFFS = payoffs;
});
subscribe("pd/editPayoffs/P", function(value){ PD.PAYOFFS.P = value; });
subscribe("pd/editPayoffs/S", function(value){ PD.PAYOFFS.S = value; });
subscribe("pd/editPayoffs/R", function(value){ PD.PAYOFFS.R = value; });
subscribe("pd/editPayoffs/T", function(value){ PD.PAYOFFS.T = value; });
subscribe("pd/defaultPayoffs", function(){
PD.PAYOFFS = JSON.parse(JSON.stringify(PD.PAYOFFS_DEFAULT));
publish("pd/editPayoffs/P", [PD.PAYOFFS.P]);
publish("pd/editPayoffs/S", [PD.PAYOFFS.S]);
publish("pd/editPayoffs/R", [PD.PAYOFFS.R]);
publish("pd/editPayoffs/T", [PD.PAYOFFS.T]);
});
PD.NOISE = 0;

View file

@ -12,10 +12,29 @@ function SandboxUI(config){
// BUTTONS for playing //////////////////
/////////////////////////////////////////
var playButton = new Button({x:130, y:135, text_id:"label_play", message:"tournament/autoplay"});
var playButton = new Button({
x:130, y:135, text_id:"label_play",
onclick: function(){
if(slideshow.objects.tournament.isAutoPlaying){
publish("tournament/autoplay/stop");
}else{
publish("tournament/autoplay/start");
}
}
});
subscribe("tournament/autoplay/stop",function(){
playButton.setText("label_play");
});
subscribe("tournament/autoplay/start",function(){
playButton.setText("label_stop");
});
dom.appendChild(playButton.dom);
var stepButton = new Button({x:130, y:135+70, text_id:"label_step", message:"tournament/step"});
var stepButton = new Button({
x:130, y:135+70, text_id:"label_step", message:"tournament/step"
});
dom.appendChild(stepButton.dom);
var resetButton = new Button({x:130, y:135+70*2, text_id:"label_reset", message:"tournament/reset"});
dom.appendChild(resetButton.dom);
@ -84,13 +103,62 @@ function SandboxUI(config){
var page = pages[1];
var label = _makeLabel("sandbox_payoffs", 0, 0, 433);
page.appendChild(label);
// Labels
page.appendChild(_makeLabel("sandbox_payoffs", {x:0, y:0, w:433}));
page.appendChild(_makeLabel("label_cooperate", {x:212, y:64, rotation:45, align:"center", color:"#cccccc"}));
page.appendChild(_makeLabel("label_cooperate", {x:116, y:64, rotation:-45, align:"center", color:"#cccccc"}));
page.appendChild(_makeLabel("label_cheat", {x:309, y:137, rotation:45, align:"center", color:"#cccccc"}));
page.appendChild(_makeLabel("label_cheat", {x:70, y:137, rotation:-45, align:"center", color:"#cccccc"}));
// Inc(rement) De(crement) Numbers
// which are symmetrical, and update each other!
var numbers = [];
var _makeIncDec = function(letter,x,y){
(function(letter,x,y){
var number = new IncDecNumber({
x:x, y:y, max:5, min:-5,
value: PD.PAYOFFS_DEFAULT[letter],
onchange: function(value){
publish("pd/editPayoffs/"+letter,[value]);
}
});
subscribe("pd/editPayoffs/"+letter,function(value){
number.setValue(value);
});
page.appendChild(number.dom);
numbers.push(number);
})(letter,x,y);
};
_makeIncDec("R", 191, 127);
_makeIncDec("R", 233, 127);
_makeIncDec("T", 121, 197);
_makeIncDec("S", 161, 197);
_makeIncDec("S", 263, 197);
_makeIncDec("T", 306, 197);
_makeIncDec("P", 192, 268);
_makeIncDec("P", 232, 268);
// Reset
var resetPayoffs = new Button({x:240, y:300, text_id:"sandbox_reset_payoffs", message:"pd/defaultPayoffs"});
page.appendChild(resetPayoffs.dom);
/////////////////////////////////////////
// PAGE 2: RULES ////////////////////////
/////////////////////////////////////////
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}));
/////////////////////////////////////////
// Add & Remove Object //////////////////
/////////////////////////////////////////

View file

@ -82,7 +82,7 @@ function Tournament(config){
self.populateAgents = function(){
// Clear EVERYTHING
self.agentsContainer.removeChildren();
while(self.agents.length>0) self.agents[0].kill();
// Convert to an array
self.agents = _convertCountToArray(AGENTS);
@ -117,11 +117,7 @@ function Tournament(config){
self.createNetwork = function(){
// Clear EVERYTHING
self.connections = [];
self.networkContainer.removeChildren();
for(var i=0; i<self.agents.length; i++){
self.agents[i].clearConnections();
}
while(self.connections.length>0) self.connections[0].kill();
// Connect all of 'em
for(var i=0; i<self.agents.length; i++){
@ -129,8 +125,9 @@ function Tournament(config){
for(var j=i+1; j<self.agents.length; j++){
var playerB = self.agents[j];
var connection = new TournamentConnection({
from:playerA,
to:playerB
tournament: self,
from: playerA,
to: playerB
});
self.networkContainer.addChild(connection.graphics);
self.connections.push(connection);
@ -138,6 +135,10 @@ function Tournament(config){
}
};
self.actuallyRemoveConnection = function(connection){
var index = self.connections.indexOf(connection);
self.connections.splice(index,1);
};
///////////////////////
@ -146,10 +147,21 @@ function Tournament(config){
var AGENTS;
self.reset = function(){
// Agents & Network...
AGENTS = JSON.parse(JSON.stringify(Tournament.INITIAL_AGENTS));
self.populateAgents();
self.createNetwork();
self.isAutoPlaying = false;
// Animation...
self.STAGE = STAGE_REST;
_playIndex = 0;
_tweenTimer = 0;
// Stop autoplay!
publish("tournament/autoplay/stop");
_step = 0;
};
subscribe("tournament/reset", self.reset);
@ -184,7 +196,7 @@ function Tournament(config){
return config.strategy==badAgent.strategyName;
});
config.count--; // remove one
badAgent.kill(); // KILL
badAgent.eliminate(); // ELIMINATE
}
};
@ -249,6 +261,7 @@ function Tournament(config){
self.isAutoPlaying = false;
var _step = 0;
var _nextStep = function(){
if(self.STAGE!=STAGE_REST) return;
if(_step==0) publish("tournament/play");
if(_step==1) publish("tournament/eliminate");
if(_step==2) publish("tournament/reproduce");
@ -261,9 +274,13 @@ function Tournament(config){
if(self.isAutoPlaying) _startAutoPlay();
},500);
};
subscribe("tournament/autoplay", _startAutoPlay);
subscribe("tournament/step", function(){
var _stopAutoPlay = function(){
self.isAutoPlaying = false;
};
subscribe("tournament/autoplay/start", _startAutoPlay);
subscribe("tournament/autoplay/stop", _stopAutoPlay);
subscribe("tournament/step", function(){
publish("tournament/autoplay/stop");
_nextStep();
});
@ -272,6 +289,9 @@ function Tournament(config){
var _tweenTimer = 0;
app.ticker.add(function(delta) {
// Tick
Tween.tick();
// PLAY!
if(self.STAGE == STAGE_PLAY){
if(_playIndex>0) self.agents[_playIndex-1].dehighlightConnections();
@ -281,6 +301,7 @@ function Tournament(config){
}else{
self.playOneTournament(); // FOR REAL, NOW.
_playIndex = 0;
_tweenTimer = 0;
self.STAGE = STAGE_REST;
// slideshow.objects._b2.activate(); // activate NEXT button!
}
@ -289,7 +310,11 @@ function Tournament(config){
// ELIMINATE!
if(self.STAGE == STAGE_ELIMINATE){
self.eliminateBottom(Tournament.SELECTION);
_tweenTimer++;
if(_tweenTimer==_s(0.3)){
_tweenTimer = 0;
self.STAGE = STAGE_REST;
}
// slideshow.objects._b3.activate(); // activate NEXT button!
}
@ -366,6 +391,8 @@ function Tournament(config){
function TournamentConnection(config){
var self = this;
self.config = config;
self.tournament = config.tournament;
// Connect from & to
self.from = config.from;
@ -419,7 +446,8 @@ function TournamentConnection(config){
self.kill = function(){
if(self.IS_DEAD) return;
self.IS_DEAD = true;
if(self.graphics.parent) self.graphics.parent.removeChild(self.graphics); // remove self's graphics
self.graphics.parent.removeChild(self.graphics); // remove self's graphics
self.tournament.actuallyRemoveConnection(self);
};
};
@ -445,6 +473,9 @@ function TournamentAgent(config){
for(var i=0;i<self.connections.length;i++) self.connections[i].dehighlight();
};
self.clearConnections = function(){
for(var i=0;i<self.connections.length;i++){
self.connections[i].kill();
}
self.connections = [];
};
@ -521,31 +552,35 @@ function TournamentAgent(config){
};
self.updatePosition();
// KILL
self.kill = function(){
// ELIMINATE
self.eliminate = function(){
// KILL ALL CONNECTIONS
for(var i=0;i<self.connections.length;i++){
self.connections[i].kill();
}
// INSTA-KILL ALL CONNECTIONS
self.clearConnections();
// Tween -- DIE!
scoreText.visible = false;
Tween.get(g).to({
Tween_get(g).to({
alpha: 0,
x: g.x+Math.random()*20-10,
y: g.y+Math.random()*20-10,
rotation: Math.random()*0.5-0.25
}, 300, Ease.circOut).call(function(){
}, _s(0.3), Ease.circOut).call(self.kill);
};
// KILL (actually insta-remove)
self.kill = function(){
// Remove ANY tweens
Tween.removeTweens(g);
// NOW remove graphics.
if(self.graphics.parent) self.graphics.parent.removeChild(self.graphics);
g.parent.removeChild(g);
// AND remove self from tournament
self.tournament.actuallyRemoveAgent(self);
});
};
}

View file

@ -9,6 +9,9 @@ Start the simulation with this distribution of players:
<p id="sandbox_payoffs">
The payoffs in a one-on-one game are:
</p>
<p id="sandbox_reset_payoffs">
reset payoffs
</p>
<!--
When translating the following, keep the "[N]", with square brackets,
@ -31,7 +34,7 @@ After each tournament, eliminate the bottom [N] player &amp; reproduce the top [
</p>
<p id="sandbox_rules_3">
In a one-on-one game, there's a [N]% chance in each round that a player will make a mistake
In each round of a one-on-one game, there's a [N]% chance a player makes a mistake
</p>
@ -128,6 +131,10 @@ cheat
play
</p>
<p id="label_stop">
stop
</p>
<p id="label_step">
step
</p>