<commit_before>
var globalInputs = [
    'Rata estimari',
    'Rata fluctuatii personal',
    'Resurse materiale'
];

var globalOutputs = [
    'Timp',
    'Cost'
    ];

var parametersPercent = [];
var coeficients = [];
var timeChanges = [];

var globalTimeFields = [1];

var globalParametersOutputInfluenceTime = [1, -1, 1];
var globalParametersOutputInfluenceCost = [-1, -1, 1];

var globalInputVar = 0;

var globalReferences = [];

var computeDenCoeficient = 1;

function addNewTimeField() {
    var position = globalTimeFields.length + 1;
    globalTimeFields.push(position);

    var content1 = "
<input data-prev='" + (position - 1) + "' data-next='" + (position + 1) + "' class='validate[required,custom[number],min[1],funcCall[verificareTimpMaxim]] form-control' type='text' id='time_" + (position) + "' placeholder='Timp'>
	";
    $("#time_input").append(content1);
    var content2 = "
<input data-prev='" + (position - 1) + "' data-next='" + (position + 1) + "' class='validate[required,custom[number],min[0],max[200]] form-control' type='text' id='percent_" + (position) + "' placeholder='%'>
	";
    $("#percent_input").append(content2);
}

function generateInputsFields() {
    var i = 1;
    generateNewParameterButton();
    globalInputs.forEach(function (input) {
        renderInput(i, input);
        renderSelect(i++, input);
    });
    i = 1;
    globalOutputs.forEach(function (label) {
        renderOutputSelect(i++, label);
    });
    renderInputTitle();

}

function renderInput(position, label) {
    var content = "
" + label + ":
<input type='text' name='parametru_" + position + "' id='parametru_" + position + "' class='validate[required,custom[number],min[1],max[99],funcCall[testSumaProcente]] form-control'>%

";
    $('#parameter').append(content);
}

function renderInputTitle() {
    var content = "
Nume parametru
Pondere

";
    $('#parameter_first').prepend(content);
}

function renderSelect(position, label) {
    var content = "<option value='" + position + "'>" + label + "</option>";
    $("#parameters").append(content);
}

function renderOutputSelect(position, label) {
    var content = "<option value='" + position + "'>" + label + "</option>";
    $("#output").append(content);
}

function renderOptionSelectInput(position, label) {
    $(".chzn-select").trigger("chosen:updated");

}

function generateNewParameterButton() {
    var content = "

 Adauga paramentru nou

<input class='form-control' placeholder='Nume parametru' type='text' id='new_parameter' />

";
    $('#parameter_btn').append(content);
}

function addNewParameter(input) {
    var new_parameter = $('#new_parameter ').val();
    if (!new_parameter) {
        alert('Completeaza numele noului parametru ');
        return false;
    }
    globalInputs.push(new_parameter);
    renderInput(globalInputs.length, new_parameter);
    renderSelect(globalInputs.length, new_parameter);
    renderOptionSelectInput(globalInputs.length, new_parameter);
    var timeInfluence = parseInput('new_parameter_time');
    var costInfluence = parseInput('new_parameters_cost');
    globalParametersOutputInfluenceTime.push(timeInfluence);
    globalParametersOutputInfluenceCost.push(costInfluence);
    $('#new_parameter ').val('');
    return false;
}

function testSumaProcente(field, rules, i, options) {
    "use strict";
    var param_max_sum = 100;
    var result = 0;
    var ok = true;
    for (var i = 1; i <= globalInputs.length; i++) {
        var partial = parseInput("parametru_" + i);
        if (!partial) {
            ok = false;
            break;
        } else {
            result += partial;
        }
    }
    if (ok)
        if (result != param_max_sum) {
            return 'Suma procentelor trebuie sa fie 100!';
        }
}

function verificareTimpMaxim(field, rules, i, options) {
    "use strict";
    var timpMax = $("#timp_stabilit").val();
    var camp = field.val();
    timpMax = timpMax ? timpMax : 0;
    camp = camp ? camp : 0;

    timpMax = parseInt(timpMax);
    camp = parseInt(camp);
    if (timpMax == 0) {
        return 'Completati mai intai campul Timp stabilit!';
    }
    if (timpMax < camp) {
        return 'Timpul selectat trebuie sa fie mai mic decat: ' + timpMax;
    }
}

function verificarePerioadaTimp(field, rules, i, options) {
    "use strict";
    var data_prev = field.attr('data - prev ');
    var prevTime = $("#time_" + data_prev).val();
    var time = field.val();

    prevTime = prevTime ? prevTime : 0;
    time = time ? time : 0;

    prevTime = parseInt(prevTime);
    time = parseInt(time);
    if (prevTime > time) {
        return 'Aceasta valoare trebuie sa fie mai mare decat: ' + prevTime;
    }
}

function verificareCompletare(field, rules, i, options) {
    "use strict";
    var data_next = field.attr('data-next');
    var time = field.val();
    var i = parseInt(data_next);
    i = i - 1;
    if (time != "")
        $("#time_" + data_next).attr({
            'disabled': false
        });
    else if (time == "" || $("#time_" + i).attr('disabled ') == "true")
        $("#time_" + data_next).attr({
            'disabled': true
        });
    return i;
}

function generateNewChartLog() {
    "use strict";
    var timpStabilit = parseInput('timp_stabilit');
    var costStabilit = parseInput('cost_stabilit');
    var output = getOutput();
    var input = getInput();

    setGlobalInput(input - 1);

    var coeficient = 1;
    var pConstant = 10000 * coeficient / timpStabilit;
    var interval = timpStabilit / 500;
    var initConstant = pConstant;

    getInputParameters();
    getTimeChanges();
    setInitialCoeficients();

    var data = [];
    var lastR = 0;
    for (var i = 0; i <= timpStabilit; i += interval) {
        lastR = Math.sqrt(i * coeficient);
        data.push([i, lastR]);
    }

    var maxValue1 = data[data.length - 1][1];
    var test = 100 / maxValue1;

    data.forEach(function (curent, index) {
        data[index][1] = data[index][1] * test;
    });
    var dataNew = [];
    var initialTime = 0;

    var lastTime = 0;
    for (var i = 0; i < globalTimeFields.length; i++) {
        for (var j = initialTime; j <= timeChanges[i][0][0]; j += interval) {
            lastR = Math.sqrt(j * coeficient);
            dataNew.push([j, lastR]);
        }
        lastTime = timeChanges[i][0][0];
        initialTime = timeChanges[i][0][0] + interval;
        changeParametersPercent(input, timeChanges[i][0][1]);
        computeDenCoeficient = timeChanges[i][0][0] / 10;
        coeficient = computeCoeficient();
        //console.log(coeficient);

    }
    var lastMaxValue = dataNew[dataNew.length - 1][1];
    var j = lastTime;
    while(lastMaxValue <= maxValue1) {
        lastMaxValue = Math.sqrt(j * coeficient);
        dataNew.push([j, lastMaxValue]);
        j += interval;

    }

    var maxValue = dataNew[dataNew.length - 1][1];
    var test = 100 / maxValue;
    var currentValue = 0;

    dataNew.forEach(function (curent, index) {
        currentValue = dataNew[index][1] * test;
    });

    chart(globalInputs[input - 1], globalOutputs[output - 1], data, dataNew);
    return false;

}

function changeParametersPercent(input, value) {
    coeficients[input - 1] = value / 100;

}

function computeCoeficient() {
    changeReferences();
    var num = computeNum();
    var den = computeDen();
    return num / den;
}

function changeReferences() {

    var reference = timeChanges[globalInputVar][0][1] / 100;
    reference = globalParametersOutputInfluenceTime[globalInputVar] == 1 ? reference : 1 / reference;
    globalReferences[globalInputVar] = reference;
}

function computeNum() {
    var result = 0;
    parametersPercent.forEach(function (value, index) {
        result += parametersPercent[index] * globalParametersOutputInfluenceTime[index];
    });
    return result;
}

function computeDen() {
    return computeDenCoeficient;
}

function setGlobalInput(input) {
    globalInputVar = input;
}

function getInputParameters() {
    parametersPercent = [];
    globalInputs.forEach(function (value, index) {
        parametersPercent.push(parseInput("parametru_" + (index + 1)));
        globalReferences.push(1);
    });
}

function setInitialCoeficients() {
    for (i = 1; i <= globalInputs.length; i++) {
        coeficients.push(1);
    }
}

function getTimeChanges() {
    timeChanges = [];
    globalTimeFields.forEach(function (value, index) {
        timeChanges.push([[parseInput("time_" + value), parseInput("percent_" + value)]]);
    });
}

function getInput() {
    return parseInput('parameters');
}

function getOutput() {
    return parseInput('output');
}

function parseInput(selector) {
    var result = $('#' + selector).val();
    result = parseInt(result);
    return result ? result : 0;
}

function chart(labelInput, labelOuptut, data1, data2) {
    "use strict";

    $.plot($("#chart"), [

        {
            data: data1,
            label: "Grafic initial iesire: " + labelOuptut
        },
        {
            data: data2,
            label: "Grafic iesire " + labelOuptut + "
parametru modificat " + labelInput,
        }

    ], {
        clickable: true,
        hoverable: true,
        series: {
            lines: {
                show: true,
                fill: false,
                fillColor: {
                    colors: [
                        {
                            opacity: 0.5
                        },
                        {
                            opacity: 0.15
                        }
            ]
                }
            },
            points: {
                show: false
            }
        },
        legend: {
            position: "nw"
        },
        xaxis: {
            label: labelOuptut
        },
        yaxis: {
            label: 'Realizabilitate'
        },
        grid: {
            hoverable: true,
            clickable: true
        },
        tooltip: true,
        tooltipOpts: {
            content: "Timp: %x
 Realizabilitate: %y",
            shifts: {
                x: -60,
                y: 25
            }
        }
    });

}
/////////////////////////////////////////////////////////////

function popUp(URL) {
    day = new Date();
    id = day.getTime();
    eval("page" + id + " = window.open(URL, '" + id + "', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=500,height=500,left = 390,top = 50');");
}<commit_msg>update function that generate graph<commit_after>var globalInputs = [
    'Rata estimari',
    'Rata fluctuatii personal',
    'Resurse materiale'
];

var globalOutputs = [
    'Timp',
    'Cost'
    ];

var parametersPercent = [];
var coeficients = [];
var timeChanges = [];

var globalTimeFields = [1];

var globalParametersOutputInfluenceTime = [1, -1, 1];
var globalParametersOutputInfluenceCost = [-1, -1, 1];

var globalInputVar = 0;

var globalReferences = [];

var computeDenCoeficient = 1;

function addNewTimeField() {
    var position = globalTimeFields.length + 1;
    globalTimeFields.push(position);

    var content1 = "
<input data-prev='" + (position - 1) + "' data-next='" + (position + 1) + "' class='validate[required,custom[number],min[1],funcCall[verificareTimpMaxim]] form-control' type='text' id='time_" + (position) + "' placeholder='Timp'>
	";
    $("#time_input").append(content1);
    var content2 = "
<input data-prev='" + (position - 1) + "' data-next='" + (position + 1) + "' class='validate[required,custom[number],min[0],max[200]] form-control' type='text' id='percent_" + (position) + "' placeholder='%'>
	";
    $("#percent_input").append(content2);
}

function generateInputsFields() {
    var i = 1;
    generateNewParameterButton();
    globalInputs.forEach(function (input) {
        renderInput(i, input);
        renderSelect(i++, input);
    });
    i = 1;
    globalOutputs.forEach(function (label) {
        renderOutputSelect(i++, label);
    });
    renderInputTitle();

}

function renderInput(position, label) {
    var content = "
" + label + ":
<input type='text' name='parametru_" + position + "' id='parametru_" + position + "' class='validate[required,custom[number],min[1],max[99],funcCall[testSumaProcente]] form-control'>%

";
    $('#parameter').append(content);
}

function renderInputTitle() {
    var content = "
Nume parametru
Pondere

";
    $('#parameter_first').prepend(content);
}

function renderSelect(position, label) {
    var content = "<option value='" + position + "'>" + label + "</option>";
    $("#parameters").append(content);
}

function renderOutputSelect(position, label) {
    var content = "<option value='" + position + "'>" + label + "</option>";
    $("#output").append(content);
}

function renderOptionSelectInput(position, label) {
    $(".chzn-select").trigger("chosen:updated");

}

function generateNewParameterButton() {
    var content = "

 Adauga paramentru nou

<input class='form-control' placeholder='Nume parametru' type='text' id='new_parameter' />

";
    $('#parameter_btn').append(content);
}

function addNewParameter(input) {
    var new_parameter = $('#new_parameter ').val();
    if (!new_parameter) {
        alert('Completeaza numele noului parametru ');
        return false;
    }
    globalInputs.push(new_parameter);
    renderInput(globalInputs.length, new_parameter);
    renderSelect(globalInputs.length, new_parameter);
    renderOptionSelectInput(globalInputs.length, new_parameter);
    var timeInfluence = parseInput('new_parameter_time');
    var costInfluence = parseInput('new_parameters_cost');
    globalParametersOutputInfluenceTime.push(timeInfluence);
    globalParametersOutputInfluenceCost.push(costInfluence);
    $('#new_parameter ').val('');
    return false;
}

function testSumaProcente(field, rules, i, options) {
    "use strict";
    var param_max_sum = 100;
    var result = 0;
    var ok = true;
    for (var i = 1; i <= globalInputs.length; i++) {
        var partial = parseInput("parametru_" + i);
        if (!partial) {
            ok = false;
            break;
        } else {
            result += partial;
        }
    }
    if (ok)
        if (result != param_max_sum) {
            return 'Suma procentelor trebuie sa fie 100!';
        }
}

function verificareTimpMaxim(field, rules, i, options) {
    "use strict";
    var timpMax = $("#timp_stabilit").val();
    var camp = field.val();
    timpMax = timpMax ? timpMax : 0;
    camp = camp ? camp : 0;

    timpMax = parseInt(timpMax);
    camp = parseInt(camp);
    if (timpMax == 0) {
        return 'Completati mai intai campul Timp stabilit!';
    }
    if (timpMax < camp) {
        return 'Timpul selectat trebuie sa fie mai mic decat: ' + timpMax;
    }
}

function verificarePerioadaTimp(field, rules, i, options) {
    "use strict";
    var data_prev = field.attr('data - prev ');
    var prevTime = $("#time_" + data_prev).val();
    var time = field.val();

    prevTime = prevTime ? prevTime : 0;
    time = time ? time : 0;

    prevTime = parseInt(prevTime);
    time = parseInt(time);
    if (prevTime > time) {
        return 'Aceasta valoare trebuie sa fie mai mare decat: ' + prevTime;
    }
}

function verificareCompletare(field, rules, i, options) {
    "use strict";
    var data_next = field.attr('data-next');
    var time = field.val();
    var i = parseInt(data_next);
    i = i - 1;
    if (time != "")
        $("#time_" + data_next).attr({
            'disabled': false
        });
    else if (time == "" || $("#time_" + i).attr('disabled ') == "true")
        $("#time_" + data_next).attr({
            'disabled': true
        });
    return i;
}

function generateNewChartLog() {
    "use strict";
    var timpStabilit = parseInput('timp_stabilit');
    var costStabilit = parseInput('cost_stabilit');
    var output = getOutput();
    var input = getInput();
    coeficients = [];
    globalReferences = [];
    parametersPercent = [];
    timeChanges = [];
    setGlobalInput(input - 1);

    var coeficient = 100;
    var interval = timpStabilit / 500;
    var coeficientInitial = 100 / timpStabilit;
    var procentCalculat = 1;

    getInputParameters();
    getTimeChanges();

    var data = [];
    var lastR = 0;
    for (var i = 0; i <= timpStabilit; i += interval) {
        lastR = i * coeficientInitial * procentCalculat;
        data.push([i, lastR]);
    }

    var dataNew = [];
    var initialTime = 0;

    var lastTime = 0;
    var adder = 0;
    for (var i = 0; i < globalTimeFields.length; i++) {
        for (var j = initialTime; j <= timeChanges[i][0][0]; j += interval) {
            lastR = j * coeficientInitial * procentCalculat + adder;
            if (lastR <= 100) {
                dataNew.push([j, lastR]);
            } else {
                break;
            }
        }
        lastTime = timeChanges[i][0][0];
        initialTime = timeChanges[i][0][0] + interval;
        changeReferences(i);
        procentCalculat = computeCoeficient();
        adder = lastR - lastTime * coeficientInitial * procentCalculat;
        var tst = 1;

    }
    j = lastTime + interval;
    while (lastR < 100) {
        lastR = j * coeficientInitial * procentCalculat + adder;
        dataNew.push([j, lastR]);
        j += interval;
    }
    chart(globalInputs[input - 1], globalOutputs[output - 1], data, dataNew);
    return false;

}

function computeCoeficient() {
    return computeNum();
}

function changeReferences(value) {

    var reference = timeChanges[value][0][1] / 100;
    reference = globalParametersOutputInfluenceTime[globalInputVar] == 1 ? 1 / reference : reference;
    globalReferences[globalInputVar] = reference;
}

function computeNum() {
    var result = 0;
    parametersPercent.forEach(function (value, index) {
        result += parametersPercent[index] * globalReferences[index] / 100;
    });
    return result;
}

function computeDen() {
    return computeDenCoeficient;
}

function setGlobalInput(input) {
    globalInputVar = input;
}

function getInputParameters() {
    parametersPercent = [];
    globalInputs.forEach(function (value, index) {
        parametersPercent.push(parseInput("parametru_" + (index + 1)));
        globalReferences.push(1);
    });
}

function getTimeChanges() {
    timeChanges = [];
    globalTimeFields.forEach(function (value, index) {
        timeChanges.push([[parseInput("time_" + value), parseInput("percent_" + value)]]);
    });
}

function getInput() {
    return parseInput('parameters');
}

function getOutput() {
    return parseInput('output');
}

function parseInput(selector) {
    var result = $('#' + selector).val();
    result = parseInt(result);
    return result ? result : 0;
}

function chart(labelInput, labelOuptut, data1, data2) {
    "use strict";

    $.plot($("#chart"), [

        {
            data: data1,
            label: "Grafic initial iesire: " + labelOuptut
        },
        {
            data: data2,
            label: "Grafic iesire " + labelOuptut + "
parametru modificat " + labelInput,
        }

    ], {
        clickable: true,
        hoverable: true,
        series: {
            lines: {
                show: true,
                fill: false,
                fillColor: {
                    colors: [
                        {
                            opacity: 0.5
                        },
                        {
                            opacity: 0.15
                        }
            ]
                }
            },
            points: {
                show: false
            }
        },
        legend: {
            position: "nw"
        },
        xaxis: {
            label: labelOuptut
        },
        yaxis: {
            label: 'Realizabilitate'
        },
        grid: {
            hoverable: true,
            clickable: true
        },
        tooltip: true,
        tooltipOpts: {
            content: "Timp: %x
 Realizabilitate: %y",
            shifts: {
                x: -60,
                y: 25
            }
        }
    });

}
/////////////////////////////////////////////////////////////

function popUp(URL) {
    day = new Date();
    id = day.getTime();
    eval("page" + id + " = window.open(URL, '" + id + "', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=500,height=500,left = 390,top = 50');");
}<|endoftext|>


<commit_before>var _ = require('lodash');
var fs = require('fs');
var util = require('../../core/util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var ENV = util.gekkoEnv();
var mode = util.gekkoMode();
var startTime = util.getStartTime();

var talib = require(dirs.core + 'talib');
if(talib == null) {
  log.warn('TALIB indicators could not be loaded, they will be unavailable.');
}

var tulind = require(dirs.core + 'tulind');
if(tulind == null) {
  log.warn('TULIP indicators could not be loaded, they will be unavailable.');
}

var indicatorsPath = dirs.methods + 'indicators/';
var indicatorFiles = fs.readdirSync(indicatorsPath);
var Indicators = {};

_.each(indicatorFiles, function(indicator) {
  const indicatorName = indicator.split(".")[0];
  if (indicatorName[0] != "_")
    try {
      Indicators[indicatorName] = require(indicatorsPath + indicator);
    } catch (e) {
      log.error("Failed to load indicator", indicatorName);
    }
});

var allowedIndicators = _.keys(Indicators);
var allowedTalibIndicators = _.keys(talib);
var allowedTulipIndicators = _.keys(tulind);

var Base = function(settings) {
  _.bindAll(this);

  // properties
  this.age = 0;
  this.processedTicks = 0;
  this.setup = false;
  this.settings = settings;
  this.tradingAdvisor = config.tradingAdvisor;
  // defaults
  this.requiredHistory = 0;
  this.priceValue = 'close';
  this.indicators = {};
  this.talibIndicators = {};
  this.tulipIndicators = {};
  this.asyncTick = false;
  this.candlePropsCacheSize = 1000;
  this.deferredTicks = [];

  this._prevAdvice;

  this.candleProps = {
    open: [],
    high: [],
    low: [],
    close: [],
    volume: [],
    vwp: [],
    trades: []
  };

  // make sure we have all methods
  _.each(['init', 'check'], function(fn) {
    if(!this[fn])
      util.die('No ' + fn + ' function in this trading method found.')
  }, this);

  if(!this.update)
    this.update = function() {};

  if(!this.end)
    this.end = function() {};

  // let's run the implemented starting point
  this.init();

  if(!config.debug || !this.log)
    this.log = function() {};

  this.setup = true;

  if(_.size(this.talibIndicators) || _.size(this.tulipIndicators))
    this.asyncTick = true;

  if(_.size(this.indicators))
    this.hasSyncIndicators = true;
}

// teach our base trading method events
util.makeEventEmitter(Base);

Base.prototype.tick = function(candle) {

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.age !== this.processedTicks
  ) {
    // Gekko will call talib and run strat
    // functions when talib is done, but by
    // this time the sync indicators might be
    // updated with future candles.
    //
    // See @link: https://github.com/askmike/gekko/issues/837#issuecomment-316549691
    return this.deferredTicks.push(candle);
  }

  this.age++;

  if(this.asyncTick) {
    this.candleProps.open.push(candle.open);
    this.candleProps.high.push(candle.high);
    this.candleProps.low.push(candle.low);
    this.candleProps.close.push(candle.close);
    this.candleProps.volume.push(candle.volume);
    this.candleProps.vwp.push(candle.vwp);
    this.candleProps.trades.push(candle.trades);

    if(this.age > this.candlePropsCacheSize) {
      this.candleProps.open.shift();
      this.candleProps.high.shift();
      this.candleProps.low.shift();
      this.candleProps.close.shift();
      this.candleProps.volume.shift();
      this.candleProps.vwp.shift();
      this.candleProps.trades.shift();
    }
  }

  // update all indicators
  var price = candle[this.priceValue];
  _.each(this.indicators, function(i) {
    if(i.input === 'price')
      i.update(price);
    if(i.input === 'candle')
      i.update(candle);
  },this);

  // update the trading method
  if(!this.asyncTick) {
    this.propogateTick(candle);
  } else {

    var next = _.after(
      _.size(this.talibIndicators) + _.size(this.tulipIndicators),
      () => this.propogateTick(candle)
    );

    var basectx = this;

    // handle result from talib
    var talibResultHander = function(err, result) {
      if(err)
        util.die('TALIB ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from talib
    _.each(
      this.talibIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        talibResultHander.bind(indicator)
      )
    );

    // handle result from tulip
    var tulindResultHander = function(err, result) {
      if(err)
        util.die('TULIP ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from tulip indicators
    _.each(
      this.tulipIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        tulindResultHander.bind(indicator)
      )
    );
  }

  this.propogateCustomCandle(candle);
}

// if this is a child process the parent might
// be interested in the custom candle.
if(ENV !== 'child-process') {
  Base.prototype.propogateCustomCandle = _.noop;
} else {
  Base.prototype.propogateCustomCandle = function(candle) {
    process.send({
      type: 'candle',
      candle: candle
    });
  }
}

Base.prototype.propogateTick = function(candle) {
  this.candle = candle;

  this.update(candle);

  var isAllowedToCheck = this.requiredHistory <= this.age;

  // in live mode we might receive more candles
  // than minimally needed. In that case check
  // whether candle start time is > startTime
  var isPremature;
  if(mode === 'realtime')
    isPremature = candle.start < startTime;
  else
    isPremature = false;

  if(isAllowedToCheck && !isPremature) {
    this.log(candle);
    this.check(candle);
  }
  this.processedTicks++;

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.deferredTicks.length
  ) {
    return this.tick(this.deferredTicks.shift())
  }

  // are we totally finished?
  var done = this.age === this.processedTicks;
  if(done && this.finishCb)
    this.finishCb();
}

Base.prototype.addTalibIndicator = function(name, type, parameters) {
  if(!talib)
    util.die('Talib is not enabled');

  if(!_.contains(allowedTalibIndicators, type))
    util.die('I do not know the talib indicator ' + type);

  if(this.setup)
    util.die('Can only add talib indicators in the init method!');

  var basectx = this;

  this.talibIndicators[name] = {
    run: talib[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addTulipIndicator = function(name, type, parameters) {
  if(!tulind)
  util.die('Tulip indicators is not enabled');

  if(!_.contains(allowedTulipIndicators, type))
    util.die('I do not know the tulip indicator ' + type);

  if(this.setup)
    util.die('Can only add tulip indicators in the init method!');

  var basectx = this;

  this.tulipIndicators[name] = {
    run: tulind[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addIndicator = function(name, type, parameters) {
  if(!_.contains(allowedIndicators, type))
    util.die('I do not know the indicator ' + type);

  if(this.setup)
    util.die('Can only add indicators in the init method!');

  this.indicators[name] = new Indicators[type](parameters);

  // some indicators need a price stream, others need full candles
}

Base.prototype.advice = function(newPosition, _candle) {
  // ignore soft advice coming from legacy
  // strategies.
  if(!newPosition)
    return;

  // ignore if advice equals previous advice
  if(newPosition === this._prevAdvice)
    return;

  // cache the candle this advice is based on
  if(_candle)
    var candle = _candle;
  else
    var candle = this.candle;

  this._prevAdvice = newPosition;

  _.defer(function() {
    this.emit('advice', {
      recommendation: newPosition,
      portfolio: 1,
      candle
    });
  }.bind(this));
}

// Because the trading method might be async we need
// to be sure we only stop after all candles are
// processed.
Base.prototype.finish = function(done) {
  if(!this.asyncTick) {
    this.end();
    return done();
  }

  if(this.age === this.processedTicks) {
    this.end();
    return done();
  }

  // we are not done, register cb
  // and call after we are..
  this.finishCb = done;
}

module.exports = Base;
<commit_msg>subtract candle length from start time to calculate isPremature<commit_after>var _ = require('lodash');
var fs = require('fs');
var util = require('../../core/util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var ENV = util.gekkoEnv();
var mode = util.gekkoMode();
var startTime = util.getStartTime();

var talib = require(dirs.core + 'talib');
if(talib == null) {
  log.warn('TALIB indicators could not be loaded, they will be unavailable.');
}

var tulind = require(dirs.core + 'tulind');
if(tulind == null) {
  log.warn('TULIP indicators could not be loaded, they will be unavailable.');
}

var indicatorsPath = dirs.methods + 'indicators/';
var indicatorFiles = fs.readdirSync(indicatorsPath);
var Indicators = {};

_.each(indicatorFiles, function(indicator) {
  const indicatorName = indicator.split(".")[0];
  if (indicatorName[0] != "_")
    try {
      Indicators[indicatorName] = require(indicatorsPath + indicator);
    } catch (e) {
      log.error("Failed to load indicator", indicatorName);
    }
});

var allowedIndicators = _.keys(Indicators);
var allowedTalibIndicators = _.keys(talib);
var allowedTulipIndicators = _.keys(tulind);

var Base = function(settings) {
  _.bindAll(this);

  // properties
  this.age = 0;
  this.processedTicks = 0;
  this.setup = false;
  this.settings = settings;
  this.tradingAdvisor = config.tradingAdvisor;
  // defaults
  this.requiredHistory = 0;
  this.priceValue = 'close';
  this.indicators = {};
  this.talibIndicators = {};
  this.tulipIndicators = {};
  this.asyncTick = false;
  this.candlePropsCacheSize = 1000;
  this.deferredTicks = [];

  this._prevAdvice;

  this.candleProps = {
    open: [],
    high: [],
    low: [],
    close: [],
    volume: [],
    vwp: [],
    trades: []
  };

  // make sure we have all methods
  _.each(['init', 'check'], function(fn) {
    if(!this[fn])
      util.die('No ' + fn + ' function in this trading method found.')
  }, this);

  if(!this.update)
    this.update = function() {};

  if(!this.end)
    this.end = function() {};

  // let's run the implemented starting point
  this.init();

  if(!config.debug || !this.log)
    this.log = function() {};

  this.setup = true;

  if(_.size(this.talibIndicators) || _.size(this.tulipIndicators))
    this.asyncTick = true;

  if(_.size(this.indicators))
    this.hasSyncIndicators = true;
}

// teach our base trading method events
util.makeEventEmitter(Base);

Base.prototype.tick = function(candle) {

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.age !== this.processedTicks
  ) {
    // Gekko will call talib and run strat
    // functions when talib is done, but by
    // this time the sync indicators might be
    // updated with future candles.
    //
    // See @link: https://github.com/askmike/gekko/issues/837#issuecomment-316549691
    return this.deferredTicks.push(candle);
  }

  this.age++;

  if(this.asyncTick) {
    this.candleProps.open.push(candle.open);
    this.candleProps.high.push(candle.high);
    this.candleProps.low.push(candle.low);
    this.candleProps.close.push(candle.close);
    this.candleProps.volume.push(candle.volume);
    this.candleProps.vwp.push(candle.vwp);
    this.candleProps.trades.push(candle.trades);

    if(this.age > this.candlePropsCacheSize) {
      this.candleProps.open.shift();
      this.candleProps.high.shift();
      this.candleProps.low.shift();
      this.candleProps.close.shift();
      this.candleProps.volume.shift();
      this.candleProps.vwp.shift();
      this.candleProps.trades.shift();
    }
  }

  // update all indicators
  var price = candle[this.priceValue];
  _.each(this.indicators, function(i) {
    if(i.input === 'price')
      i.update(price);
    if(i.input === 'candle')
      i.update(candle);
  },this);

  // update the trading method
  if(!this.asyncTick) {
    this.propogateTick(candle);
  } else {

    var next = _.after(
      _.size(this.talibIndicators) + _.size(this.tulipIndicators),
      () => this.propogateTick(candle)
    );

    var basectx = this;

    // handle result from talib
    var talibResultHander = function(err, result) {
      if(err)
        util.die('TALIB ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from talib
    _.each(
      this.talibIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        talibResultHander.bind(indicator)
      )
    );

    // handle result from tulip
    var tulindResultHander = function(err, result) {
      if(err)
        util.die('TULIP ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from tulip indicators
    _.each(
      this.tulipIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        tulindResultHander.bind(indicator)
      )
    );
  }

  this.propogateCustomCandle(candle);
}

// if this is a child process the parent might
// be interested in the custom candle.
if(ENV !== 'child-process') {
  Base.prototype.propogateCustomCandle = _.noop;
} else {
  Base.prototype.propogateCustomCandle = function(candle) {
    process.send({
      type: 'candle',
      candle: candle
    });
  }
}

Base.prototype.propogateTick = function(candle) {
  this.candle = candle;

  this.update(candle);

  var isAllowedToCheck = this.requiredHistory <= this.age;

  // in live mode we might receive more candles
  // than minimally needed. In that case check
  // whether candle start time is > startTime
  var isPremature;

  // Subtract number of minutes in current candle for instant start
  let startTimeMinusCandleSize = startTime.clone();
  startTimeMinusCandleSize.subtract(this.tradingAdvisor.candleSize, "minutes");

  if(mode === 'realtime'){
    isPremature = candle.start < startTimeMinusCandleSize;
  }
  else{
    isPremature = false;
  }

  if(isAllowedToCheck && !isPremature) {
    this.log(candle);
    this.check(candle);
  }
  this.processedTicks++;

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.deferredTicks.length
  ) {
    return this.tick(this.deferredTicks.shift())
  }

  // are we totally finished?
  var done = this.age === this.processedTicks;
  if(done && this.finishCb)
    this.finishCb();
}

Base.prototype.addTalibIndicator = function(name, type, parameters) {
  if(!talib)
    util.die('Talib is not enabled');

  if(!_.contains(allowedTalibIndicators, type))
    util.die('I do not know the talib indicator ' + type);

  if(this.setup)
    util.die('Can only add talib indicators in the init method!');

  var basectx = this;

  this.talibIndicators[name] = {
    run: talib[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addTulipIndicator = function(name, type, parameters) {
  if(!tulind)
  util.die('Tulip indicators is not enabled');

  if(!_.contains(allowedTulipIndicators, type))
    util.die('I do not know the tulip indicator ' + type);

  if(this.setup)
    util.die('Can only add tulip indicators in the init method!');

  var basectx = this;

  this.tulipIndicators[name] = {
    run: tulind[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addIndicator = function(name, type, parameters) {
  if(!_.contains(allowedIndicators, type))
    util.die('I do not know the indicator ' + type);

  if(this.setup)
    util.die('Can only add indicators in the init method!');

  this.indicators[name] = new Indicators[type](parameters);

  // some indicators need a price stream, others need full candles
}

Base.prototype.advice = function(newPosition, _candle) {
  // ignore soft advice coming from legacy
  // strategies.
  if(!newPosition)
    return;

  // ignore if advice equals previous advice
  if(newPosition === this._prevAdvice)
    return;

  // cache the candle this advice is based on
  if(_candle)
    var candle = _candle;
  else
    var candle = this.candle;

  this._prevAdvice = newPosition;

  _.defer(function() {
    this.emit('advice', {
      recommendation: newPosition,
      portfolio: 1,
      candle
    });
  }.bind(this));
}

// Because the trading method might be async we need
// to be sure we only stop after all candles are
// processed.
Base.prototype.finish = function(done) {
  if(!this.asyncTick) {
    this.end();
    return done();
  }

  if(this.age === this.processedTicks) {
    this.end();
    return done();
  }

  // we are not done, register cb
  // and call after we are..
  this.finishCb = done;
}

module.exports = Base;
<|endoftext|>


<commit_before>$(document).ready(function() {
	function setup(){
		 _(_.range(6)).each(function(i) {
		 	var tds = "";
		 	 _(_.range(7)).each(function(j) {
		 	 	tds += "	";
		 	 });

		 	$('#board-table').append($("
" + tds + "
"));
		 })
		 $('#board-table').on("click", 'td', pieceClicked);
	}

	setup();

	var currentplayer = "red";
	var won = false;

	function getColumn(index) {
		elements = []
		$('#board-table tr').each(function() {
			elements.push($(this).children().eq(index))
		})

		return elements;
	}

	function pieceClicked() {
		if(won) return;
		column = getColumn($(this).index());
		emptyCell = _.chain(column.reverse()).filter(function(el) {
			return el.html() === '';
		}).first().value();

		if(emptyCell === undefined) {
			alert('column is full');
			return;
		}

		cell = placePieces(currentplayer, emptyCell)
		currentplayer = (currentplayer === 'red') ? 'yellow' : 'red';
		check_winner(cell);
	}

	function getSurroundingCells(cell) {
		//coulum its in(index) need it row index cells parent
		var row = cell.parent().index();
		var column = cell.index();
		var positions = [[-1, 0], [0, 1], [0, -1], [1, 0]];
		return _(positions).map(function(thing) {
			return getCell(row+thing[0],column+thing[1])
		});
	}

	function getCell(row, column)
	{
		return $('#board-table tr').eq(row).children().eq(column);
	}

	function check_winner(cell) {
		function recurse(count, cell, visited_cells) {

		}

		return recurse(7, cell, [])
	}

	function placePieces(player, cell) {
		cell.html('');
		cell.attr('class', player);
		return cell;
	}
});<commit_msg>Wonning<commit_after>$(document).ready(function() {
	function setup(){
		 _(_.range(6)).each(function(i) {
		 	var tds = "";
		 	 _(_.range(7)).each(function(j) {
		 	 	tds += "	";
		 	 });

		 	$('#board-table').append($("
" + tds + "
"));
		 })
		 $('#board-table').on("click", 'td', pieceClicked);
	}

	setup();

	var currentplayer = "red";
	var won = false;

	function getColumn(index) {
		elements = []
		$('#board-table tr').each(function() {
			elements.push($(this).children().eq(index))
		})

		return elements;
	}

	function pieceClicked() {
		if(won) return;
		column = getColumn($(this).index());
		emptyCell = _.chain(column.reverse()).filter(function(el) {
			return el.html() === '';
		}).first().value();

		if(emptyCell === undefined) {
			alert('column is full');
			return;
		}

		cell = placePieces(currentplayer, emptyCell)
		currentplayer = (currentplayer === 'red') ? 'yellow' : 'red';
		check_winner(cell);
	}

	function getSurroundingCells(cell) {
		//column its in(index) need it row index cells parent
		var row = cell.parent().index();
		var column = cell.index();
		var positions = [[-1, 0], [0, 1], [0, -1], [1, 0]];
		return _(positions).map(function(thing) {
			return getCell(row+thing[0],column+thing[1])
		});
	}

	function getCell(row, column)
	{
		return $('#board-table tr').eq(row).children().eq(column);
	}

	function check_winner(cell) {
		var player = cell.attr("class")
		function recurse(count, cell, visitedCells) {
			console.info(cell.html());
			visitedCells.push(cell.attr('id'));
			var surroundingCells = _(getSurroundingCells(cell)).filter(function (el) {
				if (el.attr('class') === player && !_(visitedCells).contains(el.attr('id')))
					return true;
				return false;
			});

			if(count == 1) return true;
			if(surroundingCells.length == 0) return false;
			for(var i = 0;i < surroundingCells.length;i++) {
				return recurse(--count, surroundingCells[i], visitedCells);
			}
		}

		return recurse(7, cell, []);
	}

	function placePieces(player, cell) {
		cell.html('');
		cell.attr('class', player);
		return cell;
	}
});<|endoftext|>


<commit_before>import { toQueryString, getUrlVars, sleep } from './utils.js';

window.visualize = startVisualization; // Add function to global scope.

// The timeout variable
var simulationTimeout;

// Holds the simulation's metadata
var simulation;

// Holds the simulation's visualization state
var state = {
    currentRound: 0,
    isPaused: false,
    isFinished: false,
}

/**
 * Update simulation to holds simulation's metadata,
 * initialize the visualization of the first round.
*/
async function startVisualization() {
    simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;

    // Define the pause-button behavior.
    var pauseButton = document.getElementById('pause-button');
    pauseButton.onclick = function() {
        if (pauseButton.innerHTML === "Pause") {
            pause();
        } else {
            resume();
        }
    }
    nextRound();
}

/** Visualize the next round of the simulation. */
async function nextRound() {
    var roundHeader = document.getElementById('current-round-header');
    roundHeader.innerText = 'Current Round: ' + state.currentRound;

    var params = {
        simulationId: simulation.id,
        round: state.currentRound,
    };

    // Request real board state.
    params['isReal'] = true;
    await fetchReadBoardState(params, 'real-board-table');

    // Request estimated board state.
    params['isReal'] = false;
    await fetchReadBoardState(params, 'estimated-board-table');

    // Update the simulation's state and the timeout.
    updateState();
    if (!state.isPaused && !state.isFinished) {
        var delay = 1/document.getElementById('speed-controller').value;
        simulationTimeout = setTimeout(nextRound, delay);
    }

    // Visualize the statistics if the simulation is over.
    if (state.isFinished) {
        simulationTimeout = setTimeout(displayStats, delay);
    }
}

/** Update the current round. */
function updateState() {
    state.currentRound += 1;
    if (state.currentRound >= simulation.roundsNum) {
        state.isFinished = true;
    }
}

/** Continue the visualization. */
function resume() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Pause";

    state.isPaused = false;
    var delay = 1/document.getElementById('speed-controller').value;
    simulationTimeout = setTimeout(nextRound, delay);
}

/** Stop the visualization. */
function pause() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Play";

    state.isPaused = true;
    clearTimeout(simulationTimeout);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/**
 * Display the statistical data of a simulation.
 */
function displayStats() {
    var params = {simulationId: simulation.id};
    const queryString = toQueryString(params);

    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => Object.keys(stats).forEach(kind => createStatsTable(kind, stats[kind])))
    .then(() => clearVisualizationElements());
}

/**
 * Create an HTML table element and fill it with statistical data.
 * @param {String} kind is the statistical data kind (distance or beacon observed percent).
 * @param {Object} stats is an object storing the simulations' stats of a specific kind.
 */
function createStatsTable(kind, stats) {
    var title = document.createElement('h4');
    title.innerText = kind;
    var table = document.createElement('table');
    table.classList.add('stats-table');
    var header = table.createTHead();
    var headerRow = header.insertRow(0);
    var dataRow = table.insertRow(1);

    // For beacon observed stats add the prefix 'beacon' to each measure name.
    var prefix = kind === 'BeaconsObservedPercentStats' ? 'beacon #' : '';

    var i = 0;
    Object.keys(stats).forEach(measure => {
        headerRow.insertCell(i).innerText = prefix + measure;
        dataRow.insertCell(i).innerText = stats[measure];
        i++;
    });
    document.body.appendChild(title);
    document.body.appendChild(table);
}

/** Clear all HTML element used for visualization. */
function clearVisualizationElements() {
    document.getElementsByClassName('boards')[0].innerHTML = '';
    document.getElementsByClassName('choose-beacon')[0].innerHTML = '';
    document.getElementsByClassName('speed-controller')[0].innerHTML = '';
}<commit_msg>Clear visualiztion's HTML elements after the simulation ends.<commit_after>import { toQueryString, getUrlVars, sleep } from './utils.js';

window.visualize = startVisualization; // Add function to global scope.

// The timeout variable.
var simulationTimeout;

// Holds the simulation's metadata.
var simulation;

// Holds the simulation's visualization state.
var state = {
    currentRound: 0,
    isPaused: false,
    isFinished: false,
}

/**
 * Update simulation to holds simulation's metadata.
 * Initialize the visualization of the first round.
*/
async function startVisualization() {
    simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;

    // Define the pause-button behavior.
    var pauseButton = document.getElementById('pause-button');
    pauseButton.onclick = function() {
        if (pauseButton.innerHTML === "Pause") {
            pause();
        } else {
            resume();
        }
    }
    nextRound();
}

/** Visualize the next round of the simulation. */
async function nextRound() {
    var roundHeader = document.getElementById('current-round-header');
    roundHeader.innerText = 'Current Round: ' + state.currentRound;

    var params = {
        simulationId: simulation.id,
        round: state.currentRound,
    };

    // Request real board state.
    params['isReal'] = true;
    await fetchReadBoardState(params, 'real-board-table');

    // Request estimated board state.
    params['isReal'] = false;
    await fetchReadBoardState(params, 'estimated-board-table');

    // Update the simulation's state and the timeout.
    updateState();
    if (!state.isPaused && !state.isFinished) {
        var delay = 1/document.getElementById('speed-controller').value;
        simulationTimeout = setTimeout(nextRound, delay);
    }

    // Visualize the statistics if the simulation is over.
    if (state.isFinished) {
        simulationTimeout = setTimeout(displayStats, delay);
    }
}

/** Update the current round. */
function updateState() {
    state.currentRound += 1;
    if (state.currentRound >= simulation.roundsNum) {
        state.isFinished = true;
    }
}

/** Continue the visualization. */
function resume() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Pause";

    state.isPaused = false;
    var delay = 1/document.getElementById('speed-controller').value;
    simulationTimeout = setTimeout(nextRound, delay);
}

/** Stop the visualization. */
function pause() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Play";

    state.isPaused = true;
    clearTimeout(simulationTimeout);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/**
 * Display the statistical data of a simulation.
 */
function displayStats() {
    var params = {simulationId: simulation.id};
    const queryString = toQueryString(params);

    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => Object.keys(stats).forEach(kind => createStatsTable(kind, stats[kind])))
    .then(() => clearVisualizationElements());
}

/**
 * Create an HTML table element and fill it with statistical data.
 * @param {String} kind is the statistical data kind (distance or beacon observed percent).
 * @param {Object} stats is an object storing the simulations' stats of a specific kind.
 */
function createStatsTable(kind, stats) {
    var title = document.createElement('h4');
    title.innerText = kind;
    var table = document.createElement('table');
    table.classList.add('stats-table');
    var header = table.createTHead();
    var headerRow = header.insertRow(0);
    var dataRow = table.insertRow(1);

    // For beacon observed stats add the prefix 'beacon' to each measure name.
    var prefix = kind === 'BeaconsObservedPercentStats' ? 'beacon #' : '';

    var i = 0;
    Object.keys(stats).forEach(measure => {
        headerRow.insertCell(i).innerText = prefix + measure;
        dataRow.insertCell(i).innerText = stats[measure];
        i++;
    });
    document.body.appendChild(title);
    document.body.appendChild(table);
}

/** Clear all HTML element used for visualization. */
function clearVisualizationElements() {
    document.getElementsByClassName('boards')[0].innerHTML = '';
    document.getElementsByClassName('choose-beacon')[0].innerHTML = '';
    document.getElementsByClassName('speed-controller')[0].innerHTML = '';
    document.getElementsByClassName('pause-button')[0].innerHTML = '';
    document.getElementsByClassName('legend')[0].innerHTML = '';
    document.getElementById('current-round-header').innerHTML = '';
}<|endoftext|>


<commit_before>const Promise = require('bluebird')
const logger = require('log4js').getLogger()

function promisify(func, params) {
  return new Promise((resolve, reject) => {
    func(params, (err, response) => {
      if (err) {
        reject(err)
      } else {
        resolve(response)
      }
    })
  })
}

function parseForm(str) {
  const form = {}
  const arr = str.split('&')
  arr.forEach(qs => {
    const i = qs.indexOf('=')
    const key = qs.slice(0, i)
    const value = decodeURIComponent(qs.slice(i + 1))
    form[key] = value
  })
  return form
}

function decodeURIForm(form) {
  const copyForm = {}
  const keys = Object.keys(form)
  keys.forEach(key => {
    copyForm[key] = decodeURIComponent(form[key])
  })
  return copyForm
}

function encodeURIForm(form) {
  const keys = Object.keys(form)
  return keys.map(key => {
    if (form[key]) {
      return `${key}=${encodeURIComponent(form[key])}`
    } else {
      return key
    }
  }).join('&')
}

exports.promisify = promisify
exports.parseForm = parseForm
exports.decodeURIForm = decodeURIForm
exports.encodeURIForm = encodeURIForm
<commit_msg>update last bug fixed<commit_after>const Promise = require('bluebird')
const logger = require('log4js').getLogger()

function promisify(func, params) {
  return new Promise((resolve, reject) => {
    func(params, (err, response) => {
      if (err) {
        reject(err)
      } else {
        resolve(response)
      }
    })
  })
}

function parseForm(str) {
  const form = {}
  const arr = str.split('&')
  arr.forEach(qs => {
    const i = qs.indexOf('=')
    const key = qs.slice(0, i)
    const value = decodeURIComponent(qs.slice(i + 1))
    form[key] = value
  })
  return form
}

function decodeURIForm(form) {
  const copyForm = {}
  const keys = Object.keys(form)
  keys.forEach(key => {
    copyForm[key] = decodeURIComponent(form[key])
  })
  return copyForm
}

function encodeURIForm(form) {
  const keys = Object.keys(form)
  return keys.map(key => {
    if (form[key] !== null) {
      return `${key}=${encodeURIComponent(form[key])}`
    } else {
      return key
    }
  }).join('&')
}

exports.promisify = promisify
exports.parseForm = parseForm
exports.decodeURIForm = decodeURIForm
exports.encodeURIForm = encodeURIForm
<|endoftext|>


<commit_before>(function () {
	angular.module('index-manager', ['ng']).factory('IndexManager', function () {

		/**
		 * @constructor
		 */
		function IndexManager () {
			// default properties
			this.index = 0; // default is on 0
			this.lastIndex = 0;
			this.maxIndex = 0;
			this.isInfinite = false;
			this.onIndexChange = function () {}; // callback fired
		}

		IndexManager.prototype = {
			/**
			 * @enum
			 */
			Direction: {
				Left: 0,
				Right: 1
			},
			/**
			 * Proceeds to next index value going forward, invokes `setIndex`
			 * Does nothing if conditions aren't met
			 * @return {Boolean}
			 */
			next: function () {
				if (this.index < this.maxIndex) {
					this.setIndex(this.index + 1, this.Direction.Right);
				} else if (this.isInfinite === true) {
					this.setIndex(0, this.Direction.Right);
				} else {
					return false;
				}
			},
			/**
			 * Proceeds to next index value going backwards, invokes `setIndex`
			 * Does nothing if conditions aren't met
			 * @return {Boolean}
			 */
			back: function () {
				if (this.index > 0) {
					this.setIndex(this.index - 1, this.Direction.Left);
				} else if (this.isInfinite === true) {
					this.setIndex(this.maxIndex, this.Direction.Left);
				} else {
					return false;
				}
			},
			/**
			 * Sets index, defines previous index, invokes `action`
			 * @param {Number} index
			 * @param {IndexManager.Direction} direction
			 * @return {IndexManager}
			 */
			setIndex: function (index, direction) {
				// set a last index for point of references in `action`
				this.lastIndex = this.index;
				// defined new index
				this.index = index;
				// invoke action which may have animation
				this.action(direction || null);
				// fire onIndexChange event
				this.onIndexChange(index, this.lastIndex);

				return this;
			},
			/**
			 * Return current index
			 * @return {Number}
			 */
			getIndex: function () {
				return this.index;
			},
			/**
			 * Stub, overridable method
			 * @return {undefined}
			 */
			action: function () {},
			/**
			 * Set max index
			 * @param {number} number
			 * @return {IndexManager}
			 */
			setMaxIndex: function (number) {
				this.maxIndex = number;

				return this;
			},
			/**
			 * Set max index
			 * @param {number} number
			 * @return {Number}
			 */
			getMaxIndex: function (number) {
				return this.maxIndex;
			},
			/**
			 * Set event for on index change
			 * @param {Function} event
			 * @return {IndexManager}
			 */
			setOnIndexChange: function (event) {
				this.onIndexChange = event;

				return this;
			},
			/**
			 * @param {Boolean} isInfinite
			 * @return {IndexManager}
			 */
			setIsInfinite: function (isInfinite) {
				this.isInfinite = isInfinite;

				return this;
			}
		};

		return IndexManager;
	});
}());<commit_msg>added index integrity check<commit_after>(function () {
	angular.module('index-manager', ['ng']).factory('IndexManager', function () {

		/**
		 * @constructor
		 */
		function IndexManager () {
			// default properties
			this.index = 0; // default is on 0
			this.lastIndex = 0;
			this.maxIndex = 0;
			this.isInfinite = false;
			this.onIndexChange = function () {}; // callback fired
		}

		IndexManager.prototype = {
			/**
			 * @enum
			 */
			Direction: {
				Left: 0,
				Right: 1
			},
			/**
			 * Proceeds to next index value going forward, invokes `setIndex`
			 * Does nothing if conditions aren't met
			 * @return {Boolean}
			 */
			next: function () {
				if (this.index < this.maxIndex) {
					this.setIndex(this.index + 1, this.Direction.Right);
				} else if (this.isInfinite === true) {
					this.setIndex(0, this.Direction.Right);
				} else {
					return false;
				}
			},
			/**
			 * Proceeds to next index value going backwards, invokes `setIndex`
			 * Does nothing if conditions aren't met
			 * @return {Boolean}
			 */
			back: function () {
				if (this.index > 0) {
					this.setIndex(this.index - 1, this.Direction.Left);
				} else if (this.isInfinite === true) {
					this.setIndex(this.maxIndex, this.Direction.Left);
				} else {
					return false;
				}
			},
			/**
			 * Sets index, defines previous index, invokes `action`
			 * @param {Number} index
			 * @param {IndexManager.Direction} direction
			 * @return {IndexManager}
			 */
			setIndex: function (index, direction) {
				if (index > this.maxIndex) {
					throw new RangeError('index provided cannot be larger than the max index');
				}

				// set a last index for point of references in `action`
				this.lastIndex = this.index;
				// defined new index
				this.index = index;
				// invoke action which may have animation
				this.action(direction || null);
				// fire onIndexChange event
				this.onIndexChange(index, this.lastIndex);

				return this;
			},
			/**
			 * Return current index
			 * @return {Number}
			 */
			getIndex: function () {
				return this.index;
			},
			/**
			 * Stub, overridable method
			 * @return {undefined}
			 */
			action: function () {},
			/**
			 * Set max index
			 * @param {number} number
			 * @return {IndexManager}
			 */
			setMaxIndex: function (number) {
				this.maxIndex = number;

				return this;
			},
			/**
			 * Set max index
			 * @param {number} number
			 * @return {Number}
			 */
			getMaxIndex: function (number) {
				return this.maxIndex;
			},
			/**
			 * Set event for on index change
			 * @param {Function} event
			 * @return {IndexManager}
			 */
			setOnIndexChange: function (event) {
				this.onIndexChange = event;

				return this;
			},
			/**
			 * @param {Boolean} isInfinite
			 * @return {IndexManager}
			 */
			setIsInfinite: function (isInfinite) {
				this.isInfinite = isInfinite;

				return this;
			}
		};

		return IndexManager;
	});
}());<|endoftext|>


<commit_before>(function(root, factory) {
  if (typeof module !== 'undefined' && module.exports) {
    // Node.js
    module.exports = factory(
      {
        'levels': require('./levels'),
        'physics': require('./physics'),
        'time': require('../core/time')
      },
      require('async'),
      require('redis'),
      require('underscore')
    );
  }
})(this, function(game, async, redis, _) {

  var init = function(socket, store) {

    // init server update loop, fixed time step in milliseconds
    setInterval((function() {
      this.loop(socket, store);
    }).bind(this), 45);

    return this;

  };

  var updatePlayers = function(store, data, callback) {

    store.smembers('player', function(err, res) {
      var players = res;
      var length = players.length;

      async.forEach(
        players,
        function(uuid, callback) {
          // only publish updates for players originating from this server
          var player = game.levels.players[uuid];

          if (player) {
            updatePlayer(store, data, uuid, player, callback);
          } else {
            // notify async.forEach that function has completed
            if (typeof callback === 'function') callback();
          }
        }, function() {
          // notify async that iterator has completed
          if (typeof callback === 'function') callback();
        }
      );
    });

  };

  var updateNPCs = function(store, data, callback) {

    // iterate over all npcs in redis
    // TODO: should this just iterate over server NPCs instead?
    store.smembers('npc', function(err, res) {
      var npcs = res;
      var length = npcs.length;

      // don't return until all updateNPC calls have completed
      async.forEach(
        npcs,
        function(uuid, callback) {
          // only publish updates for NPCs originating from this server
          var npc = game.levels.npcs[uuid];

          if (npc) {
            updateNPC(store, data, npc, uuid, callback);
          } else {
            // notify async.forEach that function has completed
            if (typeof callback === 'function') callback();
          }
        }, function() {
          // notify async.parallel in loop that iterator has completed
          if (typeof callback === 'function') callback();
        }
      );
    });

  };

  var updatePlayer = function(store, data, uuid, player, callback) {

    var delta = {};

    // acknowledge most recent processed command and clear array
    // TODO: get ack from redis???
    if (player.processed.length) {
      delta.ack = _.max(player.processed);
      player.processed = [];
    }

    // TODO: DRY THIS UP!!!
    // defer to redis for absolute state, delta compression
    store.hgetall('player:' + uuid, function(err, res) {

      // save reference to old values and update state
      var prev = player.state;

      // some scope issues with iterating over res and updating values individually?
      var next = player.state = res || {};

      // init delta array for changed keys
      var deltaKeys = [];

      // iterate over new values and compare to old
      var keys = Object.keys(next);
      var length = keys.length;
      var key;

      for (var i = 0; i < length; i++) {
        key = keys[i];

        // check for changed values and push key to deltaKeys array
        if (prev[key] !== next[key]) {
          deltaKeys.push(key);
        }
      }

      // set changed values in data object
      if (deltaKeys.length > 0) {
        delta.state = _.pick(next, deltaKeys);
      }

      // only expire socket or browser session clients
      store.zadd('expire', Date.now(), 'player+' + uuid, function(err, res) {});

      store.hgetall('player:' + uuid + ':ship', function(err, res) {

        // save reference to old values and update state
        var prev = player.ship.state;

        // some scope issues with iterating over res and updating values individually?
        var next = player.ship.state = res;

        // init delta array for changed keys
        var deltaKeys = [];

        // iterate over new values and compare to old
        var keys = Object.keys(next);
        var length = keys.length;
        var key;

        for (var i = 0; i < length; i++) {
          key = keys[i];

          // check for changed values and push key to deltaKeys array
          if (prev[key] !== next[key]) {
            deltaKeys.push(key);
          }
        }

        // set changed values in data object
        if (deltaKeys.length) {
          delta.ship = {};
          delta.ship.state = _.pick(next, deltaKeys);
        }

        // init missiles
        var missiles = [];
        var missile;

        // iterate over missiles
        for (var i = 0; i < player.ship.missiles.length; i++) {
          missile = player.ship.missiles[i].getState();
          missiles.push(missile);
        }

        if (missiles) {
          delta.ship = delta.ship || {};
          delta.ship.missiles = missiles;
          // console.log(delta.ship.missiles);
        }

        // set changed values in data object
        if (Object.keys(delta).length) {
          delta.time = Date.now();
          data.players[uuid] = delta;
        }

        // notify async that iterator has completed
        if (typeof callback === 'function') callback();

      });

    });

  };

  var updateNPC = function(store, data, npc, uuid, callback) {

    // defer to redis for absolute state, delta compression
    store.hgetall('npc:' + uuid, function(err, res) {

      // save reference to old values and update state
      var prev = npc.state;

      // some scope issues with iterating over res and updating values individually?
      var next = npc.state = res;

      // init delta array for changed keys
      var delta = [];

      // iterate over new values and compare to old
      var keys = Object.keys(next);
      var length = keys.length;
      var key;

      for (var i = 0; i < length; i++) {
        key = keys[i];

        // check for changed values and push key to delta array
        if (prev[key] !== next[key]) {
          delta.push(key);
        }
      }

      // set changed values in data object
      if (delta.length) {
        data.npcs[uuid] = _.pick(next, delta);
      }

      // expire all NPCs to clean redis on server crash
      store.zadd('expire', Date.now(), 'npc+' + uuid, function(err, res) {});

      // notify async.forEach in updateNPCs that function has completed
      if (typeof callback === 'function') callback();

    });

  };

  var update = function(socket, data) {

    // server time stamp
    data.time = Date.now();

    // console.log(data);

    /*
    var keys = Object.keys(data.players);
    var key;

    for (var i = 0; i < keys.length; i++) {
      key = keys[i];
      if (data.players[key].ship) {
        console.log(data.players[key].ship);
      }
    }
    */

    // return delta object to client
    socket.io.sockets.emit('state:update', data);

  };

  var loop = function(socket, store) {

    // create data object containing
    // authoritative state and last processed input id
    var data = {};
    data.players = {};
    data.npcs = {};

    // get updated states from redis, then return delta object to client
    async.parallel([
      function(callback) { updatePlayers(store, data, callback) },
      function(callback) { updateNPCs(store, data, callback) }
    ], function() {
      update(socket, data);
    });

  };

  return {
    init: init,
    updatePlayers: updatePlayers,
    updateNPCs: updateNPCs,
    loop: loop
  };

});
<commit_msg>moving player expire to after ship update<commit_after>(function(root, factory) {
  if (typeof module !== 'undefined' && module.exports) {
    // Node.js
    module.exports = factory(
      {
        'levels': require('./levels'),
        'physics': require('./physics'),
        'time': require('../core/time')
      },
      require('async'),
      require('redis'),
      require('underscore')
    );
  }
})(this, function(game, async, redis, _) {

  var init = function(socket, store) {

    // init server update loop, fixed time step in milliseconds
    setInterval((function() {
      this.loop(socket, store);
    }).bind(this), 45);

    return this;

  };

  var updatePlayers = function(store, data, callback) {

    store.smembers('player', function(err, res) {
      var players = res;
      var length = players.length;

      async.forEach(
        players,
        function(uuid, callback) {
          // only publish updates for players originating from this server
          var player = game.levels.players[uuid];

          if (player) {
            updatePlayer(store, data, uuid, player, callback);
          } else {
            // notify async.forEach that function has completed
            if (typeof callback === 'function') callback();
          }
        }, function() {
          // notify async that iterator has completed
          if (typeof callback === 'function') callback();
        }
      );
    });

  };

  var updateNPCs = function(store, data, callback) {

    // iterate over all npcs in redis
    // TODO: should this just iterate over server NPCs instead?
    store.smembers('npc', function(err, res) {
      var npcs = res;
      var length = npcs.length;

      // don't return until all updateNPC calls have completed
      async.forEach(
        npcs,
        function(uuid, callback) {
          // only publish updates for NPCs originating from this server
          var npc = game.levels.npcs[uuid];

          if (npc) {
            updateNPC(store, data, npc, uuid, callback);
          } else {
            // notify async.forEach that function has completed
            if (typeof callback === 'function') callback();
          }
        }, function() {
          // notify async.parallel in loop that iterator has completed
          if (typeof callback === 'function') callback();
        }
      );
    });

  };

  var updatePlayer = function(store, data, uuid, player, callback) {

    var delta = {};

    // acknowledge most recent processed command and clear array
    // TODO: get ack from redis???
    if (player.processed.length) {
      delta.ack = _.max(player.processed);
      player.processed = [];
    }

    // TODO: DRY THIS UP!!!
    // defer to redis for absolute state, delta compression
    store.hgetall('player:' + uuid, function(err, res) {

      // save reference to old values and update state
      var prev = player.state;

      // some scope issues with iterating over res and updating values individually?
      var next = player.state = res || {};

      // init delta array for changed keys
      var deltaKeys = [];

      // iterate over new values and compare to old
      var keys = Object.keys(next);
      var length = keys.length;
      var key;

      for (var i = 0; i < length; i++) {
        key = keys[i];

        // check for changed values and push key to deltaKeys array
        if (prev[key] !== next[key]) {
          deltaKeys.push(key);
        }
      }

      // set changed values in data object
      if (deltaKeys.length > 0) {
        delta.state = _.pick(next, deltaKeys);
      }

      store.hgetall('player:' + uuid + ':ship', function(err, res) {

        // save reference to old values and update state
        var prev = player.ship.state;

        // some scope issues with iterating over res and updating values individually?
        var next = player.ship.state = res;

        // init delta array for changed keys
        var deltaKeys = [];

        // iterate over new values and compare to old
        var keys = Object.keys(next);
        var length = keys.length;
        var key;

        for (var i = 0; i < length; i++) {
          key = keys[i];

          // check for changed values and push key to deltaKeys array
          if (prev[key] !== next[key]) {
            deltaKeys.push(key);
          }
        }

        // set changed values in data object
        if (deltaKeys.length) {
          delta.ship = {};
          delta.ship.state = _.pick(next, deltaKeys);
        }

        // init missiles
        var missiles = [];
        var missile;

        // iterate over missiles
        for (var i = 0; i < player.ship.missiles.length; i++) {
          missile = player.ship.missiles[i].getState();
          missiles.push(missile);
        }

        if (missiles) {
          delta.ship = delta.ship || {};
          delta.ship.missiles = missiles;
          // console.log(delta.ship.missiles);
        }

        // set changed values in data object
        if (Object.keys(delta).length) {
          delta.time = Date.now();
          data.players[uuid] = delta;
        }

        // only expire socket or browser session clients
        store.zadd('expire', Date.now(), 'player+' + uuid, function(err, res) {});

        // notify async that iterator has completed
        if (typeof callback === 'function') callback();

      });

    });

  };

  var updateNPC = function(store, data, npc, uuid, callback) {

    // defer to redis for absolute state, delta compression
    store.hgetall('npc:' + uuid, function(err, res) {

      // save reference to old values and update state
      var prev = npc.state;

      // some scope issues with iterating over res and updating values individually?
      var next = npc.state = res;

      // init delta array for changed keys
      var delta = [];

      // iterate over new values and compare to old
      var keys = Object.keys(next);
      var length = keys.length;
      var key;

      for (var i = 0; i < length; i++) {
        key = keys[i];

        // check for changed values and push key to delta array
        if (prev[key] !== next[key]) {
          delta.push(key);
        }
      }

      // set changed values in data object
      if (delta.length) {
        data.npcs[uuid] = _.pick(next, delta);
      }

      // expire all NPCs to clean redis on server crash
      store.zadd('expire', Date.now(), 'npc+' + uuid, function(err, res) {});

      // notify async.forEach in updateNPCs that function has completed
      if (typeof callback === 'function') callback();

    });

  };

  var update = function(socket, data) {

    // server time stamp
    data.time = Date.now();

    // console.log(data);

    /*
    var keys = Object.keys(data.players);
    var key;

    for (var i = 0; i < keys.length; i++) {
      key = keys[i];
      if (data.players[key].ship) {
        console.log(data.players[key].ship);
      }
    }
    */

    // return delta object to client
    socket.io.sockets.emit('state:update', data);

  };

  var loop = function(socket, store) {

    // create data object containing
    // authoritative state and last processed input id
    var data = {};
    data.players = {};
    data.npcs = {};

    // get updated states from redis, then return delta object to client
    async.parallel([
      function(callback) { updatePlayers(store, data, callback) },
      function(callback) { updateNPCs(store, data, callback) }
    ], function() {
      update(socket, data);
    });

  };

  return {
    init: init,
    updatePlayers: updatePlayers,
    updateNPCs: updateNPCs,
    loop: loop
  };

});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
goog.addDependency('../../../tachyfont/backendservice.js',
    ['tachyfont.BackendService', 'tachyfont.GoogleBackendService'],
    ['goog.Promise', 'goog.events', 'goog.functions', 'goog.net.EventType',
     'goog.net.XhrIo', 'tachyfont.FontInfo']);
goog.addDependency('../../../tachyfont/binaryfonteditor.js',
  ['tachyfont.BinaryFontEditor'],
  []);
goog.addDependency('../../../tachyfont/charcmapinfo.js',
    ['tachyfont.CharCmapInfo'],
    []);
goog.addDependency('../../../tachyfont/demobackendservice.js',
    ['tachyfont.DemoBackendService'],
    ['goog.Promise', 'goog.events', 'goog.net.EventType', 'goog.net.XhrIo',
     'tachyfont.BackendService', 'tachyfont.FontInfo',
     'tachyfont.GlyphBundleResponse']);
goog.addDependency('../../../tachyfont/fontinfo.js',
  ['tachyfont.FontInfo'],
  []);
goog.addDependency('../../../tachyfont/fontsinfo.js',
  ['tachyfont.FontsInfo'],
  ['tachyfont.FontInfo']);
goog.addDependency('../../../tachyfont/googlebackendservice.js',
    ['tachyfont.GoogleBackendService'],
    ['goog.Promise', 'tachyfont.BackendService', 'tachyfont.FontInfo',
     'tachyfont.GlyphBundleResponse', 'tachyfont.utils']);
goog.addDependency('../../../tachyfont/glyphbundleresponse.js',
    ['tachyfont.GlyphBundleResponse'],
    ['goog.log', 'tachyfont.BinaryFontEditor']);
goog.addDependency('../../../tachyfont/incrementalfont.js',
    ['tachyfont.IncrementalFont', 'tachyfont.TachyFont'],
    ['goog.Promise', 'goog.log', 'goog.log.Level', 'goog.math',
     'tachyfont.BinaryFontEditor', 'tachyfont.CharCmapInfo',
     'tachyfont.DemoBackendService', 'tachyfont.FontInfo',
     'tachyfont.GoogleBackendService', 'tachyfont.IncrementalFontUtils',
     'tachyfont.RLEDecoder', 'tachyfont.chainedPromises', 'tachyfont.promise',
     'tachyfont.utils']);
goog.addDependency('../../../tachyfont/incrementalfontutils.js',
    ['tachyfont.IncrementalFontUtils'],
    ['goog.asserts', 'goog.log', 'tachyfont.BinaryFontEditor',
     'tachyfont.CharCmapInfo', 'tachyfont.FontInfo']);
goog.addDependency('../../../misc_utils.js',
    ['tachyfont_misc_utils'],
    []);
goog.addDependency('../../../tachyfont/rledecoder.js',
    ['tachyfont.RLEDecoder'],
    []);
goog.addDependency('../../../tachyfont/tachyfont.js',
  ['tachyfont', 'tachyfont.IncrementalFontLoader', 'tachyfont.uint8'],
  ['goog.Promise', 'goog.Uri', 'goog.debug.Console', 'goog.debug.Logger',
   'goog.log', 'goog.log.Level', 'tachyfont.BinaryFontEditor',
   'tachyfont.FontInfo', 'tachyfont.FontsInfo',
   'tachyfont.IncrementalFontUtils', 'tachyfont.TachyFont',
   'tachyfont.TachyFontSet', 'tachyfont.Reporter']);
goog.addDependency('../../../tachyfont/tachyfontpromise.js',
    ['tachyfont.promise', 'tachyfont.chainedPromises'],
    ['goog.Promise']);
goog.addDependency('../../../tachyfont/tachyfontreporter.js',
    ['tachyfont.Reporter'],
    ['goog.log']);
goog.addDependency('../../../tachyfont/tachyfontset.js',
    ['tachyfont.TachyFontSet'],
    ['goog.Promise', 'goog.array', 'goog.log', 'goog.style',
     'tachyfont.IncrementalFontUtils', 'tachyfont.chainedPromises']);
goog.addDependency('../../../tachyfont/tachyfontutils.js',
    ['tachyfont.utils'],
    ['goog.crypt.Md5']);
goog.addDependency('../../../tachyfont/webfonttailor.js',
    ['webfonttailor'],
    ['tachyfont.FontInfo', 'tachyfont.FontInfo', 'tachyfont.FontsInfo',
     'webfonttailor.alternate']);
goog.addDependency('../../../tachyfont/webfonttailoralternate.js',
    ['webfonttailor.alternate'],
    ['tachyfont.FontInfo', 'tachyfont.FontsInfo']);
<commit_msg>Manually update the closure deps file.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
goog.addDependency('../../../tachyfont/backendservice.js',
    ['tachyfont.BackendService'],
    ['goog.Promise', 'goog.events', 'goog.functions', 'goog.net.EventType',
     'goog.net.XhrIo', 'tachyfont.FontInfo', 'tachyfont.GlyphBundleResponse']);
goog.addDependency('../../../tachyfont/binaryfonteditor.js',
    ['tachyfont.BinaryFontEditor'],
    []);
goog.addDependency('../../../tachyfont/browser_chrome.js',
    ['tachyfont.Browser'],
    ['goog.Promise']);
goog.addDependency('../../../tachyfont/charcmapinfo.js',
    ['tachyfont.CharCmapInfo'],
    []);
goog.addDependency('../../../tachyfont/cmap.js',
    ['tachyfont.Cmap'],
    ['goog.log', 'goog.log.Level', 'tachyfont.BinaryFontEditor']);
goog.addDependency('../../../tachyfont/demobackendservice.js',
    ['tachyfont.DemoBackendService'],
    ['goog.events', 'goog.net.EventType', 'goog.net.XhrIo',
     'tachyfont.BackendService', 'tachyfont.FontInfo']);
goog.addDependency('../../../tachyfont/fontinfo.js',
    ['tachyfont.FontInfo'],
    []);
goog.addDependency('../../../tachyfont/fontsinfo.js',
    ['tachyfont.FontsInfo'],
    ['tachyfont.FontInfo']);
goog.addDependency('../../../tachyfont/glyphbundleresponse.js',
    ['tachyfont.GlyphBundleResponse'],
    ['goog.log', 'tachyfont.BinaryFontEditor']);
goog.addDependency('../../../tachyfont/googlebackendservice.js',
    ['tachyfont.GoogleBackendService'],
    ['goog.Promise', 'tachyfont.BackendService', 'tachyfont.FontInfo',
     'tachyfont.utils']);
goog.addDependency('../../../tachyfont/incrementalfont.js',
    ['tachyfont.IncrementalFont'],
    ['goog.Promise', 'goog.log', 'goog.log.Level', 'goog.math',
     'tachyfont.BinaryFontEditor', 'tachyfont.Browser',
     'tachyfont.CharCmapInfo', 'tachyfont.Cmap',
     'tachyfont.DemoBackendService', 'tachyfont.FontInfo',
     'tachyfont.GoogleBackendService', 'tachyfont.IncrementalFontUtils',
     'tachyfont.Persist', 'tachyfont.RLEDecoder', 'tachyfont.chainedPromises',
     'tachyfont.promise', 'tachyfont.utils']);
goog.addDependency('../../../tachyfont/incrementalfontutils.js',
    ['tachyfont.IncrementalFontUtils'],
    ['goog.asserts', 'goog.log', 'tachyfont.BinaryFontEditor',
     'tachyfont.CharCmapInfo', 'tachyfont.FontInfo']);
goog.addDependency('../../../misc_utils.js',
    ['tachyfont_misc_utils'],
    []);
goog.addDependency('../../../tachyfont/persist_idb.js',
    ['tachyfont.Persist'],
    ['goog.Promise']);
goog.addDependency('../../../tachyfont/rledecoder.js',
    ['tachyfont.RLEDecoder'],
    []);
goog.addDependency('../../../tachyfont/tachyfont.js',
    ['tachyfont', 'tachyfont.IncrementalFontLoader', 'tachyfont.TachyFont',
     'tachyfont.uint8',],
    ['goog.Promise', 'goog.Uri', 'goog.debug.Console', 'goog.debug.Logger',
     'goog.log', 'goog.log.Level', 'tachyfont.BinaryFontEditor',
     'tachyfont.FontInfo', 'tachyfont.FontsInfo',
     'tachyfont.IncrementalFont', 'tachyfont.IncrementalFontUtils',
     'tachyfont.Reporter', 'tachyfont.TachyFontSet']);
goog.addDependency('../../../tachyfont/tachyfontpromise.js',
    [ 'tachyfont.chainedPromises', 'tachyfont.promise'],
    ['goog.Promise']);
goog.addDependency('../../../tachyfont/tachyfontreporter.js',
    ['tachyfont.Reporter'],
    ['goog.log']);
goog.addDependency('../../../tachyfont/tachyfontset.js',
    ['tachyfont.TachyFontSet'],
    ['goog.Promise', 'goog.array', 'goog.log', 'goog.style',
     'tachyfont.IncrementalFontUtils', 'tachyfont.chainedPromises']);
goog.addDependency('../../../tachyfont/tachyfontutils.js',
    ['tachyfont.utils'],
    []);
goog.addDependency('../../../tachyfont/webfonttailor.js',
    ['webfonttailor'],
    ['tachyfont.FontInfo', 'tachyfont.FontsInfo', 'webfonttailor.alternate']);
goog.addDependency('../../../tachyfont/webfonttailoralternate.js',
    ['webfonttailor.alternate'],
    ['tachyfont.FontInfo', 'tachyfont.FontsInfo']);
<|endoftext|>