MOVE TO TOP

This commit is contained in:
Nicky Case 2018-04-06 10:38:02 -04:00
parent 9714fd8d06
commit 9261ee1a08
80 changed files with 0 additions and 3100 deletions

View File

@ -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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -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>

View File

@ -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);
}

View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -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);
}
}

View File

@ -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;
};
}

View File

@ -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();
}
};
}

View File

@ -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();
});
}

View File

@ -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);

View File

@ -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
}

View File

@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -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();

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;
});
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 &rarr;</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"/> &rarr;
When you're done playing around, press "next" &searr;
</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 &amp; disconnect people, and see how that changes their perception
of how many people do X &rarr;
</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 &rarr;
</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) &rarr;
</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)
&rarr;
</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 &mdash;
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 &rarr;
</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!
&rarr;
</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 &rarr;
</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 &rarr;
</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. &rarr;</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"/>
&rarr;
</b>
<br><br>
When you solve it, press "next" &darr;
</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 &mdash;
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> &mdash;
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 &rarr;
</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>. &rarr;
</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" &mdash;
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" &mdash; 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. &rarr;
</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 &mdash; 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.
&rarr;
</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 &mdash; literally rocket scientists &mdash;
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 &mdash; 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 &mdash;
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. &rarr;</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 &mdash; 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. &rarr;
</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"/>
&rarr;
</b>
<br>
(remember: bonding &amp; bridging...)
</slide>
<slide>
Ta-da! And that's how you can maximize the wisdom of crowds,
while minimizing the madness of crowds &mdash; 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 &amp; Terry Pratchett
<br><br>
<b>THE END</b>
</slide-->
</slides>
</body>
</html>
<script src="puzzle.js"></script>

View File

@ -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);

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 240 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 687 KiB

After

Width:  |  Height:  |  Size: 687 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB