MOVE TO TOP
|
@ -1,563 +0,0 @@
|
|||
Math.TAU = Math.PI*2;
|
||||
|
||||
var canvas = document.getElementById("canvas");// || document.createElement("canvas");
|
||||
canvas.style.cursor = "none";
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
var peeps = [];
|
||||
var connections = [];
|
||||
var drawing = new Drawing();
|
||||
var cursor = new Cursor();
|
||||
|
||||
var clearNetwork = function(){
|
||||
peeps = [];
|
||||
connections = [];
|
||||
};
|
||||
var loadNetwork = function(data){
|
||||
|
||||
// Clear!
|
||||
clearNetwork();
|
||||
|
||||
// Peeps
|
||||
data.peeps.forEach(function(p){
|
||||
addPeep(p[0], p[1], p[2]);
|
||||
});
|
||||
|
||||
// Connections
|
||||
data.connections.forEach(function(c){
|
||||
var from = peeps[c[0]];
|
||||
var to = peeps[c[1]];
|
||||
addConnection(from, to);
|
||||
});
|
||||
|
||||
}
|
||||
var saveNetwork = function(){
|
||||
var data = {
|
||||
peeps: [],
|
||||
connections: []
|
||||
};
|
||||
peeps.forEach(function(peep){
|
||||
data.peeps.push([peep.x, peep.y, peep.state]);
|
||||
});
|
||||
connections.forEach(function(c){
|
||||
var fromIndex = peeps.indexOf(c.from);
|
||||
var toIndex = peeps.indexOf(c.to);
|
||||
data.connections.push([fromIndex, toIndex]);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
var DRAW_STATE = 0; // 0-nothing | 1-connecting | 2-erasing
|
||||
var DRAW_CONNECT_FROM = null;
|
||||
var CONNECT_FROM_BUFFER = 15;
|
||||
var CONNECT_TO_BUFFER = 25;
|
||||
|
||||
function update(){
|
||||
|
||||
// Mouse logic...
|
||||
if(Mouse.justPressed && DRAW_STATE===0){
|
||||
|
||||
// Clicked on a peep?
|
||||
var peepClicked = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
|
||||
if(peepClicked){
|
||||
DRAW_CONNECT_FROM = peepClicked;
|
||||
DRAW_STATE = 1; // START CONNECTING
|
||||
drawing.startConnect(peepClicked); // Drawing logic
|
||||
}else{
|
||||
DRAW_STATE = 2; // START ERASING
|
||||
}
|
||||
|
||||
}
|
||||
if(DRAW_STATE==2){ // ERASE
|
||||
|
||||
// Intersect with any connections?
|
||||
var line = [Mouse.lastX, Mouse.lastY, Mouse.x, Mouse.y];
|
||||
for(var i=connections.length-1; i>=0; i--){ // going BACKWARDS coz killing
|
||||
var c = connections[i];
|
||||
if(c.hitTest(line)) connections.splice(i,1);
|
||||
}
|
||||
drawing.startErase(); // Drawing logic
|
||||
|
||||
}
|
||||
if(Mouse.justReleased && DRAW_STATE!==0){
|
||||
|
||||
// Connecting peeps, and released on a peep?
|
||||
if(DRAW_STATE==1){
|
||||
var peepReleased = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
|
||||
if(peepReleased){ // connect 'em!
|
||||
addConnection(DRAW_CONNECT_FROM, peepReleased);
|
||||
DRAW_CONNECT_FROM = null;
|
||||
}
|
||||
drawing.endConnect(); // Drawing logic
|
||||
}else if(DRAW_STATE==2){
|
||||
drawing.endErase(); // Drawing logic
|
||||
}
|
||||
DRAW_STATE = 0; // back to normal
|
||||
|
||||
}
|
||||
Mouse.update();
|
||||
|
||||
// Cursor Logic
|
||||
if(DRAW_STATE==0){
|
||||
var peepHovered = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
|
||||
if(peepHovered){
|
||||
cursor.setMode(Cursor.CONNECT);
|
||||
}else{
|
||||
cursor.setMode(Cursor.NORMAL);
|
||||
}
|
||||
}
|
||||
if(DRAW_STATE==1){
|
||||
cursor.setMode(Cursor.CONNECT);
|
||||
}
|
||||
if(DRAW_STATE==2){
|
||||
cursor.setMode(Cursor.ERASE);
|
||||
}
|
||||
|
||||
// Update Logic
|
||||
connections.forEach(function(connection){
|
||||
connection.update(ctx);
|
||||
});
|
||||
drawing.update();
|
||||
peeps.forEach(function(peep){
|
||||
peep.update();
|
||||
});
|
||||
cursor.update();
|
||||
|
||||
// Draw Logic
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.save();
|
||||
ctx.scale(2,2);
|
||||
_preUpdate();
|
||||
//ctx.translate(0,100);
|
||||
|
||||
connections.forEach(function(connection){
|
||||
connection.draw(ctx);
|
||||
});
|
||||
drawing.draw(ctx);
|
||||
peeps.forEach(function(peep){
|
||||
peep.draw(ctx);
|
||||
});
|
||||
cursor.draw(ctx);
|
||||
|
||||
_onUpdate();
|
||||
ctx.restore();
|
||||
|
||||
// RAF
|
||||
requestAnimationFrame(update);
|
||||
|
||||
}
|
||||
function _preUpdate(){
|
||||
// TO IMPLEMENT
|
||||
}
|
||||
function _onUpdate(){
|
||||
// TO IMPLEMENT
|
||||
}
|
||||
|
||||
function Peep(config){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Properties
|
||||
self.x = config.x;
|
||||
self.y = config.y;
|
||||
self.state = config.state;
|
||||
|
||||
// Update:
|
||||
self.numFriends = 0;
|
||||
self.numInfectedFriends = 0;
|
||||
self.faceX = 0;
|
||||
self.faceY = 0;
|
||||
self.faceBlink = 0;
|
||||
self.isMajority = false;
|
||||
var _faceFollow = 0.75+(Math.random()*0.1);
|
||||
self.update = function(){
|
||||
|
||||
// Face position!
|
||||
var faceVector = {
|
||||
x: (Mouse.x-self.x)/5,
|
||||
y: (Mouse.y-self.y)/5
|
||||
};
|
||||
faceVector.mag = Math.sqrt(faceVector.x*faceVector.x + faceVector.y*faceVector.y);
|
||||
var max_distance = 5;
|
||||
if(faceVector.mag>max_distance){
|
||||
faceVector.x = faceVector.x * (max_distance/faceVector.mag);
|
||||
faceVector.y = faceVector.y * (max_distance/faceVector.mag);
|
||||
}
|
||||
self.faceX = self.faceX*_faceFollow + faceVector.x*(1-_faceFollow);
|
||||
self.faceY = self.faceY*_faceFollow + faceVector.y*(1-_faceFollow);
|
||||
|
||||
// Blink?
|
||||
if(!self.faceBlink){
|
||||
if(Math.random()<0.002) self.faceBlink=true;
|
||||
}else{
|
||||
if(Math.random()<0.09) self.faceBlink=false;
|
||||
}
|
||||
|
||||
// Friends connected... or infected
|
||||
var friends = getConnected(self);
|
||||
self.numFriends = friends.length;
|
||||
self.numInfectedFriends = 0;
|
||||
friends.forEach(function(friend){
|
||||
if(friend.state==2) self.numInfectedFriends++;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Draw
|
||||
var radius = 20;
|
||||
var bubbleScale = 1;
|
||||
var bubbleScaleVel = 0;
|
||||
self.draw = function(ctx){
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(self.x, self.y);
|
||||
|
||||
// Circle
|
||||
ctx.fillStyle = (self.state==1) ? "#ccc" : "#dd4040"; //"#ffdf00";
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
|
||||
// Face
|
||||
ctx.save();
|
||||
ctx.translate(self.faceX, self.faceY);
|
||||
ctx.fillStyle = "rgba(0,0,0,0.5)";
|
||||
if(self.faceBlink){
|
||||
ctx.beginPath();
|
||||
ctx.rect(-14, -1, 8, 2);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.rect(6, -1, 8, 2);
|
||||
ctx.fill();
|
||||
}else{
|
||||
ctx.beginPath();
|
||||
ctx.arc(-10, -1, 3, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(10, -1, 3, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.rect(-7, 4, 14, 2);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
|
||||
// Say: Infected/Friends
|
||||
var label = self.numInfectedFriends + "/" + self.numFriends;
|
||||
ctx.font = '12px sans-serif';
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fontWeight = "bold";
|
||||
ctx.fillText(label, 0, -27);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
};
|
||||
|
||||
// Hit Test
|
||||
self.hitTest = function(x,y,buffer){
|
||||
if(buffer===undefined) buffer=0;
|
||||
var dx = self.x-x;
|
||||
var dy = self.y-y;
|
||||
var dist2 = dx*dx+dy*dy;
|
||||
var r = radius+buffer;
|
||||
return (dist2<r*r);
|
||||
};
|
||||
|
||||
}
|
||||
function _mouseOverPeep(buffer){
|
||||
var result;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.hitTest(Mouse.x, Mouse.y, buffer)) result=peep;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function addPeep(x,y, state){
|
||||
var peep = new Peep({
|
||||
x:x, y:y,
|
||||
state: state ? state : 1
|
||||
});
|
||||
peeps.push(peep);
|
||||
}
|
||||
function removePeep(peep){
|
||||
removeAllConnectedTo(peep); // remove connections first
|
||||
peeps.splice(peeps.indexOf(peep),1); // BYE peep
|
||||
}
|
||||
|
||||
function Connection(config){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Properties
|
||||
self.from = config.from;
|
||||
self.to = config.to;
|
||||
|
||||
// Update
|
||||
self.update = function(){
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
ctx.strokeStyle = "#333";
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.from.x, self.from.y);
|
||||
ctx.lineTo(self.to.x, self.to.y);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
// Hit Test with a LINE SEGMENT
|
||||
// code adapted from https://gist.github.com/Joncom/e8e8d18ebe7fe55c3894
|
||||
self.hitTest = function(line){
|
||||
|
||||
var p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y;
|
||||
p0_x = line[0];
|
||||
p0_y = line[1];
|
||||
p1_x = line[2];
|
||||
p1_y = line[3];
|
||||
p2_x = self.from.x;
|
||||
p2_y = self.from.y;
|
||||
p3_x = self.to.x;
|
||||
p3_y = self.to.y;
|
||||
|
||||
var s1_x, s1_y, s2_x, s2_y;
|
||||
s1_x = p1_x - p0_x;
|
||||
s1_y = p1_y - p0_y;
|
||||
s2_x = p3_x - p2_x;
|
||||
s2_y = p3_y - p2_y;
|
||||
var s, t;
|
||||
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
|
||||
return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
function addConnection(from, to){
|
||||
|
||||
// Don't allow connect if connecting to same...
|
||||
if(from==to) return;
|
||||
|
||||
// ...or if already exists, in either direction
|
||||
for(var i=0; i<connections.length; i++){
|
||||
var c = connections[i];
|
||||
if(c.from==from && c.to==to) return;
|
||||
if(c.from==to && c.to==from) return;
|
||||
}
|
||||
|
||||
// Otherwise, sure!
|
||||
connections.push(new Connection({
|
||||
from:from, to:to
|
||||
}));
|
||||
|
||||
}
|
||||
function getConnected(peep){
|
||||
var results = [];
|
||||
for(var i=0; i<connections.length; i++){ // in either direction
|
||||
var c = connections[i];
|
||||
if(c.from==peep) results.push(c.to);
|
||||
if(c.to==peep) results.push(c.from);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
function removeAllConnectedTo(peep){
|
||||
for(var i=connections.length-1; i>=0; i--){ // backwards index coz we're deleting
|
||||
var c = connections[i];
|
||||
if(c.from==peep || c.to==peep){ // in either direction
|
||||
connections.splice(i,1); // remove!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Drawing(){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Update!
|
||||
self.update = function(){
|
||||
|
||||
// Connection
|
||||
if(self.connectFrom){
|
||||
// Over any peeps? Connect to THAT! Else, connect to Mouse
|
||||
var peepHovered = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
|
||||
if(peepHovered==self.connectFrom) peepHovered=null; // if same, nah
|
||||
self.connectTo = peepHovered ? peepHovered : Mouse;
|
||||
}
|
||||
|
||||
// Erase
|
||||
if(self.isErasing){
|
||||
self.eraseTrail.unshift([Mouse.x,Mouse.y]); // add to start
|
||||
if(self.eraseTrail.length>10){
|
||||
self.eraseTrail.pop(); // remove from end
|
||||
}
|
||||
}else{
|
||||
self.eraseTrail.pop(); // remove from end
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Connection!
|
||||
self.connectFrom = null;
|
||||
self.connectTo = null;
|
||||
self.startConnect = function(from){
|
||||
self.connectFrom = from;
|
||||
};
|
||||
self.endConnect = function(){
|
||||
self.connectFrom = null;
|
||||
};
|
||||
|
||||
// Erase!
|
||||
self.isErasing = false;
|
||||
self.eraseTrail = [];
|
||||
self.startErase = function(){
|
||||
self.isErasing = true;
|
||||
};
|
||||
self.endErase = function(){
|
||||
self.isErasing = false;
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
|
||||
// Connecting...
|
||||
if(self.connectFrom){
|
||||
ctx.strokeStyle = "#666";
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.connectFrom.x, self.connectFrom.y);
|
||||
ctx.lineTo(self.connectTo.x, self.connectTo.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Erase
|
||||
if(self.eraseTrail.length>0){
|
||||
ctx.strokeStyle = "#dd4040";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.eraseTrail[0][0], self.eraseTrail[0][1]);
|
||||
for(var i=1; i<self.eraseTrail.length; i++){
|
||||
ctx.lineTo(self.eraseTrail[i][0], self.eraseTrail[i][1]);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
var cursorImage = new Image();
|
||||
cursorImage.src = "cursor.png";
|
||||
function Cursor(){
|
||||
var self = this;
|
||||
|
||||
// MODES
|
||||
// 0 - normal
|
||||
// 1 - hover, CAN connect
|
||||
// 2 - erase
|
||||
self.mode = 0;
|
||||
self.setMode = function(mode){
|
||||
self.mode = mode;
|
||||
};
|
||||
|
||||
// Rotate when mouse pressed
|
||||
self.rotation = 0;
|
||||
|
||||
// Update
|
||||
self.update = function(){
|
||||
var r = Mouse.pressed ? -0.2 : 0;
|
||||
self.rotation = self.rotation*0.5 + r*0.5;
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
if(!self.visible) return; // NAH
|
||||
ctx.save();
|
||||
ctx.translate(Mouse.x, Mouse.y);
|
||||
ctx.rotate(self.rotation);
|
||||
var sx;
|
||||
switch(self.mode){
|
||||
case Cursor.NORMAL: sx=0; break;
|
||||
case Cursor.CONNECT: sx=1; break;
|
||||
case Cursor.ERASE: sx=2; break;
|
||||
}
|
||||
sx = sx*100;
|
||||
ctx.drawImage(cursorImage,
|
||||
sx, 0, 100, 100,
|
||||
0, -40, 40, 40);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// HIDE OR SHOW
|
||||
self.visible = true;
|
||||
self.show = function(){
|
||||
self.visible = true;
|
||||
};
|
||||
self.hide = function(){
|
||||
self.visible = false;
|
||||
};
|
||||
|
||||
}
|
||||
Cursor.NORMAL = 0;
|
||||
Cursor.CONNECT = 1;
|
||||
Cursor.ERASE = 2;
|
||||
|
||||
/////////////////////////////
|
||||
// MOUSE ////////////////////
|
||||
/////////////////////////////
|
||||
|
||||
var Mouse = {
|
||||
x:0, y:0,
|
||||
pressed:false
|
||||
};
|
||||
Mouse.ondown = function(event){
|
||||
cursor.show();
|
||||
Mouse.pressed = true;
|
||||
Mouse.onmove(event);
|
||||
};
|
||||
Mouse.onmove = function(event){
|
||||
cursor.show();
|
||||
Mouse.x = event.offsetX;
|
||||
Mouse.y = event.offsetY;
|
||||
};
|
||||
Mouse.onup = function(event){
|
||||
Mouse.pressed = false;
|
||||
};
|
||||
Mouse.update = function(){
|
||||
|
||||
// Just pressed, or just released (one frame ago)
|
||||
Mouse.justPressed = (!Mouse.lastPressed && Mouse.pressed);
|
||||
Mouse.justReleased = (Mouse.lastPressed && !Mouse.pressed);
|
||||
|
||||
// The last frame's stuff
|
||||
Mouse.lastX = Mouse.x;
|
||||
Mouse.lastY = Mouse.y;
|
||||
Mouse.lastPressed = Mouse.pressed;
|
||||
|
||||
};
|
||||
canvas.addEventListener("mousedown", Mouse.ondown);
|
||||
canvas.addEventListener("mousemove", Mouse.onmove);
|
||||
window.addEventListener("mouseup", Mouse.onup);
|
||||
|
||||
// TOUCH.
|
||||
function _touchWrapper(callback){
|
||||
return function(event){
|
||||
var _event = {};
|
||||
_event.offsetX = event.changedTouches[0].clientX;
|
||||
_event.offsetY = event.changedTouches[0].clientY;
|
||||
event.preventDefault();
|
||||
callback(_event);
|
||||
};
|
||||
}
|
||||
canvas.addEventListener("touchstart", _touchWrapper(Mouse.ondown), false);
|
||||
canvas.addEventListener("touchmove", _touchWrapper(Mouse.onmove), false);
|
||||
document.body.addEventListener("touchend", function(){
|
||||
cursor.hide();
|
||||
Mouse.onup();
|
||||
}, false);
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
|
@ -1,99 +0,0 @@
|
|||
<!doctype>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body{
|
||||
margin: 25px;
|
||||
background: #eee;
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 100;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
#canvas{
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
float:left;
|
||||
}
|
||||
#sidebar{
|
||||
float:left;
|
||||
margin-left: 25px;
|
||||
width:300px;
|
||||
}
|
||||
#sidebar hr{
|
||||
border:none;
|
||||
border-bottom: 2px solid rgba(0,0,0,0.12);
|
||||
margin: 1em 0;
|
||||
}
|
||||
textarea#data{
|
||||
width:240px;
|
||||
height:50px;
|
||||
}
|
||||
#goal{
|
||||
font-size:20px;
|
||||
width:100%;
|
||||
padding:5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Game Canvas -->
|
||||
<canvas id="canvas"></canvas>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div id="sidebar">
|
||||
|
||||
<!-- Meta Settings -->
|
||||
<input type="input" id="goal" placeholder="puzzle goal"></input>
|
||||
<br>
|
||||
contagion threshold:
|
||||
<span id="contagionLabel"></span>
|
||||
<br>
|
||||
<input type="range" min="0" max="12" step="1" value="4" id="contagionSlider">
|
||||
|
||||
<!-- Simulate -->
|
||||
<hr>
|
||||
simulation:
|
||||
<span id="simIsNotRunning">
|
||||
(not running)
|
||||
<br>
|
||||
<button id="simStart">start sim</button>
|
||||
</span>
|
||||
<span id="simIsRunning">
|
||||
(running! step <span id="sim_step"></span>)
|
||||
<br>
|
||||
<button id="simNext">next step</button>
|
||||
<button id="simStop">stop sim</button>
|
||||
</span>
|
||||
|
||||
<!-- Metrics
|
||||
<hr>
|
||||
connected:
|
||||
<span id="metric_connected"></span>
|
||||
<br>
|
||||
avg path length:
|
||||
<span id="metric_avg_path"></span>
|
||||
<br>
|
||||
longest path length:
|
||||
<span id="metric_longest_path"></span>
|
||||
<br>
|
||||
avg clustering:
|
||||
<span id="metric_clustering"></span>-->
|
||||
|
||||
<!-- Save & Load -->
|
||||
<hr>
|
||||
<div style="display:inline-block; width:50px">
|
||||
<button id="buttonSave">(s)ave</button>
|
||||
<button id="buttonLoad">load</button>
|
||||
<button id="buttonClear">clear</button>
|
||||
</div>
|
||||
<textarea id="data"></textarea>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<script src="Old_Game.js"></script>
|
||||
<script src="editor.js"></script>
|
209
editor/editor.js
|
@ -1,209 +0,0 @@
|
|||
window.onload = function(){
|
||||
init();
|
||||
}
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData;
|
||||
if(window.location.hash){
|
||||
initData = JSON.parse(window.location.hash.substr(1));
|
||||
}else{
|
||||
initData = {
|
||||
"goal": "herp derp",
|
||||
"contagion": contagionThreshold,
|
||||
"peeps": [ [200,200], [300,300] ],
|
||||
"connections": [ [0,1] ]
|
||||
};
|
||||
}
|
||||
|
||||
function init(){
|
||||
|
||||
// Add peeps!
|
||||
_loadData(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
// KEYS TO ADD & REMOVE PEEPS
|
||||
window.addEventListener("keydown", function(event){
|
||||
|
||||
// "S" for SAVE
|
||||
if(event.keyCode==83){
|
||||
event.cancelBubble = true;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
_save();
|
||||
}
|
||||
|
||||
// "1" to "2" TO ADD A PEEP
|
||||
var keybase = 48;
|
||||
if(event.keyCode>=keybase+1 && event.keyCode<=keybase+2){
|
||||
// Am I hovering over a peep (no buffer)?
|
||||
var peepHovered = _mouseOverPeep(0);
|
||||
if(peepHovered){
|
||||
removePeep(peepHovered); // If so, DELETE IT
|
||||
}else{
|
||||
var state = event.keyCode-keybase;
|
||||
addPeep(Mouse.x, Mouse.y, state); // If not, ADD ONE
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// SPACE TO MOVE PEEPS
|
||||
var movingPeep = null;
|
||||
var movingPeepOffset = {x:0,y:0}
|
||||
function _preUpdate(){
|
||||
if(movingPeep){
|
||||
movingPeep.x = Mouse.x - movingPeepOffset.x;
|
||||
movingPeep.y = Mouse.y - movingPeepOffset.y;
|
||||
}
|
||||
}
|
||||
window.addEventListener("keydown", function(event){
|
||||
if(event.keyCode==32){
|
||||
var peepHovered = _mouseOverPeep(0);
|
||||
if(peepHovered){
|
||||
movingPeep = peepHovered;
|
||||
movingPeepOffset.x = Mouse.x - movingPeep.x;
|
||||
movingPeepOffset.y = Mouse.y - movingPeep.y;
|
||||
}
|
||||
}
|
||||
});
|
||||
window.addEventListener("keyup", function(event){
|
||||
if(event.keyCode==32){
|
||||
movingPeep = null;
|
||||
}
|
||||
});
|
||||
|
||||
// BUTTONS: SAVE / LOAD / CLEAR
|
||||
var dataTextbox = $("#data");
|
||||
var _saveData = function(){
|
||||
var network = saveNetwork();
|
||||
return {
|
||||
goal: $("#goal").value,
|
||||
contagion: contagionThreshold,
|
||||
peeps: network.peeps,
|
||||
connections: network.connections
|
||||
}
|
||||
}
|
||||
var _save = function(){
|
||||
var newData;
|
||||
if(SIM_IS_RUNNING){
|
||||
newData = initData;
|
||||
}else{
|
||||
newData = _saveData();
|
||||
}
|
||||
dataTextbox.value = JSON.stringify(newData);
|
||||
dataTextbox.select();
|
||||
window.location.hash = dataTextbox.value;
|
||||
};
|
||||
$("#buttonSave").onclick = _save;
|
||||
var _loadData = function(data){
|
||||
loadNetwork(data);
|
||||
$("#goal").value = data.goal;
|
||||
$("#contagionSlider").value = Math.round(data.contagion*12);
|
||||
_getThreshold();
|
||||
}
|
||||
$("#buttonLoad").onclick = function(){
|
||||
try{
|
||||
var data = JSON.parse(dataTextbox.value);
|
||||
_loadData(data);
|
||||
dataTextbox.value = "loaded!";
|
||||
}catch(e){
|
||||
alert("DATA AIN'T PROPER JSON, YO");
|
||||
}
|
||||
};
|
||||
$("#buttonClear").onclick = function(){
|
||||
clearNetwork();
|
||||
};
|
||||
|
||||
// Editing the Mission Goal statement: DON'T PROPAGATE KEYS
|
||||
$("#goal").addEventListener("keydown", function(event){
|
||||
event.cancelBubble = true;
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////
|
||||
//// RUN THE SIMULATION, YO /////////////
|
||||
/////////////////////////////////////////
|
||||
|
||||
var SIM_IS_RUNNING = false;
|
||||
var SIM_STEP = 0;
|
||||
var _updateSimRunningUI = function(){
|
||||
if(SIM_IS_RUNNING){
|
||||
$("#simIsNotRunning").style.display = "none";
|
||||
$("#simIsRunning").style.display = "inline";
|
||||
document.body.style.background = "#777";
|
||||
$("#sim_step").innerHTML = SIM_STEP;
|
||||
}else{
|
||||
$("#simIsNotRunning").style.display = "inline";
|
||||
$("#simIsRunning").style.display = "none";
|
||||
document.body.style.background = "";
|
||||
}
|
||||
};
|
||||
_updateSimRunningUI();
|
||||
$("#simStart").onclick = function(){
|
||||
SIM_STEP = 0;
|
||||
SIM_IS_RUNNING = true;
|
||||
initData = _saveData();
|
||||
_updateSimRunningUI();
|
||||
};
|
||||
$("#simStop").onclick = function(){
|
||||
SIM_IS_RUNNING = false;
|
||||
_loadData(initData);
|
||||
update();
|
||||
_updateSimRunningUI();
|
||||
};
|
||||
|
||||
var contagionThreshold = 0;
|
||||
var _getThreshold = function(){
|
||||
contagionThreshold = $("#contagionSlider").value/12;
|
||||
$("#contagionLabel").innerHTML = Math.floor(contagionThreshold*100)+"%";
|
||||
}
|
||||
$("#contagionSlider").oninput = _getThreshold;
|
||||
_getThreshold();
|
||||
|
||||
function stepSimulation(){
|
||||
|
||||
// Consider all peeps, and their friends
|
||||
var toInfect = [];
|
||||
peeps.forEach(function(peep){
|
||||
|
||||
// How many infected friends?
|
||||
if(peep.numFriends==0) return; // No friends? NVM.
|
||||
var ratioOfInfectedFriends = peep.numInfectedFriends/peep.numFriends;
|
||||
|
||||
// Passed threshold?
|
||||
if(contagionThreshold==0){ // simple contagion, just ANY friend
|
||||
if(peep.numInfectedFriends>0) toInfect.push(peep);
|
||||
}else{
|
||||
// greater OR EQUALS (fuzz coz floating point)
|
||||
if(ratioOfInfectedFriends>=contagionThreshold-0.0001){
|
||||
toInfect.push(peep);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// "Infect" the peeps who need to get infected
|
||||
toInfect.forEach(function(peep){
|
||||
peep.state = 2;
|
||||
});
|
||||
|
||||
}
|
||||
$("#simNext").onclick = function(){
|
||||
SIM_STEP++;
|
||||
_updateSimRunningUI();
|
||||
stepSimulation();
|
||||
};
|
||||
|
||||
function $(query){
|
||||
return document.querySelector(query);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<!doctype>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body{
|
||||
margin:0;
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 100;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
#sim_ui{
|
||||
display: none;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom:0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<canvas id="canvas"></canvas>
|
||||
|
||||
<div id="sim_ui">
|
||||
simulation:
|
||||
<span id="sim_is_not_running">
|
||||
(not running)
|
||||
<br>
|
||||
<button id="sim_start">start sim</button>
|
||||
</span>
|
||||
<span id="sim_is_running">
|
||||
(running! step <span id="sim_step"></span>)
|
||||
<br>
|
||||
<button id="sim_next">next step</button>
|
||||
<button id="sim_stop">stop sim</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script src="js/Mouse.js"></script>
|
||||
<script src="js/Peep.js"></script>
|
||||
<script src="js/Connection.js"></script>
|
||||
<script src="js/Drawing.js"></script>
|
||||
<script src="js/Cursor.js"></script>
|
||||
<script src="js/Game.js"></script>
|
||||
<script src="js/main.js"></script>
|
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 74 KiB |
|
@ -1,92 +0,0 @@
|
|||
function Connection(config){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Properties
|
||||
self.from = config.from;
|
||||
self.to = config.to;
|
||||
self.uncuttable = config.uncuttable || false;
|
||||
|
||||
// Update
|
||||
self.update = function(){
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
ctx.strokeStyle = self.uncuttable ? "#000" : "#888";
|
||||
ctx.lineWidth = 2; //self.uncuttable ? 3 : 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.from.x, self.from.y);
|
||||
ctx.lineTo(self.to.x, self.to.y);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
// Hit Test with a LINE SEGMENT
|
||||
// code adapted from https://gist.github.com/Joncom/e8e8d18ebe7fe55c3894
|
||||
self.hitTest = function(line){
|
||||
|
||||
var p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y;
|
||||
p0_x = line[0];
|
||||
p0_y = line[1];
|
||||
p1_x = line[2];
|
||||
p1_y = line[3];
|
||||
p2_x = self.from.x;
|
||||
p2_y = self.from.y;
|
||||
p3_x = self.to.x;
|
||||
p3_y = self.to.y;
|
||||
|
||||
var s1_x, s1_y, s2_x, s2_y;
|
||||
s1_x = p1_x - p0_x;
|
||||
s1_y = p1_y - p0_y;
|
||||
s2_x = p3_x - p2_x;
|
||||
s2_y = p3_y - p2_y;
|
||||
var s, t;
|
||||
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
|
||||
return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
function addConnection(from, to, uncuttable){
|
||||
|
||||
// Don't allow connect if connecting to same...
|
||||
if(from==to) return;
|
||||
|
||||
// ...or if already exists, in either direction
|
||||
for(var i=0; i<connections.length; i++){
|
||||
var c = connections[i];
|
||||
if(c.from==from && c.to==to) return;
|
||||
if(c.from==to && c.to==from) return;
|
||||
}
|
||||
|
||||
// Otherwise, sure!
|
||||
connections.push(new Connection({
|
||||
from:from, to:to,
|
||||
uncuttable:uncuttable
|
||||
}));
|
||||
|
||||
}
|
||||
function getConnected(peep){
|
||||
var results = [];
|
||||
for(var i=0; i<connections.length; i++){ // in either direction
|
||||
var c = connections[i];
|
||||
if(c.from==peep) results.push(c.to);
|
||||
if(c.to==peep) results.push(c.from);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
function removeAllConnectedTo(peep){
|
||||
for(var i=connections.length-1; i>=0; i--){ // backwards index coz we're deleting
|
||||
var c = connections[i];
|
||||
if(c.from==peep || c.to==peep){ // in either direction
|
||||
connections.splice(i,1); // remove!
|
||||
}
|
||||
}
|
||||
}
|
||||
function _makeUncuttable(arrayOfConnections){
|
||||
for(var i=0; i<arrayOfConnections.length; i++){
|
||||
arrayOfConnections[i].push(true);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
Cursor.NORMAL = 0;
|
||||
Cursor.CONNECT = 1;
|
||||
Cursor.ERASE = 2;
|
||||
|
||||
var cursorImage = new Image();
|
||||
cursorImage.src = "img/cursor.png";
|
||||
|
||||
function Cursor(){
|
||||
var self = this;
|
||||
|
||||
// MODES
|
||||
// 0 - normal
|
||||
// 1 - hover, CAN connect
|
||||
// 2 - erase
|
||||
self.mode = 0;
|
||||
self.setMode = function(mode){
|
||||
self.mode = mode;
|
||||
};
|
||||
|
||||
// Rotate when mouse pressed
|
||||
self.rotation = 0;
|
||||
|
||||
// Update
|
||||
self.update = function(){
|
||||
var r = Mouse.pressed ? -0.2 : 0;
|
||||
self.rotation = self.rotation*0.5 + r*0.5;
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
if(!self.visible) return; // NAH
|
||||
ctx.save();
|
||||
ctx.translate(Mouse.x, Mouse.y);
|
||||
ctx.rotate(self.rotation);
|
||||
var sx;
|
||||
switch(self.mode){
|
||||
case Cursor.NORMAL: sx=0; break;
|
||||
case Cursor.CONNECT: sx=1; break;
|
||||
case Cursor.ERASE: sx=2; break;
|
||||
}
|
||||
sx = sx*100;
|
||||
ctx.drawImage(cursorImage,
|
||||
sx, 0, 100, 100,
|
||||
0, -40, 40, 40);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// HIDE OR SHOW
|
||||
self.visible = true;
|
||||
self.show = function(){
|
||||
self.visible = true;
|
||||
};
|
||||
self.hide = function(){
|
||||
self.visible = false;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
function Drawing(){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Update!
|
||||
self.update = function(){
|
||||
|
||||
// Connection
|
||||
if(self.connectFrom){
|
||||
// Over any peeps? Connect to THAT! Else, connect to Mouse
|
||||
var peepHovered = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
|
||||
if(peepHovered==self.connectFrom) peepHovered=null; // if same, nah
|
||||
self.connectTo = peepHovered ? peepHovered : Mouse;
|
||||
}
|
||||
|
||||
// Erase
|
||||
if(self.isErasing){
|
||||
self.eraseTrail.unshift([Mouse.x,Mouse.y]); // add to start
|
||||
if(self.eraseTrail.length>10){
|
||||
self.eraseTrail.pop(); // remove from end
|
||||
}
|
||||
}else{
|
||||
self.eraseTrail.pop(); // remove from end
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Connection!
|
||||
self.connectFrom = null;
|
||||
self.connectTo = null;
|
||||
self.startConnect = function(from){
|
||||
self.connectFrom = from;
|
||||
};
|
||||
self.endConnect = function(){
|
||||
self.connectFrom = null;
|
||||
};
|
||||
|
||||
// Erase!
|
||||
self.isErasing = false;
|
||||
self.eraseTrail = [];
|
||||
self.startErase = function(){
|
||||
self.isErasing = true;
|
||||
};
|
||||
self.endErase = function(){
|
||||
self.isErasing = false;
|
||||
};
|
||||
|
||||
// Draw
|
||||
self.draw = function(ctx){
|
||||
|
||||
// Connecting...
|
||||
if(self.connectFrom){
|
||||
ctx.strokeStyle = "#ccc";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.connectFrom.x, self.connectFrom.y);
|
||||
ctx.lineTo(self.connectTo.x, self.connectTo.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Erase
|
||||
if(self.eraseTrail.length>0){
|
||||
ctx.strokeStyle = "#dd4040";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(self.eraseTrail[0][0], self.eraseTrail[0][1]);
|
||||
for(var i=1; i<self.eraseTrail.length; i++){
|
||||
ctx.lineTo(self.eraseTrail[i][0], self.eraseTrail[i][1]);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
Math.TAU = Math.PI*2;
|
||||
|
||||
var canvas = document.getElementById("canvas");// || document.createElement("canvas");
|
||||
canvas.style.cursor = "none";
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
var peeps = [];
|
||||
var connections = [];
|
||||
var drawing = new Drawing();
|
||||
var cursor = new Cursor();
|
||||
|
||||
var winnerImage = new Image();
|
||||
winnerImage.src = "img/winner.png";
|
||||
|
||||
var CONTAGION_THRESHOLD = 0;
|
||||
var CONTAGION_THRESHOLD_2 = 0;
|
||||
|
||||
var clearNetwork = function(){
|
||||
peeps = [];
|
||||
connections = [];
|
||||
};
|
||||
var loadNetwork = function(data){
|
||||
|
||||
// Clear!
|
||||
clearNetwork();
|
||||
|
||||
// Peeps
|
||||
data.peeps.forEach(function(p){
|
||||
addPeep(p[0], p[1], p[2]);
|
||||
});
|
||||
|
||||
// Connections
|
||||
data.connections.forEach(function(c){
|
||||
var from = peeps[c[0]];
|
||||
var to = peeps[c[1]];
|
||||
var uncuttable = c[2];
|
||||
addConnection(from, to, uncuttable);
|
||||
});
|
||||
|
||||
// Contagion threshold?
|
||||
if(data.contagion !== undefined){
|
||||
CONTAGION_THRESHOLD = data.contagion;
|
||||
}else{
|
||||
CONTAGION_THRESHOLD = 0;
|
||||
}
|
||||
if(data.contagion2 !== undefined){
|
||||
CONTAGION_THRESHOLD_2 = data.contagion2;
|
||||
}else{
|
||||
CONTAGION_THRESHOLD_2 = 0;
|
||||
}
|
||||
|
||||
}
|
||||
var saveNetwork = function(){
|
||||
var data = {
|
||||
peeps: [],
|
||||
connections: [],
|
||||
contagion: CONTAGION_THRESHOLD,
|
||||
contagion2: CONTAGION_THRESHOLD_2,
|
||||
};
|
||||
peeps.forEach(function(peep){
|
||||
data.peeps.push([peep.x, peep.y, peep.state]);
|
||||
});
|
||||
connections.forEach(function(c){
|
||||
var fromIndex = peeps.indexOf(c.from);
|
||||
var toIndex = peeps.indexOf(c.to);
|
||||
data.connections.push([fromIndex, toIndex, c.uncuttable]);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
var DRAW_STATE = 0; // 0-nothing | 1-connecting | 2-erasing
|
||||
var DRAW_CONNECT_FROM = null;
|
||||
var CONNECT_FROM_BUFFER = 15;//25;
|
||||
var CONNECT_TO_BUFFER = 25;
|
||||
|
||||
var YOU_ARE_WINNER = false;
|
||||
|
||||
function update(){
|
||||
|
||||
// Mouse logic...
|
||||
if(SIM_IS_RUNNING){
|
||||
DRAW_STATE = 0; // back to normal
|
||||
Mouse.update();
|
||||
}else{
|
||||
if(Mouse.justPressed && DRAW_STATE===0){
|
||||
|
||||
// Clicked on a peep?
|
||||
var peepClicked = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
|
||||
if(peepClicked){
|
||||
DRAW_CONNECT_FROM = peepClicked;
|
||||
DRAW_STATE = 1; // START CONNECTING
|
||||
drawing.startConnect(peepClicked); // Drawing logic
|
||||
}else{
|
||||
DRAW_STATE = 2; // START ERASING
|
||||
}
|
||||
|
||||
}
|
||||
if(DRAW_STATE==2){ // ERASE
|
||||
|
||||
// Intersect with any CUTTABLE connections?
|
||||
var line = [Mouse.lastX, Mouse.lastY, Mouse.x, Mouse.y];
|
||||
for(var i=connections.length-1; i>=0; i--){ // going BACKWARDS coz killing
|
||||
var c = connections[i];
|
||||
if(c.uncuttable) continue; // don't touch the UNCUTTABLES
|
||||
if(c.hitTest(line)) connections.splice(i,1);
|
||||
}
|
||||
drawing.startErase(); // Drawing logic
|
||||
|
||||
}
|
||||
if(Mouse.justReleased && DRAW_STATE!==0){
|
||||
|
||||
// Connecting peeps, and released on a peep?
|
||||
if(DRAW_STATE==1){
|
||||
var peepReleased = _mouseOverPeep(CONNECT_TO_BUFFER); // buffer of 20px
|
||||
if(peepReleased){ // connect 'em!
|
||||
addConnection(DRAW_CONNECT_FROM, peepReleased);
|
||||
DRAW_CONNECT_FROM = null;
|
||||
}
|
||||
drawing.endConnect(); // Drawing logic
|
||||
}else if(DRAW_STATE==2){
|
||||
drawing.endErase(); // Drawing logic
|
||||
}
|
||||
DRAW_STATE = 0; // back to normal
|
||||
|
||||
}
|
||||
Mouse.update();
|
||||
|
||||
// Cursor Logic
|
||||
if(DRAW_STATE==0){
|
||||
var peepHovered = _mouseOverPeep(CONNECT_FROM_BUFFER); // buffer of 20px
|
||||
if(peepHovered){
|
||||
cursor.setMode(Cursor.CONNECT);
|
||||
}else{
|
||||
cursor.setMode(Cursor.NORMAL);
|
||||
}
|
||||
}
|
||||
if(DRAW_STATE==1){
|
||||
cursor.setMode(Cursor.CONNECT);
|
||||
}
|
||||
if(DRAW_STATE==2){
|
||||
cursor.setMode(Cursor.ERASE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update Logic
|
||||
connections.forEach(function(connection){
|
||||
connection.update(ctx);
|
||||
});
|
||||
drawing.update();
|
||||
peeps.forEach(function(peep){
|
||||
peep.update();
|
||||
});
|
||||
cursor.update();
|
||||
|
||||
// Draw Logic
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = SIM_IS_RUNNING ? "#eee" : "#fff";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.save();
|
||||
ctx.scale(2,2);
|
||||
_preUpdate();
|
||||
//ctx.translate(0,100);
|
||||
|
||||
connections.forEach(function(connection){
|
||||
connection.draw(ctx);
|
||||
});
|
||||
drawing.draw(ctx);
|
||||
peeps.forEach(function(peep){
|
||||
peep.draw(ctx);
|
||||
});
|
||||
cursor.draw(ctx);
|
||||
|
||||
_onUpdate();
|
||||
if(YOU_ARE_WINNER){
|
||||
ctx.drawImage(winnerImage, 0, 0, 500, 500);
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
// RAF
|
||||
requestAnimationFrame(update);
|
||||
|
||||
}
|
||||
function _preUpdate(){
|
||||
// TO IMPLEMENT
|
||||
}
|
||||
function _onUpdate(){
|
||||
// TO IMPLEMENT
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// CONTAGION UI, WHY NOT HMMMM ////////
|
||||
///////////////////////////////////////
|
||||
|
||||
function $(query){
|
||||
return document.querySelector(query);
|
||||
}
|
||||
|
||||
function showContagionUI(){
|
||||
|
||||
// Just display the div
|
||||
$("#sim_ui").style.display = "block";
|
||||
_updateSimRunningUI();
|
||||
|
||||
}
|
||||
|
||||
var SIM_IS_RUNNING = false;
|
||||
var SIM_STEP = 0;
|
||||
var _updateSimRunningUI = function(){
|
||||
if(SIM_IS_RUNNING){
|
||||
$("#sim_is_not_running").style.display = "none";
|
||||
$("#sim_is_running").style.display = "inline";
|
||||
//document.body.style.background = "#777";
|
||||
$("#sim_step").innerHTML = SIM_STEP;
|
||||
}else{
|
||||
$("#sim_is_not_running").style.display = "inline";
|
||||
$("#sim_is_running").style.display = "none";
|
||||
//document.body.style.background = "";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var _networkBeforeSimulationStarted = null;
|
||||
function _startSim(){
|
||||
SIM_STEP = 0;
|
||||
SIM_IS_RUNNING = true;
|
||||
_networkBeforeSimulationStarted = saveNetwork();
|
||||
_updateSimRunningUI();
|
||||
_startSimulation();
|
||||
};
|
||||
$("#sim_start").onclick = _startSim;
|
||||
function _stopSim(){
|
||||
SIM_IS_RUNNING = false;
|
||||
_resetToBeforeSimStarted();
|
||||
_updateSimRunningUI();
|
||||
_stopSimulation();
|
||||
};
|
||||
$("#sim_stop").onclick = _stopSim;
|
||||
function _simNext(){
|
||||
SIM_STEP++;
|
||||
_updateSimRunningUI();
|
||||
_stepSimulation();
|
||||
};
|
||||
$("#sim_next").onclick = _simNext;
|
||||
|
||||
function _resetToBeforeSimStarted(){
|
||||
loadNetwork(_networkBeforeSimulationStarted);
|
||||
}
|
||||
function _startSimulation(){
|
||||
// To Implement
|
||||
}
|
||||
function _stopSimulation(){
|
||||
// To Implement
|
||||
}
|
||||
function _stepSimulation(){
|
||||
_infectPeople();
|
||||
}
|
||||
|
||||
function _infectPeople(){
|
||||
|
||||
// Consider all peeps, and their friends
|
||||
var toInfect = [];
|
||||
peeps.forEach(function(peep){
|
||||
|
||||
// How many infected friends?
|
||||
if(peep.numFriends==0) return; // No friends? NVM.
|
||||
var ratioOfInfectedFriends = peep.numInfectedFriends/peep.numFriends;
|
||||
|
||||
// Passed threshold?
|
||||
if(CONTAGION_THRESHOLD==0){ // simple contagion, just ANY friend
|
||||
if(peep.numInfectedFriends>0) toInfect.push(peep);
|
||||
}else{
|
||||
// greater OR EQUALS (fuzz coz floating point)
|
||||
if(ratioOfInfectedFriends>=CONTAGION_THRESHOLD-0.0001){
|
||||
toInfect.push(peep);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// "Infect" the peeps who need to get infected
|
||||
toInfect.forEach(function(peep){
|
||||
peep.infect();
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/////////////////////////////
|
||||
// MOUSE ////////////////////
|
||||
/////////////////////////////
|
||||
|
||||
var Mouse = {
|
||||
x:0, y:0,
|
||||
pressed:false
|
||||
};
|
||||
Mouse.ondown = function(event){
|
||||
cursor.show();
|
||||
Mouse.pressed = true;
|
||||
Mouse.onmove(event);
|
||||
};
|
||||
Mouse.onmove = function(event){
|
||||
cursor.show();
|
||||
Mouse.x = event.offsetX;
|
||||
Mouse.y = event.offsetY;
|
||||
};
|
||||
Mouse.onup = function(event){
|
||||
Mouse.pressed = false;
|
||||
};
|
||||
Mouse.update = function(){
|
||||
|
||||
// Just pressed, or just released (one frame ago)
|
||||
Mouse.justPressed = (!Mouse.lastPressed && Mouse.pressed);
|
||||
Mouse.justReleased = (Mouse.lastPressed && !Mouse.pressed);
|
||||
|
||||
// The last frame's stuff
|
||||
Mouse.lastX = Mouse.x;
|
||||
Mouse.lastY = Mouse.y;
|
||||
Mouse.lastPressed = Mouse.pressed;
|
||||
|
||||
};
|
||||
canvas.addEventListener("mousedown", Mouse.ondown);
|
||||
canvas.addEventListener("mousemove", Mouse.onmove);
|
||||
window.addEventListener("mouseup", Mouse.onup);
|
||||
|
||||
// TOUCH.
|
||||
function _touchWrapper(callback){
|
||||
return function(event){
|
||||
var _event = {};
|
||||
_event.offsetX = event.changedTouches[0].clientX;
|
||||
_event.offsetY = event.changedTouches[0].clientY;
|
||||
event.preventDefault();
|
||||
callback(_event);
|
||||
};
|
||||
}
|
||||
canvas.addEventListener("touchstart", _touchWrapper(Mouse.ondown), false);
|
||||
canvas.addEventListener("touchmove", _touchWrapper(Mouse.onmove), false);
|
||||
document.body.addEventListener("touchend", function(){
|
||||
cursor.hide();
|
||||
Mouse.onup();
|
||||
}, false);
|
|
@ -1,221 +0,0 @@
|
|||
var PEEP_STATE_COLORS = {
|
||||
1: "#ccc",
|
||||
2: "#dd4040"
|
||||
};
|
||||
|
||||
function Peep(config){
|
||||
|
||||
var self = this;
|
||||
|
||||
// Properties
|
||||
self.x = config.x;
|
||||
self.y = config.y;
|
||||
self.state = config.state;
|
||||
|
||||
// Update:
|
||||
self.numFriends = 0;
|
||||
self.numInfectedFriends = 0;
|
||||
self.faceX = 0;
|
||||
self.faceY = 0;
|
||||
self.faceBlink = 0;
|
||||
self.isMajority = false;
|
||||
var _faceFollow = 0.75+(Math.random()*0.1);
|
||||
self.update = function(){
|
||||
|
||||
// Face position!
|
||||
var faceVector = {
|
||||
x: (Mouse.x-self.x)/5,
|
||||
y: (Mouse.y-self.y)/5
|
||||
};
|
||||
faceVector.mag = Math.sqrt(faceVector.x*faceVector.x + faceVector.y*faceVector.y);
|
||||
var max_distance = 5;
|
||||
if(faceVector.mag>max_distance){
|
||||
faceVector.x = faceVector.x * (max_distance/faceVector.mag);
|
||||
faceVector.y = faceVector.y * (max_distance/faceVector.mag);
|
||||
}
|
||||
self.faceX = self.faceX*_faceFollow + faceVector.x*(1-_faceFollow);
|
||||
self.faceY = self.faceY*_faceFollow + faceVector.y*(1-_faceFollow);
|
||||
|
||||
// Blink?
|
||||
if(!self.faceBlink){
|
||||
if(Math.random()<0.002) self.faceBlink=true;
|
||||
}else{
|
||||
if(Math.random()<0.09) self.faceBlink=false;
|
||||
}
|
||||
|
||||
// Friends connected... or infected
|
||||
var friends = self.sim.getConnected(self);
|
||||
self.numFriends = friends.length;
|
||||
self.numInfectedFriends = 0;
|
||||
friends.forEach(function(friend){
|
||||
if(friend.state==2) self.numInfectedFriends++;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Draw
|
||||
var radius = 20;
|
||||
var barWidth = 30;
|
||||
var barHeight = 10;
|
||||
self.draw = function(ctx){
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(self.x, self.y);
|
||||
|
||||
// Circle
|
||||
//ctx.fillStyle = (self.state==1) ? "#ccc" : "#dd4040"; //"#ffdf00";
|
||||
var myColor = PEEP_STATE_COLORS[self.state];
|
||||
ctx.fillStyle = myColor;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
|
||||
// INFECT ON NEXT TURN?
|
||||
/*var infectOnNextTurn = (self.numFriends>0 && self.numInfectedFriends/self.numFriends>=CONTAGION_THRESHOLD);
|
||||
if(infectOnNextTurn){
|
||||
ctx.strokeStyle = PEEP_STATE_COLORS[2];
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
}*/
|
||||
|
||||
// Face
|
||||
ctx.save();
|
||||
ctx.translate(self.faceX, self.faceY);
|
||||
ctx.fillStyle = "rgba(0,0,0,0.5)";
|
||||
if(self.faceBlink){
|
||||
ctx.beginPath();
|
||||
ctx.rect(-14, -1, 8, 2);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.rect(6, -1, 8, 2);
|
||||
ctx.fill();
|
||||
}else{
|
||||
ctx.beginPath();
|
||||
ctx.arc(-10, -1, 3, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(10, -1, 3, 0, Math.TAU, false);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.rect(-7, 4, 14, 2);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// LABEL FOR INFECTED/FRIENDS, BAR, AND CONTAGION LEVEL //
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
//if(!_hack_HIDE_BARS && !self._hack_TESTED){
|
||||
|
||||
ctx.save();
|
||||
|
||||
// Say: Infected/Friends
|
||||
ctx.translate(0,-42);
|
||||
var labelNum = self.numInfectedFriends+"/"+self.numFriends;
|
||||
var labelPercent = "";
|
||||
if(self.numFriends>0){
|
||||
labelPercent = Math.round(100*(self.numInfectedFriends/self.numFriends)) + "%";
|
||||
}
|
||||
ctx.font = '12px sans-serif';
|
||||
ctx.fillStyle = myColor;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fontWeight = "bold";
|
||||
ctx.fillText(labelNum, 0, 0);
|
||||
|
||||
// A nice bar
|
||||
ctx.translate(0,12);
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
// the white fill
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.beginPath();
|
||||
ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight);
|
||||
ctx.fill();
|
||||
|
||||
// The color fills
|
||||
if(self.numFriends>0){
|
||||
ctx.fillStyle = PEEP_STATE_COLORS[2]; // state = 2 infected
|
||||
ctx.beginPath();
|
||||
ctx.rect(-barWidth/2, -barHeight/2, barWidth*(self.numInfectedFriends/self.numFriends), barHeight);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// The outline
|
||||
ctx.strokeStyle = myColor;
|
||||
ctx.beginPath();
|
||||
if(self.numFriends>0){
|
||||
ctx.rect(-barWidth/2, -barHeight/2, barWidth, barHeight);
|
||||
}else{
|
||||
ctx.rect(-barWidth/2, 0, barWidth, 0);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// a pointer for contagion level
|
||||
ctx.translate(0, barHeight/2+2);
|
||||
self._drawThreshold(ctx, CONTAGION_THRESHOLD);
|
||||
|
||||
// Percent
|
||||
ctx.font = '8px sans-serif';
|
||||
ctx.fillStyle = "rgba(0,0,0,0.8)";
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fontWeight = "bold";
|
||||
ctx.fillText(labelPercent, 0, -6);
|
||||
|
||||
ctx.restore();
|
||||
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
};
|
||||
self._drawThreshold = function(ctx, threshold){
|
||||
ctx.save();
|
||||
ctx.translate(barWidth*threshold - barWidth/2, 0);
|
||||
|
||||
ctx.strokeStyle = "#000"; //PEEP_STATE_COLORS[2];
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0,0);
|
||||
ctx.lineTo(0,-14);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// Hit Test
|
||||
self.hitTest = function(x,y,buffer){
|
||||
if(buffer===undefined) buffer=0;
|
||||
var dx = self.x-x;
|
||||
var dy = self.y-y;
|
||||
var dist2 = dx*dx+dy*dy;
|
||||
var r = radius+buffer;
|
||||
return (dist2<r*r);
|
||||
};
|
||||
|
||||
// Infect
|
||||
self.infect = function(){
|
||||
self.state = 2;
|
||||
}
|
||||
|
||||
}
|
||||
function _mouseOverPeep(buffer){
|
||||
var result;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.hitTest(Mouse.x, Mouse.y, buffer)) result=peep;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function addPeep(x,y, state){
|
||||
var peep = new Peep({
|
||||
x:x, y:y,
|
||||
state: state ? state : 1
|
||||
});
|
||||
peeps.push(peep);
|
||||
}
|
||||
function removePeep(peep){
|
||||
removeAllConnectedTo(peep); // remove connections first
|
||||
peeps.splice(peeps.indexOf(peep),1); // BYE peep
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
function getQueryVariable(variable){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for(var i=0; i<vars.length; i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0]==variable) return pair[1];
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
var levelName = getQueryVariable("level");
|
||||
if(levelName){
|
||||
var newScript = document.createElement("script");
|
||||
newScript.src = "../levels/"+levelName+".js";
|
||||
setTimeout(function(){
|
||||
document.body.appendChild(newScript);
|
||||
},1);
|
||||
}
|
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB |
|
@ -1,40 +0,0 @@
|
|||
_hack_HIDE_BARS = true;
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"goal": "herp derp",
|
||||
"contagion": 0,
|
||||
"peeps": [
|
||||
[27+20,154],[30+20,263],[39+10,444],[73+10,63],[88+10,367],
|
||||
[125,210],
|
||||
[125,290], // 6
|
||||
[140+10,470],[195+5,128],[215+5,358],
|
||||
[221,38],[295,450],[332,121],
|
||||
[375,290], // 13
|
||||
[375,210],
|
||||
[378,397],[429,52],[451,183],[445,459],[461,323]
|
||||
],
|
||||
"connections": [[6,13]]
|
||||
};
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
|
||||
var instruction1 = new Image();
|
||||
instruction1.src = "img/instruction_connect.png";
|
||||
var instruction2 = new Image();
|
||||
instruction2.src = "img/instruction_disconnect.png";
|
||||
function _preUpdate(){
|
||||
ctx.drawImage(instruction1, 0, 0, 500, 500);
|
||||
ctx.drawImage(instruction2, 0, 0, 500, 500);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update
|
||||
update();
|
|
@ -1,18 +0,0 @@
|
|||
PEEP_STATE_COLORS[2] = "#eebb55";
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[196,200,1],[307,297,2],[199,296,1],[305,199,2]],
|
||||
"connections":[[3,2],[3,1]]
|
||||
}
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
|
@ -1,48 +0,0 @@
|
|||
PEEP_STATE_COLORS[2] = "#eebb55";
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0.5,
|
||||
"peeps":[[128,122,2],[239,84,2],[356,131,2],[74,224,1],[95,333,1],[170,401,1],[286,411,1],[376,342,1],[403,236,1]],
|
||||
"connections":[]
|
||||
}
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// WINNER? Only if ALL peeps think drinking is in the majority
|
||||
var progress = 0;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.numFriends>0 &&peep.numInfectedFriends/peep.numFriends>=0.5){
|
||||
progress++;
|
||||
}
|
||||
});
|
||||
YOU_ARE_WINNER = (progress==9);
|
||||
|
||||
// Progress...
|
||||
var label = "FOOLED: "+progress+" out of 9 peeps";
|
||||
ctx.font = '14px sans-serif';
|
||||
ctx.fillStyle = PEEP_STATE_COLORS[2];
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(label, 250, 465);
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = PEEP_STATE_COLORS[2];
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180, 10);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180*(progress/9), 10);
|
||||
ctx.fill();
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
_hack_HIDE_BARS = true;
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[50,263,2],[141,274,1],[204,353,1],[452,264,1],[301,341,1],[364,271,1],[204,195,1],[301,206,1]],
|
||||
"connections":[[0,1],[1,2],[1,6],[6,7],[7,5],[5,3],[5,4],[4,2]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff EVERYONE is infected!
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = everyoneIsInfected;
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
_hack_HIDE_BARS = true;
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[57,72,2],[454,371,1],[418,438,1],[338,408,1],[333,340,1],[406,304,1],[269,116,1],[234,172,1],[305,173,1],[141,88,1],[143,243,1],[82,304,1],[124,366,1],[200,353,1],[215,276,1]],
|
||||
"connections":[[6,7],[4,1],[5,4],[4,3],[3,1],[1,2],[1,5],[5,2],[3,5],[4,2],[3,2],[8,6],[8,7],[9,0],[14,10],[10,11],[11,12],[12,13],[13,14],[14,11],[10,12],[13,10],[11,13],[12,14]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff EVERYONE is infected!
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = everyoneIsInfected;
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":0.25,
|
||||
"peeps":[[147,69,2],[77,117,1],[77,185,1],[140,226,1],[402,140,1],[143,307,2],[95,340,1],[68,389,1],[90,436,1],[151,465,1],[398,391,1]],
|
||||
"connections":[[0,4],[1,4],[2,4],[3,4],[5,10],[6,10],[7,10],[8,10],[9,10]]
|
||||
}
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
|
@ -1,33 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":0.25, // DIFFERENT
|
||||
"peeps":[[57,72,2],[454,371,1],[418,438,1],[338,408,1],[333,340,1],[406,304,1],[269,116,1],[234,172,1],[305,173,1],[141,88,1],[143,243,1],[82,304,1],[124,366,1],[200,353,1],[215,276,1]],
|
||||
"connections":[[6,7],[4,1],[5,4],[4,3],[3,1],[1,2],[1,5],[5,2],[3,5],[4,2],[3,2],[8,6],[8,7],[9,0],[14,10],[10,11],[11,12],[12,13],[13,14],[14,11],[10,12],[13,10],[11,13],[12,14]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff EVERYONE is infected!
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = everyoneIsInfected;
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":1/4,
|
||||
"peeps":[[50,263,2],[141,274,1],[204,353,1],[452,264,1],[301,341,1],[364,271,1],[204,195,1],[301,206,1]],
|
||||
"connections":[[0,1],[1,2],[1,6],[6,7],[7,5],[5,3],[5,4],[4,2]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
|
@ -1,22 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":0.25,
|
||||
"peeps":[[46,145,1],[91,79,2],[201,146,1],[168,211,1],[75,215,1],[168,79,1],[325,82,2],[402,78,1],[279,144,1],[320,212,1],[406,215,1],[443,143,1],[197,294,2],[150,353,1],[279,292,1],[320,349,1],[283,419,1],[196,420,1]],
|
||||
"connections":[[1,5],[8,6],[6,7],[7,11],[11,10],[10,9],[9,8],[8,7],[11,6],[8,11],[7,9],[9,6],[10,8],[9,11],[0,4],[3,2],[10,6],[10,7]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
|
@ -1,22 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":0.25,
|
||||
"peeps":[[109,87,2],[186,106,1],[219,177,1],[158,251,1],[73,230,1],[46,152,1],[271,302,1],[316,234,1],[410,229,1],[454,318,1],[401,396,1],[307,388,1]],
|
||||
"connections":[[11,6],[11,10],[10,9],[9,8],[8,7],[7,6],[6,9],[8,11],[10,7],[7,11],[10,8],[9,11],[6,10],[9,7],[6,8],[3,2],[5,4],[1,0]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
|
@ -1,22 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":0.25,
|
||||
"peeps":[[190,80,2],[267,70,1],[152,147,1],[316,116,1],[60,300,1],[138,293,1],[40,382,1],[90,449,1],[169,433,1],[194,362,1],[199,206,1],[293,197,1],[346,288,1],[412,311,1],[286,333,1],[433,386,1],[372,437,1],[299,408,1]],
|
||||
"connections":[[0,1],[2,10],[11,3],[14,17],[13,12],[16,15],[8,9],[5,4],[6,7]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
|
@ -1,40 +0,0 @@
|
|||
_hack_HIDE_BARS = true;
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"goal": "herp derp",
|
||||
"contagion": 0,
|
||||
"peeps": [
|
||||
[27+20,154],[30+20,263],[39+10,444],[73+10,63],[88+10,367],
|
||||
[125,210],
|
||||
[125,290], // 6
|
||||
[140+10,470],[195+5,128],[215+5,358],
|
||||
[221,38],[295,450],[332,121],
|
||||
[375,290], // 13
|
||||
[375,210],
|
||||
[378,397],[429,52],[451,183],[445,459],[461,323]
|
||||
],
|
||||
"connections": [[6,13]]
|
||||
};
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
|
||||
var instruction1 = new Image();
|
||||
instruction1.src = "img/instruction_connect.png";
|
||||
var instruction2 = new Image();
|
||||
instruction2.src = "img/instruction_disconnect.png";
|
||||
function _preUpdate(){
|
||||
ctx.drawImage(instruction1, 0, 0, 500, 500);
|
||||
ctx.drawImage(instruction2, 0, 0, 500, 500);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update
|
||||
update();
|
|
@ -1,48 +0,0 @@
|
|||
PEEP_STATE_COLORS[2] = "#eebb55";
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0.5,
|
||||
"peeps":[[128,122,2],[239,84,2],[356,131,2],[74,224,1],[95,333,1],[170,401,1],[286,411,1],[376,342,1],[403,236,1]],
|
||||
"connections":[]
|
||||
}
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// WINNER? Only if ALL peeps think drinking is in the majority
|
||||
var progress = 0;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.numFriends>0 &&peep.numInfectedFriends/peep.numFriends>=0.5){
|
||||
progress++;
|
||||
}
|
||||
});
|
||||
YOU_ARE_WINNER = (progress==9);
|
||||
|
||||
// Progress...
|
||||
var label = "FOOLED: "+progress+" out of 9 peeps";
|
||||
ctx.font = '14px sans-serif';
|
||||
ctx.fillStyle = PEEP_STATE_COLORS[2];
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(label, 250, 465);
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = PEEP_STATE_COLORS[2];
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180, 10);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180*(progress/9), 10);
|
||||
ctx.fill();
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[72,85,2],[335,203,1],[417,256,1],[442,344,1],[374,421,1],[272,416,1],[205,330,1],[243,246,1],[111,232,1],[214,112,1]],
|
||||
"connections":[[8,9],[7,2],[2,6],[6,1],[7,6],[6,5],[5,1],[1,4],[3,1],[2,1],[1,7],[7,3],[7,4],[5,7],[6,3],[6,4],[5,2],[5,3],[5,4],[4,2],[4,3],[3,2]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff EVERYONE is infected!
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = everyoneIsInfected;
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":1/3, // DIFFERENT
|
||||
"peeps":[[72,85,2],[335,203,1],[417,256,1],[442,344,1],[374,421,1],[272,416,1],[205,330,1],[243,246,1],[111,232,1],[214,112,1]],
|
||||
"connections":[[8,9],[7,2],[2,6],[6,1],[7,6],[6,5],[5,1],[1,4],[3,1],[2,1],[1,7],[7,3],[7,4],[5,7],[6,3],[6,4],[5,2],[5,3],[5,4],[4,2],[4,3],[3,2]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff EVERYONE is infected!
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = everyoneIsInfected;
|
||||
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
/*var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[199,165,1],[292,165,1],[146,251,1],[347,251,1],[202,333,1],[297,334,1]],
|
||||
"connections":[]
|
||||
}*/
|
||||
var initData = {
|
||||
"contagion":0,
|
||||
"peeps":[[199,165,1],[292,165,1],[146,251,1],[347,251,1],[202,333,1],[297,334,1]],
|
||||
"connections":[[4,2],[2,0],[0,1],[1,3],[3,5],[5,4]]
|
||||
};
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
/////////////////////
|
||||
// TEST EVERY PEEP //
|
||||
/////////////////////
|
||||
|
||||
var PEEP_TO_INFECT = 0;
|
||||
function _startSimulation(){
|
||||
PEEP_TO_INFECT = 0;
|
||||
var peep = peeps[PEEP_TO_INFECT];
|
||||
peep.infect();
|
||||
peep._hack_TESTED = true;
|
||||
}
|
||||
function _stopSimulation(){
|
||||
peeps.forEach(function(peep){
|
||||
peep._hack_TESTED = false;
|
||||
});
|
||||
}
|
||||
function _stepSimulation(){
|
||||
|
||||
// Everyone infected?
|
||||
var everyoneIsInfected = _isEveryoneInfected();
|
||||
|
||||
// If everyone infected, reset! and increment.
|
||||
if(everyoneIsInfected){
|
||||
PEEP_TO_INFECT++;
|
||||
if(peeps[PEEP_TO_INFECT]){
|
||||
SIM_STEP = 0;
|
||||
_resetToBeforeSimStarted();
|
||||
var peep = peeps[PEEP_TO_INFECT];
|
||||
peep.infect();
|
||||
peep._hack_TESTED = true;
|
||||
}else{
|
||||
YOU_ARE_WINNER = true;
|
||||
}
|
||||
}else{
|
||||
|
||||
// Otherwise, keep on infecting.
|
||||
_infectPeople();
|
||||
|
||||
// If didn't infect in single step, you MESSED UP.
|
||||
everyoneIsInfected = _isEveryoneInfected();
|
||||
if(!everyoneIsInfected){
|
||||
setTimeout(function(){
|
||||
alert("Alas, you did not infect everyone in a SINGLE step!");
|
||||
_stopSim();
|
||||
},500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function _isEveryoneInfected(){
|
||||
var everyoneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state!=2) everyoneIsInfected=false;
|
||||
});
|
||||
return everyoneIsInfected;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3";
|
||||
|
||||
var initData = {
|
||||
"contagion":1/4,
|
||||
"peeps":[[199,165,1],[292,165,1],[146,251,1],[347,251,1],[202,333,1],[297,334,1]],
|
||||
"connections":[[4,2],[2,0],[0,1],[1,3],[3,5],[5,4]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
/////////////////////
|
||||
// TEST EVERY PEEP //
|
||||
/////////////////////
|
||||
|
||||
var PEEP_TO_INFECT = 0;
|
||||
var lastCount = 0;
|
||||
function _startSimulation(){
|
||||
PEEP_TO_INFECT = 0;
|
||||
peeps[PEEP_TO_INFECT].infect();
|
||||
peeps[PEEP_TO_INFECT]._hack_TESTED = true;
|
||||
lastCount = 1;
|
||||
}
|
||||
function _stopSimulation(){
|
||||
peeps.forEach(function(peep){
|
||||
peep._hack_TESTED = false;
|
||||
});
|
||||
}
|
||||
function _stepSimulation(){
|
||||
|
||||
_infectPeople();
|
||||
|
||||
// Did count stay the same?
|
||||
var countStayedTheSame = (lastCount == _numPeopleInfected());
|
||||
lastCount = _numPeopleInfected();
|
||||
|
||||
// If so, yay, next round
|
||||
if(countStayedTheSame){
|
||||
PEEP_TO_INFECT++;
|
||||
lastCount = 1;
|
||||
if(peeps[PEEP_TO_INFECT]){
|
||||
SIM_STEP = 0;
|
||||
_resetToBeforeSimStarted();
|
||||
var peep = peeps[PEEP_TO_INFECT];
|
||||
peep.infect();
|
||||
peep._hack_TESTED = true;
|
||||
}else{
|
||||
YOU_ARE_WINNER = true;
|
||||
}
|
||||
}else{
|
||||
|
||||
// If everyone's infected, FAIL.
|
||||
var everyoneIsInfected = (_numPeopleInfected()==6);
|
||||
if(everyoneIsInfected){
|
||||
setTimeout(function(){
|
||||
alert("Alas, everyone's infected!");
|
||||
_stopSim();
|
||||
},500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _numPeopleInfected(){
|
||||
var count = 0;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state==2) count++;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
PEEP_STATE_COLORS[1] = "#BF5FFF"; // purple
|
||||
PEEP_STATE_COLORS[2] = "#83F52C"; // green
|
||||
_hack_SHOW_BOTH_STATES = true;
|
||||
|
||||
var initData = {
|
||||
"contagion":0.75,
|
||||
"contagion2":0.95,
|
||||
"peeps":[[162,99,1],[70,183,1],[70,301,1],[141,408,1],[357,100,2],[439,183,2],[432,305,2],[358,408,2]],
|
||||
"connections":[[0,1],[1,2],[2,3],[7,6],[6,5],[5,4]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// WINNER? Only if ratio of SAME friends is 3/4<=x<1
|
||||
var progress = 0;
|
||||
peeps.forEach(function(peep){
|
||||
var sameFriendCount = 0;
|
||||
var friends = getConnected(peep);
|
||||
friends.forEach(function(friend){
|
||||
if(friend.state==peep.state) sameFriendCount++;
|
||||
});
|
||||
var sameFriendRatio = sameFriendCount/friends.length;
|
||||
if(0.75<=sameFriendRatio && sameFriendRatio<=0.95){
|
||||
progress++;
|
||||
}
|
||||
});
|
||||
YOU_ARE_WINNER = (progress==8);
|
||||
|
||||
// Progress...
|
||||
var label = "SOLVED: "+progress+" out of 8 peeps";
|
||||
ctx.font = '14px sans-serif';
|
||||
ctx.fillStyle = "#888";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(label, 250, 465);
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = "#888";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180, 10);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.rect(160, 470, 180*(progress/8), 10);
|
||||
ctx.fill();
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
PEEP_STATE_COLORS[2] = "#eebb55"; // yellow
|
||||
_hack_SHOW_BOTH_STATES = true;
|
||||
_hack_REINTEGRATION_PUZZLE = true;
|
||||
|
||||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":1/3,
|
||||
"peeps":[[237,70,1],[67,125,1],[172,189,1],[315,185,1],[432,119,1],[249,342,2],[160,381,2],[335,396,2]],
|
||||
"connections":[[5,6],[2,0],[0,1],[0,3],[4,0],[5,7]]
|
||||
};
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
function _onUpdate(){
|
||||
|
||||
// Winner iff NO ONE is infected!
|
||||
var nooneIsInfected = true;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state==2) nooneIsInfected=false;
|
||||
});
|
||||
YOU_ARE_WINNER = nooneIsInfected;
|
||||
|
||||
}
|
||||
|
||||
function _infectPeople(){
|
||||
|
||||
// Consider all peeps, and their friends
|
||||
peeps.forEach(function(peep){
|
||||
|
||||
// How many infected friends?
|
||||
if(peep.numFriends==0) return; // No friends? NVM.
|
||||
var ratioOfInfectedFriends = peep.numInfectedFriends/peep.numFriends;
|
||||
|
||||
// If susceptible, if %>=1/3 of friends infected, get infected
|
||||
// If infected, if %<1/3 of friends not infected, get not infected
|
||||
peep._NEXT_STATE = peep.state; // default
|
||||
if(peep.state==1){
|
||||
if(ratioOfInfectedFriends>=1/3){
|
||||
peep._NEXT_STATE = 2;
|
||||
}
|
||||
}
|
||||
if(peep.state==2){
|
||||
if(ratioOfInfectedFriends<=1/3){
|
||||
peep._NEXT_STATE = 1;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// "Infect" the peeps who need to get infected
|
||||
peeps.forEach(function(peep){
|
||||
peep.state = peep._NEXT_STATE;
|
||||
});
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
canvas.style.width = 500;
|
||||
canvas.style.height = 500;
|
||||
canvas.width = parseInt(canvas.style.width)*2;
|
||||
canvas.height = parseInt(canvas.style.height)*2;
|
||||
|
||||
var initData = {
|
||||
"contagion":1/4,
|
||||
"peeps":[[174,87,1],[319,84,1],[195,183,1],[297,178,1],[143,268,1],[60,315,1],[113,404,1],[196,342,1],[299,327,1],[341,250,1],[443,296,1],[378,394,1]],
|
||||
"connections":[[2,0],[0,1],[1,3],[3,2],[0,3],[2,1]]
|
||||
}
|
||||
_makeUncuttable(initData.connections);
|
||||
|
||||
// Add peeps!
|
||||
loadNetwork(initData);
|
||||
|
||||
// Update
|
||||
update();
|
||||
|
||||
// SHOW CONTAGION UI
|
||||
showContagionUI();
|
||||
|
||||
/////////////////////
|
||||
// TEST EVERY PEEP //
|
||||
/////////////////////
|
||||
|
||||
var _ticker = 0;
|
||||
var TEST_STAGE = 1;
|
||||
var TESTS_PASSED = 0;
|
||||
var infectedLastCount = 1;
|
||||
function _onUpdate(){
|
||||
|
||||
if(TEST_STAGE==1){
|
||||
PEEP_STATE_COLORS[2] = "#8b9dc3"; // cobalt
|
||||
}
|
||||
if(TEST_STAGE==2){
|
||||
PEEP_STATE_COLORS[2] = "#dd4040"; // red
|
||||
}
|
||||
|
||||
if(SIM_IS_RUNNING){
|
||||
_ticker++;
|
||||
if(_ticker>7){
|
||||
|
||||
// Infect people
|
||||
_ticker = 0;
|
||||
_infectPeople();
|
||||
var countChanged = (infectedLastCount<_numPeopleInfected());
|
||||
infectedLastCount = _numPeopleInfected();
|
||||
|
||||
// If Test Stage = 1 (25% contagion) it SHOULD infect everyone
|
||||
// pass only when it does
|
||||
if(TEST_STAGE==1){
|
||||
if(_numPeopleInfected()==peeps.length){
|
||||
TESTS_PASSED++;
|
||||
_nextPeepTest();
|
||||
if(TESTS_PASSED==peeps.length){
|
||||
alert("Okay... now testing 33% contagion threshold...");
|
||||
TEST_STAGE=2; // NEXT STAGE
|
||||
CONTAGION_THRESHOLD = 1/3; // it's 33% THRESHOLD NOW
|
||||
TESTS_PASSED = 0;
|
||||
PEEP_TO_INFECT = 0;
|
||||
_nextPeepTest();
|
||||
}
|
||||
}else if(!countChanged){
|
||||
alert("Alas! You did NOT infect everyone at 25% contagion threshold");
|
||||
_stopSim();
|
||||
}
|
||||
}
|
||||
|
||||
// If Test Stage = 2 (33% contagion) it SHOULD NOT infect everyone
|
||||
// pass only when it stays the same
|
||||
if(TEST_STAGE==2){
|
||||
if(!countChanged){
|
||||
TESTS_PASSED++;
|
||||
_nextPeepTest();
|
||||
if(TESTS_PASSED==peeps.length){
|
||||
alert("WIN!");
|
||||
YOU_ARE_WINNER = true;
|
||||
_stopSim();
|
||||
}
|
||||
}else if(_numPeopleInfected()==peeps.length){
|
||||
alert("Alas! You've infected everyone at 33% contagion threshold");
|
||||
_stopSim();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var PEEP_TO_INFECT = 0;
|
||||
var _nextPeepTest = function(){
|
||||
_resetToBeforeSimStarted();
|
||||
if(TEST_STAGE==2) CONTAGION_THRESHOLD=1/3; // hack
|
||||
var peep = peeps[PEEP_TO_INFECT];
|
||||
if(!peep) return false;
|
||||
peep.infect();
|
||||
peep._hack_TESTED = true;
|
||||
PEEP_TO_INFECT++;
|
||||
infectedLastCount = 1;
|
||||
return true;
|
||||
};
|
||||
|
||||
// at 25% contagion, it SHOULD infect everyone
|
||||
// at 33% contagion, it SHOULD NOT infect everyone
|
||||
|
||||
var lastCount = 0;
|
||||
function _startSimulation(){
|
||||
CONTAGION_THRESHOLD = 1/4;
|
||||
TEST_STAGE = 1;
|
||||
TESTS_PASSED = 0;
|
||||
PEEP_TO_INFECT = 0;
|
||||
_nextPeepTest();
|
||||
}
|
||||
function _stopSimulation(){
|
||||
CONTAGION_THRESHOLD = 1/4;
|
||||
}
|
||||
|
||||
function _numPeopleInfected(){
|
||||
var count = 0;
|
||||
peeps.forEach(function(peep){
|
||||
if(peep.state==2) count++;
|
||||
});
|
||||
return count;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
body{
|
||||
margin: 25px;
|
||||
background: #eee;
|
||||
|
||||
font-family: sans-serif;
|
||||
font-size: 19px;
|
||||
font-weight: 100;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
h2, h3{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#content{
|
||||
width:850px;
|
||||
height:530px;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top:0; left:0; right:0; bottom:0;
|
||||
}
|
||||
#content_puzzle{
|
||||
display: block;
|
||||
float: right;
|
||||
border:none;
|
||||
/*border: 1px solid #ddd;*/
|
||||
}
|
||||
#content_words{
|
||||
width: 325px;
|
||||
float: left;
|
||||
}
|
||||
#next{
|
||||
position: absolute;
|
||||
width: 325px;
|
||||
left:0; bottom:30px;
|
||||
text-align: right;
|
||||
font-size: 15px;
|
||||
}
|
||||
.icon{
|
||||
height: 1em;
|
||||
transform: translate(0,3px) scale(1.5, 1.5);
|
||||
}
|
||||
|
||||
slide{
|
||||
display:none;
|
||||
}
|
|
@ -1,418 +0,0 @@
|
|||
<!doctype>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="puzzle.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Content... -->
|
||||
<div id="content">
|
||||
<div id="content_words"></div>
|
||||
<div id="next">
|
||||
<button onclick="nextLevel()">NEXT →</button>
|
||||
</div>
|
||||
<iframe id="content_puzzle" width="500" height="500"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- SLIDES -->
|
||||
<slides>
|
||||
<slide level="0_draw_whatever">
|
||||
<h2>THE WISDOM and/or MADNESS of the CROWDS</h2>
|
||||
Why is it that the <i>same</i> people, in <i>different</i> groups, can be kind, cruel, smart, stupid?
|
||||
In this explorable explanation,
|
||||
I'll show how the <i>network</i> of a group itself can shape the people caught in its web.
|
||||
<br>
|
||||
<br>
|
||||
<b>
|
||||
Draw a network of friends! <img class="icon" src="img/gray.png"/> →
|
||||
When you're done playing around, press "next" ↘
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="1a_thresholds">
|
||||
An example. On the right,
|
||||
<img class="icon" src="img/yellow.png"/>
|
||||
represent binge-drinkers, and
|
||||
<img class="icon" src="img/gray.png"/>
|
||||
represent non-binge-drinkers.
|
||||
People look to their network of peers, to get a sense of how many of their friends do something.
|
||||
<br><br>
|
||||
(the number over their heads show what % of their friends, <i>not counting themselves</i>,
|
||||
are binge-drinkers. #binge_drinking_friends/#friends)
|
||||
<br><br>
|
||||
<b>
|
||||
Play around to get a feel of this!
|
||||
Connect & disconnect people, and see how that changes their perception
|
||||
of how many people do X →
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="1b_majority">
|
||||
As important as friendships are, networks can <i>fool</i> people.
|
||||
A 1991 study showed that “virtually all students reported that their friends drank more than they did”. (which, if you think about it, is logically impossible!)
|
||||
In fact, it's possible to fool everyone that a majority of people do X, even if people who do X are in a minority:
|
||||
<br><br>
|
||||
<b>
|
||||
Puzzle! Fool <i>everyone</i> into thinking 50% OR MORE of their
|
||||
friends are binge-drinkers →
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="2a_sim">
|
||||
But of course, networks don't just <i>fool</i> people, they also <i>change</i> people.
|
||||
<b>"Contagions",</b> like beliefs and behaviors, spread from person to person through a network.
|
||||
<br><br>
|
||||
On the right, we start with one person
|
||||
<img class="icon" src="img/red.png"/>
|
||||
who believes a rumor. "Fake news", as the cool kids say.
|
||||
Every day, that person will pass the lie on to their friends.
|
||||
And they pass it on to <i>their</i> friends.
|
||||
And so on.
|
||||
<br><br>
|
||||
<b>
|
||||
Click START SIM to see the "contagion" spread
|
||||
(note: for now, a person adopts a contagion if AT LEAST ONE
|
||||
FRIEND does. we'll see other possibilities later) →
|
||||
</b>
|
||||
|
||||
</slide>
|
||||
<slide level="2b_simple_cascade">
|
||||
Now, we have a bunch of small, separate communities.
|
||||
<br><br>
|
||||
<b>
|
||||
Try to infect <i>everyone</i> with the rumor over time! <img class="icon" src="img/red.png"/>
|
||||
(Feel free to just hit "start sim" and try out many different networks again and again)
|
||||
→
|
||||
</b>
|
||||
(note: you can't cut the pre-existing connections)
|
||||
</slide>
|
||||
<slide>
|
||||
(someting something compounding <b>cascade effect</b>, something something knocking 'em down
|
||||
like ever-bigger dominos)
|
||||
<br><br>
|
||||
(something something about spread of madness through crowds, like
|
||||
2008 financial crisis or mass speculation or mobs or riots or whatever)
|
||||
</slide>
|
||||
<slide level="3a_complex">
|
||||
When an idea/behavior just needs a minimum of <i>one</i> friends to spread,
|
||||
it's called a <b>"simple contagion"</b>.
|
||||
However, some ideas are harder to grasp, some behaviors need more encouragement,
|
||||
and so these need <i>multiple</i> friends to spread —
|
||||
these are called <b>"complex contagions"</b>.
|
||||
<br><br>
|
||||
A false rumor <img class="icon" src="img/red.png"/> may be a simple contagion,
|
||||
but the complicated truth <img class="icon" src="img/blue.png"/> is
|
||||
a complex contagion.
|
||||
(head shows #infected_friends/#friends)
|
||||
<br><br>
|
||||
<b>On the right, a complex contagion that needs 25% OR MORE friends to spread.
|
||||
Play around to get a feel for it →
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="3b_complex_cascade">
|
||||
<b>
|
||||
Puzzle! Same as before, but now you have to spread a complex idea.
|
||||
<img class="icon" src="img/blue.png"/>
|
||||
(a person needs AT LEAST 25% of their friends to adopt the idea, before they do too)
|
||||
Try "infecting" everyone now!
|
||||
→
|
||||
</b>
|
||||
(again, feel free to hit "start sim" and just <i>try</i> a solution, as many times as you want)
|
||||
</slide>
|
||||
<slide level="3c_extinguish">
|
||||
So, is that the way to spread more complex ideas/behaviors?
|
||||
Just add <i>more</i> connections?
|
||||
<br><br>
|
||||
Well, no. While more connections can never hurt a <i>simple</i> contagion,
|
||||
they can hurt <i>complex</i> contagions!
|
||||
<br><br>
|
||||
<b>
|
||||
Puzzle! Same as before, but now, try to add to the network so it
|
||||
<i>prevents</i> the spread of a complex contagion!
|
||||
</b>
|
||||
</slide>
|
||||
<slide>
|
||||
(something something about <b>groupthink</b>, how too many connections squashes
|
||||
complex contagions due to conformity pressure. example: NASA and Challenger explosion)
|
||||
</slide>
|
||||
<slide level="4a_sweet_spot">
|
||||
(something something about how if you have too FEW connections,
|
||||
people are isolated and ideas can't spread
|
||||
but if you have too MANY connections,
|
||||
people are pressured by conformity and ideas can't grow.)
|
||||
<br><br>
|
||||
Top-left: too few connections, complex contagion doesn't spread to everyone.
|
||||
Top-right: too many connections, complex contagion can't spread to everyone.
|
||||
<br><br>
|
||||
<b>
|
||||
Make a small group network that's the sweet spot inbetween, and spreads the complex contagion
|
||||
<img class="icon" src="img/blue.png"/>
|
||||
to everyone →
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="4b_bridge">
|
||||
Within-group friendships are called <b>"bonding social capital"</b>.
|
||||
And you discovered, there's a sweet spot where people are connected enough
|
||||
to spread a complex idea, but not so much conformity squashes it.
|
||||
But how do ideas spread <i>between</i> groups?
|
||||
Between-group friendships are called <b>"bridging social capital"</b>,
|
||||
and, likewise, there's a sweet spot.
|
||||
<br><br>
|
||||
<b>
|
||||
Create a group in the top-left (remember your sweet spot?)
|
||||
and then create a bridge to spread the complex contagion to the other
|
||||
group →
|
||||
</b>
|
||||
</slide>
|
||||
<slide level="4c_small_world">
|
||||
<b>FINAL PUZZLE! Now, do both! Create a bunch of bonded communities <i>and</i>
|
||||
the bridges between them. →</b>
|
||||
</slide>
|
||||
<slide>
|
||||
(something someting <b>Small World Network</b>,
|
||||
something something "unity AND diversity", "e pluribus unum",
|
||||
etc etc)
|
||||
<br><br>
|
||||
(happy ending or whatever)
|
||||
</slide>
|
||||
|
||||
|
||||
|
||||
<!--slide level="1_majority" width="500" height="500">
|
||||
Networks change people, because networks <i>fool</i> people.
|
||||
There's a network paradox called the "Majority Illusion",
|
||||
where, even though the majority of people in a group don't do X,
|
||||
the majority of everyone's <i>friends</i> do X!
|
||||
<br><br>
|
||||
<b>
|
||||
Draw a network that fools <i>everyone</i> into thinking
|
||||
that <i>half or more</i> of their friends are binge-drinkers
|
||||
<img class="icon" src="img/yellow.png"/>
|
||||
→
|
||||
</b>
|
||||
<br><br>
|
||||
When you solve it, press "next" ↓
|
||||
</slide>
|
||||
<slide>
|
||||
Just like how you see the earth as flat because you're <i>on</i> it,
|
||||
people can get wrong ideas about society because they're <i>in</i> it.
|
||||
<br><br>
|
||||
But, you may ask, it can't <i>all</i> be about the network of a group —
|
||||
what about culture, information, ideology? And you'd be correct!
|
||||
But if ideas flow from person to person, they have to flow through <i>something</i> —
|
||||
and ideas flow through the pipes of social connections, arranged in a network.
|
||||
</slide>
|
||||
<slide level="2_simple_contagion" width="500" height="500">
|
||||
So, let's explore that idea.
|
||||
To your left, there's just one person
|
||||
<img class="icon" src="img/red.png"/>
|
||||
who has some information.
|
||||
Some <i>mis</i>information. "Fake news", as the cool kids say.
|
||||
And every day, that person will pass the lie on to their friends.
|
||||
And they'll pass it on to <i>their</i> friends.
|
||||
And so on.
|
||||
<br><br>
|
||||
<b>
|
||||
Draw a network so that eventually <i>everyone</i> gets infected with the lie!
|
||||
<img class="icon" src="img/red.png"/>
|
||||
(note: dark-black lines can't be erased)
|
||||
Click "start sim" and "next step" to move the sim forward →
|
||||
</b>
|
||||
</slide>
|
||||
<slide>
|
||||
So far, I've said nothing really that new.
|
||||
People live in social networks, and sometimes ideas (even wrong ideas) go "viral".
|
||||
Old news, so what?
|
||||
<br><br>
|
||||
But there's a new recent finding in network science, that few people know about
|
||||
but more should. Almost all viruses and day-to-day information act as what is called
|
||||
<b>"simple contagions":</b> you only need <i>one</i> contact to catch it and spread it.
|
||||
</slide>
|
||||
<slide level="3_complex_contagion" width="500" height="500">
|
||||
But it turns out, the spread of <i>complicated ideas</i> and <i>actual behavior</i> act more like
|
||||
<b>"complex contagions":</b> you need <i>more than some % of your friends</i> to be
|
||||
"infected" before you too can be "infected".
|
||||
(And the more complex the new idea/behavior, the higher the threshold for % of friends
|
||||
needed to spread the contagion)
|
||||
<br><br>
|
||||
<b>
|
||||
People are now more skeptical.
|
||||
They only believe a rumor when 1/3 or more of their friends believe it.
|
||||
<img class="icon" src="img/red.png"/>
|
||||
Try to infect everyone <i>now</i>. →
|
||||
</b>
|
||||
</slide>
|
||||
<slide>
|
||||
This kind of behavior is called an <b>information cascade</b>,
|
||||
which can explain crowd madnesses like the 2008 housing bubble.
|
||||
Risky investment may be thought of as a "complex contagion" —
|
||||
you won't dive in if just <i>one</i> other friend does...
|
||||
but you may consider it if a significant % of your friends do.
|
||||
<br><br>
|
||||
And as you saw before, <i>even with a higher level of skepticism</i>,
|
||||
bad information and behaviors can still drive a crowd to madness.
|
||||
</slide>
|
||||
<slide level="4_simple_group" width="500" height="500">
|
||||
Okay, you may say, so "complex contagions" are just "simple contagions",
|
||||
but tougher. Not quite. Let's look at the next two puzzles...
|
||||
this time, about the spread of <i>good</i> ideas!
|
||||
|
||||
<br><br>
|
||||
Here's a team of six people.
|
||||
<i>Any one</i> of them could come up with a brilliant idea.
|
||||
(note: here, the idea is a "simple contagion" — you just need one "infected" friend)
|
||||
|
||||
<br><br>
|
||||
<b>
|
||||
Connect the peeps so that a new idea,
|
||||
<img class="icon" src="img/blue.png"/>
|
||||
from any person, spreads to everyone else
|
||||
in ONE turn. →
|
||||
</b>
|
||||
|
||||
</slide>
|
||||
<slide>
|
||||
Of course, just connect everyone to everyone else!
|
||||
When everyone's connected, ideas can flow as freely as dysentery.
|
||||
<br><br>
|
||||
Simple contagions are simple ideas, clickbait, the kind of stuff you'd happily and easily share.
|
||||
But now, let's ask — what if the idea was a <i>complex contagion?</i>
|
||||
Complex contagions are complex ideas, tough questions, actions that take actual effort...
|
||||
</slide>
|
||||
<slide level="5_complex_group" width="500" height="500">
|
||||
|
||||
Same puzzle as before. But now, the (complex) idea will only spread to a person
|
||||
if 1/6 or more of their friends believe it.
|
||||
|
||||
<br><br>
|
||||
<b>
|
||||
Connect the peeps so that a new idea,
|
||||
<img class="icon" src="img/blue.png"/>
|
||||
from any person, will <i>NOT</i> spread to everyone else.
|
||||
→
|
||||
</b>
|
||||
|
||||
</slide>
|
||||
<slide>
|
||||
...Of course. Just connect everyone to everyone else.
|
||||
When everyone's connected, conformity takes over, and squashes any complex, hard-to-swallow ideas.
|
||||
<br><br>
|
||||
This can explain moments of <b>groupthink</b>, like the Space Shuttle <i>Challenger</i> explosion,
|
||||
which killed 7 people.
|
||||
NASA, a team of smart individuals — literally rocket scientists —
|
||||
failed to prevent the disaster caused by <i>cold weather</i>,
|
||||
and <i>despite</i> multiple warnings from the engineers to the managers that this would happen.
|
||||
</slide>
|
||||
<slide>
|
||||
And <i>that's</i> the most profound difference between simple and complex contagions.
|
||||
What works for simple contagions,
|
||||
can be <i>terrible</i> for complex contagions.
|
||||
<br><br>
|
||||
You're using the internet to read this.
|
||||
Long ago, a lot of us thought "connect everyone to everyone"
|
||||
would unleash the wisdom of the crowds!
|
||||
And in many cases, it did!
|
||||
But, more often, it releases the madness of crowds.
|
||||
"Connect everyone" create conditions to spread simple contagions,
|
||||
but creates echo chambers that crush complex contagions.
|
||||
</slide>
|
||||
<slide>
|
||||
So... what now?
|
||||
<br><br>
|
||||
All this time I've been making you create networks that <i>hurt</i> people.
|
||||
Now, let's see how you can make networks that <i>help</i> people!
|
||||
</slide>
|
||||
<slide level="6_depolarization" width="500" height="500">
|
||||
Let's say we're got a polarized society — between two classes, or cultures,
|
||||
or political identities, whatever.
|
||||
<img class="icon" src="img/purple.png"/>
|
||||
<img class="icon" src="img/green.png"/>
|
||||
Like in the first puzzle,
|
||||
everyone's fooled into thinking their own type is in the majority —
|
||||
actually, not just majority, but <i>entirety</i>.
|
||||
<br><br>
|
||||
<b>
|
||||
Connect them so that, for each person,
|
||||
<i>75% to 95%</i> of their friends
|
||||
are similar to them. →</b>
|
||||
<br>
|
||||
Enough similar friends to have a sense of identity,
|
||||
but enough dissimilar friends so they're not <i>just</i> their identity.
|
||||
</b>
|
||||
</slide>
|
||||
<slide>
|
||||
Nice! Notice you have two main "kinds" of connections here:
|
||||
within-group and between-group connections — also known as
|
||||
<b>bonding</b> and <b>bridging</b> social capital.
|
||||
<br><br>
|
||||
A healthy society needs both.
|
||||
If people <i>only</i> bond within their groups, you get insular tribes.
|
||||
If people <i>only</i> bond outside their groups, you have no strong communities.
|
||||
</slide>
|
||||
<slide level="7_reintegrate" width="500" height="500">
|
||||
So far, we've only been looking at cases
|
||||
with just one "contagion". But what if you have two <i>competing</i> contagions?
|
||||
<br><br>
|
||||
Here, we have a simulation of opioid users
|
||||
<img class="icon" src="img/yellow.png"/>
|
||||
and non-opioid users.
|
||||
<img class="icon" src="img/gray.png"/>
|
||||
And let's say that the odds are stacked <i>against</i> non-users.
|
||||
If <i>2/3 or more</i> of a user's friends are non-users, they'll quit.
|
||||
But, if just <i>1/3 or more</i> of a non-user's friends are users, they'll start using.
|
||||
<br><br>
|
||||
<b>
|
||||
Create a network that helps get <i>everybody</i> to quit. →
|
||||
</b>
|
||||
|
||||
</slide>
|
||||
<slide>
|
||||
If you only connected the non-users to the users (bridging)
|
||||
but without connecting the non-users to each other first (bonding),
|
||||
you'd end up with <i>everyone</i> using.
|
||||
<br><br>
|
||||
The answer, as you found once again, was to have <i>both</i>
|
||||
bonding <i>and</i> bridging connections! Interesting...
|
||||
<br><br>
|
||||
Okay. One last, final puzzle...
|
||||
</slide>
|
||||
<slide level="8_complex_filter" width="500" height="500">
|
||||
Sometimes, an idea is counterintuitive because it's genuinely revolutionary.
|
||||
Other times, an idea is counterintuitive because it's just bull<span style="background:#000">fuck</span>.
|
||||
<br><br>
|
||||
How can we create a society with a <i>healthy</i> amount of skepticism?
|
||||
<b>
|
||||
Create a network that allows contagions with a 25%-or-more-of-friends threshold to pass to everyone,
|
||||
<img class="icon" src="img/blue.png"/>
|
||||
but NOT allow contagions with a 33%-or-more-of-friends threshold to pass to everyone.
|
||||
<img class="icon" src="img/red.png"/>
|
||||
→
|
||||
</b>
|
||||
<br>
|
||||
(remember: bonding & bridging...)
|
||||
</slide>
|
||||
<slide>
|
||||
Ta-da! And that's how you can maximize the wisdom of crowds,
|
||||
while minimizing the madness of crowds — a healthy balance
|
||||
of bonding <i>and</i> bridging.
|
||||
<br><br>
|
||||
So the next time you're horrified by how cruel or stupid people are acting,
|
||||
have humility, for we can all be swept up by mad crowds.
|
||||
But also, have hope, for we can all be swept up by wise crowds, too.
|
||||
<br><br>
|
||||
The question then is: what crowds will <i>you</i> connect with?
|
||||
</slide>
|
||||
<slide>
|
||||
<i>
|
||||
“The great triumphs and tragedies of history are caused,
|
||||
not by people being fundamentally good or fundamentally bad,
|
||||
but by people being fundamentally people.”
|
||||
</i>
|
||||
<br>
|
||||
~<i>Good Omens</i>, by Neil Gaiman & Terry Pratchett
|
||||
<br><br>
|
||||
<b>THE END</b>
|
||||
</slide-->
|
||||
</slides>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<script src="puzzle.js"></script>
|
|
@ -1,29 +0,0 @@
|
|||
var currentSlide = 0;
|
||||
|
||||
function loadSlide(index){
|
||||
|
||||
var slide = $("slides").children[index];
|
||||
if(!slide) return;
|
||||
|
||||
if(slide.getAttribute("level")){
|
||||
var iframe = $("#content_puzzle");
|
||||
iframe.src = "game/game.html?level="+slide.getAttribute("level");
|
||||
//iframe.width = slide.getAttribute("width");
|
||||
//iframe.height = slide.getAttribute("height");
|
||||
}
|
||||
|
||||
var words = $("#content_words");
|
||||
words.innerHTML = slide.innerHTML;
|
||||
|
||||
}
|
||||
|
||||
function $(query){
|
||||
return document.querySelector(query);
|
||||
}
|
||||
|
||||
function nextLevel(){
|
||||
currentSlide++;
|
||||
loadSlide(currentSlide);
|
||||
}
|
||||
|
||||
loadSlide(currentSlide);
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 240 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 687 KiB After Width: | Height: | Size: 687 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |