Skip to content

Instantly share code, notes, and snippets.

@EconomiCurtis
Created April 18, 2017 04:00
Show Gist options
  • Save EconomiCurtis/e2ee24a2d56b4b77c05313b2012b4280 to your computer and use it in GitHub Desktop.
Save EconomiCurtis/e2ee24a2d56b4b77c05313b2012b4280 to your computer and use it in GitHub Desktop.
Start.js for bubbles vcm
Redwood.controller("SubjectCtrl", ["$rootScope", "$scope", "RedwoodSubject", 'SynchronizedStopWatch', function($rootScope, $scope, rs, SynchronizedStopWatch) {
//Controls tick frequency for refreshing of flow chart
var CLOCK_FREQUENCY = 7;
var LOG_FREQUENCY = 7;
//Controls how often the slider is allowed
// to update the user's value. In ms.
var SLIDER_REFRESH_TIME = 60;
$scope.actionShow = false;
$scope.flowShow = false;
$scope.histShow = false;
$scope.actions = [];
$scope.discreteActions = [];
$scope.targets = [];
$scope.discreteTargets = [];
$scope.colors = [
"rgba(183, 24, 77, 0.7)",
"rgba(1, 116, 247. 0.7)",
'rgba(242, 247, 1, 0.7)',
"rgba(247, 214, 1, 0.7)",
"rgba(197, 1, 247, 0.7)",
"rgba(196, 122, 3, 0.7)",
'rgba(66, 134, 244, 0.7)',
'rgba(73, 255, 215, 0.7)',
'rgba(255, 73, 233, 0.7)'];
$scope.myColor = "rgba(93, 187, 0, 0.9)";
$scope.data = [];
$scope.ids = [];
$scope.payoffs = [];
$scope.subPeriodNum = -1;
$scope.NUMHORIZLINES = 5;
rs.on_load(function() {
$scope.myNum ="";
$scope.text = "x: 0";
$scope.accPayoffText = "Accumulated Rewards: " + rs.accumulated_points.toFixed(2);
$scope.clock = SynchronizedStopWatch.instance()
.frequency(CLOCK_FREQUENCY).onTick(processTick)
.duration(rs.config.period_length_s).onComplete(function() {
rs.trigger("move_on");
});
$scope.logConfig(rs.user_id);
$scope.colors = shuffleArray($scope.colors);
if (rs.config.num_sub_periods == 0) {
$scope.continousGame = true;
} else {
$scope.continousGame = false;
}
var numSubPeriods = rs.config.num_sub_periods || (rs.config.period_length_s * CLOCK_FREQUENCY);
$scope.throttleStep = rs.config.step || 0;
$scope.snapDistance = rs.config.snap || 0.001;
$scope.hidePayoffs = rs.config.hidePayoffs || false;
$scope.histShow = rs.config.actionHistory || false;
$scope.payoffHorizon = rs.config.payoffProjection || false;
$scope.qone = parseFloat(rs.config.q1);
$scope.qtwo = parseFloat(rs.config.q2);
console.log("q1: " + $scope.qone + " q2: " + $scope.qtwo);
$scope.qthree = parseFloat((1 - $scope.qone - $scope.qtwo).toFixed(2));
if ($scope.qthree == 0.01) $scope.qthree = 0;
console.log("q1: " + $scope.qone + " q2: " + $scope.qtwo + " q3: " + $scope.qthree);
$scope.mu = rs.config.mu;
$scope.ticksPerSubPeriod = Math.max(Math.floor(rs.config.period_length_s * CLOCK_FREQUENCY / numSubPeriods), 1);
$scope.minX = rs.config.minX || 0;
$scope.maxX = rs.config.maxX || 1;
$scope.adjustAccuracy = parseFloat(rs.config.adjustAccuracy) || .01;
$scope.payoffTypeText = "Game Type: " + rs.config.payoffLabel;
var currSlideTime = new Date().getTime();
$scope.stepSize = rs.config.maxX/rs.subjects.length;
// $scope.initialActions = rs.config.initialActions;
// $scope.initialActions = $scope.initialActions.replace('[', '').replace(']', '').split(','); //$scope.initialActions = [];
$scope.initialActions = [0,0,0,0];
$scope.yMax = rs.config.ymax;
$scope.yMax = ((rs.subjects.length - 1) * $scope.maxX * $scope.mu) + $scope.maxX;
for (var i = 0; i < $scope.initialActions.length; i++) {
console.log("Initial Action at: " + i + " is " + parseFloat($scope.initialActions[i]));
$scope.initialActions[i] = parseFloat($scope.initialActions[i]);
}
console.log("ACTIONS");
console.log($scope.initialActions);
//initialize everyone's actions and targets
for (var i = 0; i < rs.subjects.length; i++) {
$scope.actions[i] = $scope.initialActions[i];
$scope.discreteActions[i] = $scope.initialActions[i];
$scope.targets[i] = $scope.initialActions[i];
$scope.discreteTargets[i] = $scope.initialActions[i];
var id = rs.subjects[i].user_id;
var index = $scope.indexFromId(id);
if (index == $scope.indexFromId(rs.user_id)) {
$scope.myInitialAction = $scope.initialActions[i];
}
$scope.ids[index] = id;
}
$("#slider").slider({
value: $scope.myInitialAction,
min: $scope.minX,
max: $scope.maxX,
step: $scope.adjustAccuracy,
slide: function(event, ui) {
var nowSlide = new Date().getTime();
var diff = nowSlide - currSlideTime;
//If this wasn't here, everytime a user changed selection by 0.1 the code
//would fire redwood messages and overload the router. This way, we check to see
//if it's been at least SLIDER_REFRESH_TIME since the last time the slide action is
//fired. Set via parameter in ms.
if (diff > SLIDER_REFRESH_TIME) {
$scope.text = "x: " + ui.value;
var msg = { "action": ui.value };
rs.trigger("updateAction", msg);
rs.send("updateAction", msg);
//we've slid
currSlideTime = new Date().getTime();
} else { //otherwise, let's set some temp flags incase we tick in between
$scope.text = "x: " + ui.value;
$scope.targets[$scope.indexFromId(rs.user_id)] = ui.value;
}
},
change: function( event, ui ) {
$scope.text = "x: " + ui.value;
var msg = { "action": ui.value };
rs.trigger("updateAction", msg);
rs.send("updateAction", msg);
}
});
$scope.actionShow = true;
$scope.flowShow = true;
$scope.rewards = [];
$scope.opponentRewards = [];
$scope.roundPayoff = 0;
$scope.bgColor = "white";
$scope.loaded = true;
$scope.dev_log("calculated index" + $scope.indexFromId(rs.user_id));
$scope.dev_log(rs);
$scope.clock.start();
});
rs.on("move_on", function(msg) {
$scope.bgColor = "#ccc";
$scope.showEnding = true;
$("#slider").slider("disable");
rs.next_period(10);
});
rs.recv("updateAction", function(uid, msg) {
var index = $scope.indexFromId(uid)
$scope.dev_log("updating another's action at index: " + index);
$scope.targets[index] = msg.action;
$scope.opponentAction = msg.action;
});
rs.on("updateAction", function(msg) {
var index = $scope.indexFromId(rs.user_id);
$scope.dev_log("updating my action at index: " + index);
$scope.targets[index] = msg.action;
$scope.myAction = msg.action;
});
var processTick = function(tick) {
// End of a sub period (in the "continuous" version, every tick is the end of a sub period)
if (tick % $scope.ticksPerSubPeriod === 0) {
if (rs.config.num_sub_periods != 0) {
$scope.subPeriodNum++;
rs.send("endofsubperiod", {});
}
var reward = $scope.payoffFunction($scope.indexFromId(rs.user_id));
$scope.rewards.push(reward);
rs.add_points(reward * $scope.ticksPerSubPeriod / $scope.clock.getDurationInTicks());
$scope.roundPayoff += (reward * $scope.ticksPerSubPeriod / $scope.clock.getDurationInTicks());
$scope.roundPayoffText = "Round Reward: " + $scope.roundPayoff.toFixed(2);
// Copy by value, not by reference so we can update them independently.
// The discrete arrays hold the values of everyone
// recorded at the end of each sub period
$scope.discreteActions = $scope.actions.slice();
$scope.discreteTargets = $scope.actions.slice();
}
// Discrete arrays need to always hold current info for the current user, they have knowledge
// of their own actions but not others. The others are updated above in the tick % tickspersubperiod conditional
// at the end of each subperiod
$scope.discreteTargets[$scope.indexFromId(rs.user_id)] = $scope.targets[$scope.indexFromId(rs.user_id)];
$scope.discreteActions[$scope.indexFromId(rs.user_id)] = $scope.actions[$scope.indexFromId(rs.user_id)];
// This allows us to advance a persons action by a given step and throttling
// amount. This action allows a person to only move by a certain step per tick
for (var i = 0; i < rs.subjects.length; i++) {
var targetDiff = Math.abs($scope.actions[i] - $scope.targets[i]);
/* If our difference is greather than the snap distance, and a throttle is set, let's throttle */
if (targetDiff > $scope.snapDistance && $scope.throttleStep != 0) {
var target = $scope.targets[i],
action = $scope.actions[i],
step = 0;
//deciding whether our step is going to be positive or negative
if (target > action) step = $scope.throttleStep;
else step = -$scope.throttleStep;
//positive step would set us above target
var stepPosBool = (step > 0) && ((action + $scope.throttleStep) > target);
//negative step would set us below target
var stepNegBool = (step < 0) && ((action - $scope.throttleStep) < target);
//if a step would place us above or below, snap to target
if (stepPosBool || stepNegBool) {
$scope.actions[i] = $scope.targets[i];
if ($scope.indexFromId(rs.user_id) == i) {
$scope.discreteActions[i] = $scope.discreteTargets[i];
}
} else { //else, we can move by a step
$scope.actions[i] = $scope.actions[i] + step;
if ($scope.indexFromId(rs.user_id) == i) {
$scope.discreteActions[i] = $scope.discreteActions[i] + step;
}
}
} else {
//otherwise no throttling and an action should instantaneously be their target
$scope.actions[i] = $scope.targets[i];
if ($scope.indexFromId(rs.user_id) == i) {
$scope.discreteActions[i] = $scope.targets[i];
}
}
}
console.log("about to log");
if (tick % $scope.ticksPerSubPeriod === 0) {
console.log("logging");
$scope.log(rs.user_id, tick);
}
//causes angular $watch trigger to redraw plots
$scope.tick = tick;
}
$scope.logCount = 0;
// data output messages logged only for one user
// eliminating redundant logging.
$scope.log = function(uid, tick) {
if ($scope.continousGame) {
if ($scope.logCount == LOG_FREQUENCY) {
$scope.logCount = 0;
} else {
$scope.logCount++;
return;
}
}
// Run logging with discreteAction data as state
// This works because discreteActions is equal to $scope.actions
// at the end of every subperiod because of the above 2 lines
$scope.VCMpayoff($scope.discreteActions);
$scope.data = [];
for (var i = 0; i < $scope.state.length; i++) {
var obj = $scope.state[i];
var index = $scope.indexFromId(rs.subjects[i].user_id);
var newObj = {
subjectid: obj.id + 1,
action: obj.action,
rank: obj.rank,
subperiodNumber: $scope.subPeriodNum,
payoff: obj.payoff
};
$scope.data.push(newObj);
}
//console.log($scope.data);
if ($scope.indexFromId(rs.user_id) == 0) {
console.log("for sure logging");
console.log($scope.data);
rs.send("state", {state: $scope.data});
rs.send("actions", {actions: $scope.actions});
rs.send("targets", {targets: $scope.targets});
}
}
$scope.logConfig = function(uid) {
if ($scope.indexFromId(rs.user_id) == 1) {
rs.send("LOG_CONFIG", rs.config);
}
}
$scope.payoffFunction = function(index) {
$scope.VCMpayoff($scope.discreteActions);
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.state[i].id == index) return $scope.state[i].payoff
}
}
$scope.discretePayoffFunction = function(index) {
$scope.VCMpayoff($scope.discreteActions);
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.state[i].id == index) return $scope.state[i].payoff
}
}
$scope.actionForI = function(index) {
$scope.VCMpayoff($scope.actions);
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.state[i].id == index) return $scope.state[i].action
}
}
$scope.payoffTargetFunction = function(index) {
$scope.VCMpayoff($scope.discreteTargets);
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.state[i].id == index) return $scope.state[i].payoff
}
}
$scope.payoffDiscreteTarget = function(index) {
$scope.VCMpayoff($scope.discreteTargets);
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.state[i].id == index) return $scope.state[i].payoff
}
}
$scope.state = [];
// takes an array of player locations either target or action depending on what we're plotting
//
// Array is formatted like so:
//
// [1.3, 1.2, 0.8, ... , n]
// where the first element correspons to player 1,
// second corresponds to player 2, etc. These indecies start at 0 but
// subject numbers start at 1, so we add one to index counter to get id.
$scope.VCMpayoff = function(array) {
// each time we run our payoff function, let's just rebuild state
// so we can use state to represent the payoff for different things.
$scope.state = [];
for (var i = 0; i < array.length; i++) {
var obj = {
"id": i, //rs user id since array stores from 0->n-1 where n is the number of players
"action": array[i],
"payoff": 0
};
$scope.state.push(obj);
}
// calc total group contribution
var totalcontrib = 0;
for (var i = 0; i < array.length; i++) {
totalcontrib = totalcontrib + array[i];
}
// calc individual payoffs
for (var i = 0; i < $scope.state.length; i++) {
var elem = $scope.state[i];
var payoff = 0;
payoff = $scope.maxX - elem.action + ($scope.mu * totalcontrib);
// set each element's payoff attribute
// to the calculated payoff. Therefor we calculate
// every single player's payoff, and can filter
// for a specific player's payoff later.
elem.payoff = payoff;
}
return $scope.state;
}
$scope.bjPricing = function(array) {
//each time we run our payoff function, let's just rebuild state
// so we can use state to represent the payoff for different things.
$scope.state = [];
for (var i = 0; i < array.length; i++) {
var obj = {
"id": i, //rs user id since array stores from 0->n-1 where n is the number of players
"action": array[i],
"rank": 0,
"payoff": 0
};
$scope.state.push(obj);
}
//sort descending
$scope.state.sort(function(a, b) {
return b.action - a.action;
});
//in the event of a tie, we need a counter to keep track so we advance
// rank in the case of a tie.
var extraRank = 0;
for (var i = 0; i < $scope.state.length; i++) {
var thiselem = $scope.state[i],
nextelem = $scope.state[i+1],
rank = i+1;
var index = $scope.indexFromId(i);
thiselem.subjectid = $scope.ids[index]
//if we're on the last element, and the rank hasn't been set yet
if (nextelem == null) {
if (thiselem.rank == 0) {
thiselem.rank = rank;
}
continue;
}
if (thiselem.action == nextelem.action) {
//in the event of a tie, they recieve rank equal to
// the average of the ranks they would recieve
thiselem.rank = nextelem.rank = ((rank) + (rank+1)) / 2;
//the next iteration needs to know we've had a tie so the i rank counter
// is no longer exactly correct
extraRank++;
} else if (thiselem.rank == 0) {
thiselem.rank = rank;
}
// else thiselem rank has already been set by the earlier loop iteration
// where there was a tie, and in that case both ranks have been set already
}
for (var i = 0; i < $scope.state.length; i++) {
var elem = $scope.state[i];
var payoff = 0;
var minusOne = (elem.rank-1) / (rs.subjects.length-1);
var minusTwo = (elem.rank-2) / (rs.subjects.length-2);
var rightTerm;
if (isNaN(minusTwo) || !isFinite(minusTwo)) {
rightTerm = 0;
} else {
rightTerm = Math.max(0, minusOne * minusTwo);
}
payoff = $scope.mu * elem.action * ($scope.qone + 2*$scope.qtwo*minusOne + 3*$scope.qthree*rightTerm);
// set each element's payoff attribute
// to the calculated payoff. Therefor we calculate
// every single player's payoff, and can filter
// for a specific player's payoff later.
elem.payoff = payoff;
}
return $scope.state;
}
$scope.indexFromId = function(id) {
var index = 0;
for (var i = 0; i < rs.subjects.length; i++) {
if (parseInt(rs.subjects[i].user_id) < id) index++;
}
return index;
}
$scope.logging = false;
$scope.dev_log = function(msg) {
if ($scope.logging) console.debug(msg);
}
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
}]);
//
// controls main actionspace
//
Redwood.directive('actionFlot', ['RedwoodSubject', function(rs) {
return {
link: function($scope, elem, attr) {
var actions = [],
subPeriods = [],
discreteActions = [],
loaded = false;
rs.on_load(function() {
init();
});
//initialize our actions data array starting everyone at (0,0)
function init() {
for (var i = 0; i < rs.subjects.length; i++) {
actions.push({
data: [ [0, 0] ],
points: { show: true },
color: $scope.colors[i]
});
}
loaded = true;
rebuild();
//console.log(elem);
}
$(elem).bind("onclick", function (event, pos, item) {
$scope.dev_log("clicked");
$scope.myNum("x: " + pos.x);
});
$(elem).bind("onhover", function (event, pos, item) {
$scope.dev_log("hovered");
});
$scope.$watch('bgColor', function() {
rebuild();
}, true);
$scope.$watch('tick', function(tick) {
rebuild();
}, true);
function rebuild() {
/* Flot data structure */
actions = [];
/* Main logic loop for building up data for each player */
for (var i = 0; i < rs.subjects.length; i++) {
var pt = [];
// payoff landscape
if ($scope.payoffHorizon && $scope.indexFromId(rs.user_id) == i) {
var projectionData = [];
var j = 0;
/*
Save the correct (current) target so we can
re-simulate the payoff function with a new target
*/
var targ = $scope.discreteActions[$scope.indexFromId(rs.user_id)];
while (j < 10) {
/* Set the target equal to a number between 0-10 */
$scope.discreteActions[$scope.indexFromId(rs.user_id)] = j;
/* Run the payoff function with the new (projected) target */
projectionData.push([j, $scope.discretePayoffFunction(i)]);
/* inrecement j to get the next projected payoff at new location j*/
j += $scope.adjustAccuracy;
}
/*
After we're done building our projection data, reset the target to the
correct (actual) value and append this data to our flot dataset
*/
$scope.actions[$scope.indexFromId(rs.user_id)] = targ;
$scope.discreteActions[$scope.indexFromId(rs.user_id)] = targ;
// payoff landscape line attributes
actions.push({
data: projectionData,
lines: {
lineWidth: 2
},
color: "#d4d4d4"
});
}
//If we're not on our target, also plot a grey target dot
if ($scope.actions[i] != $scope.targets[i] && $scope.indexFromId(rs.user_id) == i) {
//push the x coordinate as their target and the y coordinate as their target payoff
pt.push([$scope.discreteTargets[i], $scope.payoffTargetFunction(i) ])
actions.push({
data: pt,
points: {
show: true,
radius: 10,
lineWidth: 0.5,
fill: 0.2,
fillColor: "rgba(219, 219, 219, 0.5)"
},
color: "grey"
});
}
// plot circle for current action for this tick
pt = [];
if ( $scope.indexFromId(rs.user_id) == i ) {
//console.log("My payoff: " + $scope.discretePayoffFunction(i));
//console.log($scope.discreteActions);
pt.push([$scope.discreteActions[i], $scope.discretePayoffFunction(i) ]);
} else {
if ($scope.hidePayoffs) {
pt.push([$scope.discreteActions[i], 0 ])
} else {
pt.push([$scope.discreteActions[i], $scope.discretePayoffFunction(i) ])
}
}
var fillColor = $scope.colors[i];
if ( $scope.indexFromId(rs.user_id) == i ) {
actions.push({
data: pt,
points: {
show: true,
radius: 10,
lineWidth: 1,
fillColor: $scope.myColor,
fill: true,
fillOpacity:0.1
},
color: $scope.myColor
});
} else {
actions.push({
data: pt,
points: {
show: true,
radius: 10,
lineWidth: 0.01,
fill: true,
fillColor: $scope.colors[i]
},
color: $scope.colors[i]
});
}
}
var linedata = []
//Vertical line for selection or at 0,0 for start
linedata = [
[$scope.discreteActions[$scope.indexFromId(rs.user_id)], 0],
[$scope.discreteActions[$scope.indexFromId(rs.user_id)], $scope.yMax]
];
actions.push({
data: linedata,
lines: {
lineWidth: 1
},
color: $scope.myColor
});
replot();
}
function replot() {
if (!loaded) return;
var actionopts = {
xaxis: {
ticks: 0,
tickLength: 0,
min: $scope.minX,
max: $scope.maxX,
ticks: 10
},
yaxis: {
tickLength: 0,
min: 0,
max: $scope.yMax
},
series: {
shadowSize: 0
},
grid: {
markings: [],
backgroundColor: $scope.bgColor,
clickable: true
}
};
for (var i = 1; i < $scope.NUMHORIZLINES; i++) {
actionopts.grid.markings.push(
{
color: '#eee',
yaxis: {
from: i * ($scope.yMax/$scope.NUMHORIZLINES),
to: i * ($scope.yMax/$scope.NUMHORIZLINES)
}
}
);
}
$.plot(elem, actions, actionopts, {grid: {clickable: true}});
}
}
}
}]);
//
// controls flow payoff flot graph
//
Redwood.directive('flowflot', ['RedwoodSubject', function(rs) {
return {
link: function($scope, elem, attr) {
var plot = [],
flows = [[]],
opponentPlot = [],
subPeriods = [],
loaded = false;
rs.on_load(function() {
init();
});
function init() {
if ($scope.ticksPerSubPeriod > 1) {
var subPeriod = 0;
do {
subPeriod += $scope.ticksPerSubPeriod;
subPeriods.push(subPeriod / $scope.clock.getDurationInTicks());
} while (subPeriod < $scope.clock.getDurationInTicks());
}
for(var i = 0; i < rs.subjects.length; i++) {
flows[i] = [];
}
loaded = true;
$scope.replotFlow();
}
$scope.$watch('tick', function(tick) {
if (tick % $scope.ticksPerSubPeriod === 0) {
console.log("STATE: ");
console.log($scope.state);
for(var i = 0; i < rs.subjects.length; i++) {
if ($scope.indexFromId(rs.user_id) == i) {
//console.log("My payoff: " + $scope.discretePayoffFunction(i));
//console.log($scope.discreteActions);
}
var data = [ ($scope.tick - $scope.ticksPerSubPeriod) / $scope.clock.getDurationInTicks(), $scope.discretePayoffFunction(i) ];
flows[i].push(data);
var data = [ ($scope.tick) / $scope.clock.getDurationInTicks(), $scope.discretePayoffFunction(i) ];
flows[i].push(data);
}
}
$scope.replotFlow();
}, true);
//watch for end of period to change color of bg
$scope.$watch('bgColor', function() {
$scope.replotFlow();
}, true);
$scope.replotFlow = function() {
if (!loaded) return;
var xRange = 1;
var opts = {
xaxis: {
ticks: 0,
tickLength: 0,
min: 0,
max: xRange,
ticks: 10
},
yaxis: {
tickLength: 0,
min: 0,
max: $scope.yMax
},
series: {
shadowSize: 0
},
grid: {
markings: [
],
backgroundColor: $scope.bgColor
}
};
// grid lines
for (var i = 1; i < $scope.NUMHORIZLINES; i++) {
opts.grid.markings.push(
{
lines: {
lineWidth: 1
},
color: '#eee',
yaxis: {
from: i * ($scope.yMax/$scope.NUMHORIZLINES),
to: i * ($scope.yMax/$scope.NUMHORIZLINES)
}
}
);
}
// descrete time grid markers
var dataset = [];
for (var p = 0; p < subPeriods.length; p++) { //mark each sub-period with a vertical red line
dataset.push({
data: [
[subPeriods[p], opts.yaxis.min],
[subPeriods[p], opts.yaxis.max]
],
lines: {
lineWidth: 1
},
color: "rgba(214, 34, 34, 0.5)" //red
});
}
dataset.push({ //display the current time indicator as a vertical grey line
data: [
[$scope.tick / $scope.clock.getDurationInTicks(), opts.yaxis.min],
[$scope.tick / $scope.clock.getDurationInTicks(), opts.yaxis.max]
],
color: "grey"
});
/* First plot our own payoff data so we can shade it and put other payoffs ontop */
dataset.push({
data: flows[$scope.indexFromId(rs.user_id)],
lines: {
fill: true,
lineWidth: 2,
fillColor: $scope.myColor
},
color: $scope.myColor
});
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.indexFromId(rs.user_id) != i && !$scope.hidePayoffs) {
dataset.push({
data: flows[i],
lines: {
fill: false,
lineWidth: 3
},
color: $scope.colors[i]
});
}
}
console.log("STATE");
console.log($scope.state);
$.plot(elem, dataset, opts);
}
}
}
}]);
//
// controls flow action history flot graph
//
Redwood.directive('actionHistory', ['RedwoodSubject', function(rs) {
return {
link: function($scope, elem, attr) {
var plot = [],
flows = [[]],
opponentPlot = [],
subPeriods = [],
loaded = false;
rs.on_load(function() {
init();
});
function init() {
if ($scope.ticksPerSubPeriod > 1) {
var subPeriod = 0;
do {
subPeriod += $scope.ticksPerSubPeriod;
subPeriods.push(subPeriod / $scope.clock.getDurationInTicks());
} while (subPeriod < $scope.clock.getDurationInTicks());
}
for(var i = 0; i < rs.subjects.length; i++) {
flows[i] = [];
}
loaded = true;
$scope.replotHist();
}
$scope.$watch('tick', function(tick) {
if (tick % $scope.ticksPerSubPeriod === 0) {
for(var i = 0; i < rs.subjects.length; i++) {
var data = [ ($scope.tick - $scope.ticksPerSubPeriod) / $scope.clock.getDurationInTicks(), $scope.actionForI(i) ];
flows[i].push(data);
var data = [ ($scope.tick) / $scope.clock.getDurationInTicks(), $scope.actionForI(i) ];
flows[i].push(data);
}
}
$scope.replotHist();
}, true);
//watch for end of period to change color of bg
$scope.$watch('bgColor', function() {
$scope.replotHist();
}, true);
$scope.replotHist = function() {
if (!loaded) return;
var xRange = 1;
var opts = {
xaxis: {
ticks: 0,
tickLength: 0,
min: 0,
max: xRange,
ticks: 10
},
yaxis: {
tickLength: 0,
min: rs.config.minX,
max: rs.config.maxX
},
series: {
shadowSize: 0
},
grid: {
backgroundColor: $scope.bgColor
}
};
var dataset = [];
for (var p = 0; p < subPeriods.length; p++) { //mark each sub-period with a vertical red line
dataset.push({
data: [
[subPeriods[p], opts.yaxis.min],
[subPeriods[p], opts.yaxis.max]
],
lines: {
lineWidth: 1
},
color: "rgba(214, 34, 34, 0.5)" //red
});
}
dataset.push({ //display the current time indicator as a vertical grey line
data: [
[$scope.tick / $scope.clock.getDurationInTicks(), opts.yaxis.min],
[$scope.tick / $scope.clock.getDurationInTicks(), opts.yaxis.max]
],
color: "grey"
});
/* First plot our own payoff data so we can shade it and put other payoffs ontop */
dataset.push({
data: flows[$scope.indexFromId(rs.user_id)],
lines: {
fill: false,
lineWidth: 2,
fillColor: $scope.myColor
},
color: $scope.myColor
});
for (var i = 0; i < rs.subjects.length; i++) {
if ($scope.indexFromId(rs.user_id) != i && !$scope.hideActions) {
dataset.push({
data: flows[i],
lines: {
fill: false,
lineWidth: 3,
fillColor: $scope.colors[$scope.indexFromId(rs.user_id)]
},
color: $scope.colors[i]
});
}
}
$.plot(elem, dataset, opts);
}
}
}
}]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment