EVERYTHING

This commit is contained in:
Nicky Case 2017-06-20 07:10:41 -04:00
commit 2ddf595e38
21 changed files with 2995 additions and 0 deletions

BIN
assets/all_c.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

BIN
assets/all_d.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

BIN
assets/bun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

BIN
assets/grim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

BIN
assets/prober.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

BIN
assets/tft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

BIN
css/FuturaHandwritten.ttf Executable file

Binary file not shown.

BIN
css/paper@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

67
css/slides.css Normal file
View file

@ -0,0 +1,67 @@
body{
margin:0;
overflow: hidden;
background: url('paper@2x.png');
background-size: 100px 100px;
}
#main{
width: 100%;
height: calc(100% - 60px);
position: relative;
overflow: hidden;
}
#footer{
width: 100%;
height: 60px;
background: #222;
}
/*************************/
/******* SLIDESHOW *******/
/*************************/
#slideshow{
/*background: #bada55;*/
border: 1px solid rgba(0,0,0,0.2);
width:960px;
height:540px;
/* Center this thing */
position: absolute;
margin: auto;
top:0; left:0; right:0; bottom:0;
}
#slideshow .object{
position: absolute;
}
.fader{
-webkit-transition: opacity 0.3s ease-in-out;
-mos-transition: opacity 0.3s ease-in-out;
-ms-transition: opacity 0.3s ease-in-out;
transition: opacity 0.3s ease-in-out;
}
/*************************/
/***** SLIDE SELECT ******/
/*************************/
#select{
width:100%;
text-align: center;
padding-top: 15px;
}
#select .dot{
width: 30px;
height: 30px;
border-radius: 40px;
border: 1px solid #fff;
display: inline-block;
margin: 0 5px;
cursor: pointer;
}
#select .dot[selected]{
background: #fff;
}

56
index.html Normal file
View file

@ -0,0 +1,56 @@
<!doctype>
<html>
<head>
<title>The Evolution of Trust</title>
<link rel="stylesheet" href="css/slides.css">
</head>
<body>
<div id="main">
<div id="slideshow"></div>
</div>
<div id="footer">
<div id="select"></div>
</div>
</body>
</html>
<!-- Libraries -->
<script src="js/lib/helpers.js"></script>
<script src="js/lib/minpubsub.src.js"></script>
<script src="js/lib/q.js"></script>
<script src="js/lib/pixi.min.js"></script>
<!-- Core Engine -->
<script src="js/core/Slideshow.js"></script>
<script src="js/core/SlideSelect.js"></script>
<script src="js/core/Button.js"></script>
<script src="js/core/WordBox.js"></script>
<!-- Simulations -->
<script src="js/sims/SillyPixi.js"></script>
<script src="js/sims/TournamentSim.js"></script>
<!-- Main Code -->
<script src="js/main.js"></script>
<script>
var slideshow, slideSelect;
window.onload = function(){
// Slideshow
slideshow = new Slideshow({
dom: $("#slideshow"),
slides: slides
});
// Slide Select
slideSelect = new SlideSelect({
dom: $("#select"),
slides: slides
});
// First slide!
slideshow.nextSlide();
//slideshow.gotoSlide("intro3");
};
</script>

32
js/core/Button.js Normal file
View file

@ -0,0 +1,32 @@
function Button(config){
var self = this;
self.id = config.id;
// Create DOM
var button = document.createElement("button");
button.className = "object";
button.classList.add("fader");
self.dom = button;
// Customize DOM
button.style.left = config.x+"px";
button.style.top = config.y+"px";
button.innerHTML = config.text;
// On click...
button.onclick = function(){
publish(config.message);
};
// Add...
self.add = function(INSTANT){
return _addFade(self, INSTANT);
};
// Remove...
self.remove = function(INSTANT){
return _removeFade(self, INSTANT);
};
}

46
js/core/SlideSelect.js Normal file
View file

@ -0,0 +1,46 @@
function SlideSelect(config){
var self = this;
self.config = config;
// DOM
self.dom = config.dom;
// Slides
self.slides = config.slides;
// Create a dot, and onclick
self.addDot = function(slide){
var dot = new SlideSelectDot(slide);
self.dom.appendChild(dot.dom);
};
// Populate dots
for(var i=0; i<slides.length; i++) self.addDot(slides[i]);
}
function SlideSelectDot(slide){
var self = this;
self.slide = slide;
// DOM
self.dom = document.createElement("div");
self.dom.className = "dot";
// On Click
self.dom.onclick = function(){
publish("slideshow/goto", [slide.id]);
};
// Listen to when the slide changes
subscribe("slideshow/slideChange", function(id){
if(id==slide.id){
self.dom.setAttribute("selected","yes");
}else{
self.dom.removeAttribute("selected");
}
});
}

105
js/core/Slideshow.js Normal file
View file

@ -0,0 +1,105 @@
function Slideshow(config){
var self = this;
self.config = config;
// DOM
self.dom = config.dom;
// Slide information
self.slides = config.slides;
// Reset: INITIAL VARIABLES
self.reset = function(){
self.dom.innerHTML = "";
self.slideIndex = -1;
self.currentSlide = null;
};
self.reset();
// Go to next slide
self.nextSlide = function(INSTANT){
// Update the information
if(self.slideIndex >= self.slides.length-1) return;
self.slideIndex++;
self.currentSlide = self.slides[self.slideIndex];
// Remove whatever
var remove = self.currentSlide.remove || [];
var promisesToRemove = [];
for(var i=0; i<remove.length; i++){
var promiseToRemove = self.removeObject(remove[i], INSTANT);
if(promiseToRemove) promisesToRemove.push(promiseToRemove);
}
// After removing, add whatever
var addObjects = function(){
var add = self.currentSlide.add || [];
for(var i=0; i<add.length; i++) self.addObject(add[i], INSTANT);
};
if(INSTANT || promisesToRemove.length==0) addObjects();
else Q.all(promisesToRemove).then(addObjects);
// Send out message!
publish("slideshow/slideChange", [self.currentSlide.id]);
};
// Subscribe to "next slide" message...
subscribe("slideshow/next", function(){
self.nextSlide();
});
// FORCE go to a certain slide
self.gotoSlide = function(id){
// Go ALL the way to the past
self.reset();
// And just move all the way forward, what a hack.
// TODO: a more efficient one that looks at id's FIRST.
self.nextSlide(true);
while(self.currentSlide.id != id){
self.nextSlide(true);
}
};
// Subscribe to the "force goto" message...
subscribe("slideshow/goto", function(id){
self.gotoSlide(id);
});
// Objects!
self.objects = {};
// Add Object
self.addObject = function(objectConfig, INSTANT){
// Create object
var Classname = window[objectConfig.type];
var obj = new Classname(objectConfig);
obj.slideshow = self;
// Remember it
self.objects[objectConfig.id] = obj;
// Add it for real!
return obj.add(INSTANT); // return a possible promise
};
// Remove Object
self.removeObject = function(objectConfig, INSTANT){
// Find it...
var obj = self.objects[objectConfig.id];
// Remove from memory & DOM
delete self.objects[objectConfig.id];
return obj.remove(INSTANT); // return a possible promise
};
}

29
js/core/WordBox.js Normal file
View file

@ -0,0 +1,29 @@
function WordBox(config){
var self = this;
self.id = config.id;
// Create DOM
var words = document.createElement("div");
words.className = "object";
words.classList.add("fader");
self.dom = words;
// Customize DOM
words.style.left = config.x+"px";
words.style.top = config.y+"px";
words.style.width = config.width+"px";
words.style.height = config.height+"px";
words.innerHTML = config.text;
// Add...
self.add = function(INSTANT){
return _addFade(self, INSTANT);
};
// Remove...
self.remove = function(INSTANT){
return _removeFade(self, INSTANT);
};
}

42
js/lib/helpers.js Normal file
View file

@ -0,0 +1,42 @@
// Pi is for unwashed plebians
Math.TAU = 2*Math.PI;
// The poor man's jQuery
var $ = function(query){
return document.querySelector(query);
};
// Add & Remove INSTANTLY
var _add = function(self){
self.slideshow.dom.appendChild(self.dom);
};
var _remove = function(self){
self.slideshow.dom.removeChild(self.dom);
};
// Add & Remove... with FADE
var _addFade = function(self, INSTANT){
if(INSTANT){
_add(self);
}else{
self.dom.style.opacity = 0;
_add(self);
setTimeout(function(){
self.dom.style.opacity = 1;
},10);
}
};
var _removeFade = function(self, INSTANT){
if(INSTANT){
_remove(self);
}else{
var deferred = Q.defer();
self.dom.style.opacity = 0;
setTimeout(function(){
_remove(self);
deferred.resolve();
},300);
return deferred.promise;
}
};

95
js/lib/minpubsub.src.js Normal file
View file

@ -0,0 +1,95 @@
/*!
* MinPubSub
* Copyright(c) 2011 Daniel Lamb <daniellmb.com>
* MIT Licensed
*/
(function (context) {
var MinPubSub = {};
// the topic/subscription hash
var cache = context.c_ || {}; //check for 'c_' cache for unit testing
MinPubSub.publish = function ( /* String */ topic, /* Array? */ args) {
// summary:
// Publish some data on a named topic.
// topic: String
// The channel to publish on
// args: Array?
// The data to publish. Each array item is converted into an ordered
// arguments on the subscribed functions.
//
// example:
// Publish stuff on '/some/topic'. Anything subscribed will be called
// with a function signature like: function(a,b,c){ ... }
//
// publish('/some/topic', ['a','b','c']);
var subs = cache[topic],
len = subs ? subs.length : 0;
//can change loop or reverse array if the order matters
while (len--) {
subs[len].apply(context, args || []);
}
};
MinPubSub.subscribe = function ( /* String */ topic, /* Function */ callback) {
// summary:
// Register a callback on a named topic.
// topic: String
// The channel to subscribe to
// callback: Function
// The handler event. Anytime something is publish'ed on a
// subscribed channel, the callback will be called with the
// published array as ordered arguments.
//
// returns: Array
// A handle which can be used to unsubscribe this particular subscription.
//
// example:
// subscribe('/some/topic', function(a, b, c){ /* handle data */ });
if (!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback]; // Array
};
MinPubSub.unsubscribe = function ( /* Array */ handle, /* Function? */ callback) {
// summary:
// Disconnect a subscribed function for a topic.
// handle: Array
// The return value from a subscribe call.
// example:
// var handle = subscribe('/some/topic', function(){});
// unsubscribe(handle);
var subs = cache[callback ? handle : handle[0]],
callback = callback || handle[1],
len = subs ? subs.length : 0;
while (len--) {
if (subs[len] === callback) {
subs.splice(len, 1);
}
}
};
// UMD definition to allow for CommonJS, AMD and legacy window
if (typeof module === 'object' && module.exports) {
// CommonJS, just export
module.exports = exports = MinPubSub;
} else if (typeof define === 'function' && define.amd) {
// AMD support
define(function () {
return MinPubSub;
});
} else if (typeof context === 'object') {
// If no AMD and we are in the browser, attach to window
context.publish = MinPubSub.publish;
context.subscribe = MinPubSub.subscribe;
context.unsubscribe = MinPubSub.unsubscribe;
}
})(this.window);

21
js/lib/pixi.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2076
js/lib/q.js Normal file

File diff suppressed because it is too large Load diff

68
js/main.js Normal file
View file

@ -0,0 +1,68 @@
var slides = [
// SIM
{
id: "sim",
add:[
{id:"tournament", type:"TournamentSim", x:0, y:20}
]
},
// Intro
{
id: "intro0",
add:[
{id:"button", type:"Button", x:550, y:200, width:100, height:100, text:"NEXT SLIDE", message:"slideshow/next"},
]
},
// Intro 1
{
id: "intro1",
add:[
{id:"wordbox1", type:"WordBox", x:500, y:0, width:100, height:200, text:"foo bar foo bar foo bar"},
]
},
// Intro 2
{
id: "intro2",
add:[
{id:"wordbox2", type:"WordBox", x:500, y:100, width:100, height:200, text:"even more foo bar"},
{id:"silly", type:"SillyPixi", x:700, y:50, width:200, height:200}
]
},
// Intro 3
{
id: "intro3",
remove:[
{id:"wordbox1"},
{id:"wordbox2"}
],
add:[
{id:"wordbox3", type:"WordBox", x:500, y:0, width:100, height:200, text:"aaAAAAHHHHhhh"}
]
},
// Intro 4
{
id: "intro4",
remove:[
{id:"wordbox3"}
]
},
// Intro 5
{
id: "intro5",
remove:[
{id:"button"},
{id:"silly"}
],
add:[
{id:"the_end", type:"WordBox", x:600, y:300, width:100, height:200, text:"THE END"}
]
}
];

38
js/sims/SillyPixi.js Normal file
View file

@ -0,0 +1,38 @@
function SillyPixi(config){
var self = this;
self.id = config.id;
// APP
var app = new PIXI.Application(config.width, config.height);
self.dom = app.view;
// DOM
self.dom.className = "object";
self.dom.classList.add("fader");
self.dom.style.left = config.x+"px";
self.dom.style.top = config.y+"px";
// BUNNY
var bunny = PIXI.Sprite.fromImage("assets/bun.png")
bunny.anchor.set(0.5);
bunny.x = app.renderer.width/2;
bunny.y = app.renderer.height/2;
app.stage.addChild(bunny);
// ANIMATE
app.ticker.add(function(delta) {
bunny.rotation += 0.1 * delta;
});
// Add...
self.add = function(INSTANT){
return _addFade(self, INSTANT);
};
// Remove...
self.remove = function(INSTANT){
return _removeFade(self, INSTANT);
};
}

320
js/sims/TournamentSim.js Normal file
View file

@ -0,0 +1,320 @@
function TournamentSim(config){
var self = this;
self.id = config.id;
// APP
var app = new PIXI.Application(500, 500, {transparent:true});
self.dom = app.view;
// DOM
self.dom.className = "object";
//self.dom.classList.add("fader");
self.dom.style.left = config.x+"px";
self.dom.style.top = config.y+"px";
self.dom.style.border = "1px solid rgba(0,0,0,0.2)";
// CREATE A RING OF AGENTS
var AGENTS = [
{strategy:"all_c", count:20},
{strategy:"all_d", count:5},
{strategy:"grim", count:2},
{strategy:"tft", count:2},
];
var _convertCountToArray = function(countList){
var array = [];
for(var i=0; i<AGENTS.length; i++){
var A = AGENTS[i];
var strategy = A.strategy;
var count = A.count;
for(var j=0; j<count; j++){
array.push(strategy);
}
}
return array;
};
self.populateAgents = function(){
// Clear EVERYTHING
app.stage.removeChildren();
// Convert to an array
self.agents = _convertCountToArray(AGENTS);
// Put 'em in a ring
var count = 0;
for(var i=0; i<self.agents.length; i++){
// Position
var angle = (i/self.agents.length)*Math.TAU - Math.TAU/4;
var x = Math.cos(angle)*200 + 250;
var y = Math.sin(angle)*200 + 250;
// What kind of agent?
var strategy = self.agents[i];
var agent = new TournamentAgent({x:x, y:y, strategy:strategy});
app.stage.addChild(agent.graphics);
// Remember me!
self.agents[i] = agent;
}
};
self.populateAgents();
////////////////////////////////////
// EVOLUTION ///////////////////////
////////////////////////////////////
// Play one tournament
self.playOneTournament = function(){
PD.playOneTournament(self.agents, 10);
self.agents.sort(function(a,b){
if(a.coins==b.coins) return (Math.random()<0.5); // if equal, random
return a.coins-b.coins; // otherwise, sort as per usual
});
};
// Get rid of X worst
self.eliminateBottom = function(X){
// The worst X
var worst = self.agents.slice(0,X);
// For each one, subtract from AGENTS count, and KILL.
for(var i=0; i<worst.length; i++){
var badAgent = worst[i];
var config = AGENTS.find(function(config){
return config.strategy==badAgent.strategyName;
});
config.count--; // remove one
app.stage.removeChild(badAgent.graphics); // get rid of this // TODO: KILL?
}
};
// Reproduce the top X
self.reproduceTop = function(X){
// The top X
var best = self.agents.slice(self.agents.length-X, self.agents.length);
// For each one, add to AGENTS count
for(var i=0; i<best.length; i++){
var goodAgent = best[i];
var config = AGENTS.find(function(config){
return config.strategy==goodAgent.strategyName;
});
config.count++; // ADD one
}
// ...and REPOPULATE THE THING
self.populateAgents();
};
// HACK: ALL AT ONCE
self.ALL_AT_ONCE = function(){
self.playOneTournament();
self.eliminateBottom(5);
self.reproduceTop(5);
};
setInterval(self.ALL_AT_ONCE, 100);
// ANIMATE
/*app.ticker.add(function(delta) {
bunny.rotation += 0.1 * delta;
});*/
// Add...
self.add = function(INSTANT){
return _add(self);
};
// Remove...
self.remove = function(INSTANT){
return _remove(self);
};
}
function TournamentAgent(config){
var self = this;
self.strategyName = config.strategy;
// Number of coins
self.coins = 0;
self.addPayoff = function(payoff){
self.coins += payoff;
self.updateScore();
};
// What's the image?
var g = new PIXI.Container();
g.x = config.x;
g.y = config.y;
self.graphics = g;
// Body!
var body = PIXI.Sprite.fromImage("assets/"+self.strategyName+".png");
body.anchor.set(0.5);
g.addChild(body);
// Score!
var textStyle = new PIXI.TextStyle({
fontFamily: 'Arial',
fontSize: 16,
});
var scoreText = new PIXI.Text("", textStyle);
scoreText.anchor.x = 0.5;
scoreText.x = 0;
scoreText.y = -40;
g.addChild(scoreText);
self.updateScore = function(){
scoreText.text = self.coins;
};
self.updateScore();
// What's the play logic?
var LogicClass = window["Logic_"+self.strategyName];
self.logic = new LogicClass();
self.play = function(){
return self.logic.play();
};
self.remember = function(other){
self.logic.remember(other);
};
// Reset!
self.resetCoins = function(){
self.coins = 0; // reset coins;
self.updateScore();
}
self.resetLogic = function(){
self.logic = new LogicClass(); // reset logic
};
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
var PD = {};
PD.COOPERATE = "COOPERATE";
PD.CHEAT = "CHEAT";
PD.P = 0; // punishment: neither of you get anything
PD.S = -1; // sucker: you put in coin, other didn't.
PD.R = 2; // reward: you both put 1 coin in, both got 3 back
PD.T = 3; // temptation: you put no coin, got 3 coins anyway
PD.getPayoffs = function(move1, move2){
if(move1==PD.CHEAT && move2==PD.CHEAT) return [PD.P, PD.P]; // both punished
if(move1==PD.COOPERATE && move2==PD.CHEAT) return [PD.S, PD.T]; // sucker - temptation
if(move1==PD.CHEAT && move2==PD.COOPERATE) return [PD.T, PD.S]; // temptation - sucker
if(move1==PD.COOPERATE && move2==PD.COOPERATE) return [PD.R, PD.R]; // both rewarded
};
PD.playOneGame = function(playerA, playerB){
var A = playerA.play();
var B = playerB.play();
var payoffs = PD.getPayoffs(A,B);
playerA.remember(B);
playerB.remember(A);
playerA.addPayoff(payoffs[0]);
playerB.addPayoff(payoffs[1]);
};
PD.playRepeatedGame = function(playerA, playerB, turns){
// I've never met you before, let's pretend
playerA.resetLogic();
playerB.resetLogic();
// Play N turns
for(var i=0; i<turns; i++){
PD.playOneGame(playerA, playerB);
}
};
PD.playOneTournament = function(agents, turns){
// Reset everyone's coins
for(var i=0; i<agents.length; i++){
agents[i].resetCoins();
}
// Round robin!
for(var i=0; i<agents.length; i++){
var playerA = agents[i];
for(var j=i+1; j<agents.length; j++){
var playerB = agents[j];
PD.playRepeatedGame(playerA, playerB, turns);
}
}
};
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
function Logic_tft(){
var self = this;
var otherMove = PD.COOPERATE;
self.play = function(){
return otherMove;
};
self.remember = function(other){
otherMove = other;
};
}
function Logic_grim(){
var self = this;
var everCheatedMe = false;
self.play = function(){
if(everCheatedMe) return PD.CHEAT;
return PD.COOPERATE;
};
self.remember = function(other){
if(other==PD.CHEAT) everCheatedMe=true;
};
}
function Logic_all_d(){
var self = this;
self.play = function(){
return PD.CHEAT;
};
self.remember = function(other){
// nah
};
}
function Logic_all_c(){
var self = this;
self.play = function(){
return PD.COOPERATE;
};
self.remember = function(other){
// nah
};
}
/*
function Logic_prober(){
var self = this;
self.play = function(){
};
self.remember = function(other){
};
}
*/