<commit_before>"use strict";

var request  = require("request");
var cheerio  = require("cheerio");
var moment   = require("moment");
var mongoose = require("mongoose");
var async    = require("async");
var crypto   = require("crypto");

// how many weeks should we parse?
var weeksToParse = 2;

var parse = function(html) {
  var table = null;
  var days = [];
  var timeTableGrid = [];
  var lectures = [];
  var $ = cheerio.load(html);

  // load days before we cut of td.plan_rahmen stuff
  days = getDaysInThisWeek($);

  // remove td.plan rahmen from the table, this removes a lot of trouble
  table = $("th.plan_rahmen").closest("table");
  table.find(".plan_rahmen").remove();

  // lets parse
  // get all the rows of the timetable (except the header (days) row)
  var rows = table.children("tr:not(:first-child)");

  // create and prefill the grid, which holds our whole timetable
  for (var i = 0; i < rows.length; i++) {     // rows
    timeTableGrid[i] = new Array(days.length);
    for (var j = 0; j < days.length; j++) {   // columns
      timeTableGrid[i][j] = ".";
    }
  }

  // iterate over each row and each cell
  rows.each(function(i) {
    var cells = $(this).children("td:not(:first-child)");

    // it seems like cheerio doesnt support collection.get(), so we just
    // bottle that cheerio collection into a native array.
    var cellsArray = [];
    cells.each(function(j) {
      cellsArray.push($(this));
    });

    for (var j = 0; j < days.length; j++) {
      if (timeTableGrid[i][j] === ".") { // here must be a td-(lecture)-element

        var currCell = cellsArray[0];
        cellsArray.shift(); // removes the first element, its a pseudo queue

        if (currCell.attr("class").indexOf("plan2") > -1) { // here we have a lecture
          // now mark the whole rowspan down as the same lecture
          var rowspan = currCell.attr("rowspan");
          for (var k = 0; k < rowspan; k++) {
            timeTableGrid[i+k][j] = j;
          }

          // parse the lectures td element
          var moreLectures = parseGroupsInLecture(currCell, days, j);
          lectures = lectures.concat(moreLectures);
        }
      }
    }
  });
  return lectures;
};

var insertInDatabase = function(lectures, cb) {
  // create models from our schemas
  var Lecture = require("./model_lecture");

  // push every lecture to our db
  async.each(lectures, function(lecture, cb) {
    // create Lecture from our Model
    var Lec = new Lecture();
    // set attributes
    Lec.date = lecture.start;
    Lec.fullLectureName = lecture.lsfName;
    Lec.shortLectureName = lecture.shortName;
    Lec.room = lecture.lsfRoom;
    Lec.startTime = lecture.start;
    Lec.endTime = lecture.end;
    Lec.group = lecture.group;
    Lec.hashCode = lecture.hashCode;
    // save lecture to db
    Lec.save(cb);
  }, function(err) {
    if (err) { console.log(err); }
    console.log("Added " + lectures.length + " lectures in the database...");
    cb();
  });
};

var parseGroupsInLecture = function(html, days, dayPos) {
  var lectures = [];
  var $ = cheerio.load(html);

  // there could be more then one groups in this lecture,
  // we create a lecture for every group
  html.find("table").each(function(i) {
    var time = trimProperty($(this).find("td.notiz").first().text());
    var date = days[dayPos];

    var lecture = {};
    lecture.lsfName = $(this).find("a").first().attr("title");
    lecture.start = parseStartEnd(date, time).start;
    lecture.end = parseStartEnd(date, time).end;
    lecture.lsfDate = date;
    lecture.lsfTime = time;
    lecture.lsfRoom = $(this).find("td.notiz a").first().text();
    lecture.group = parseGroup(lecture.lsfName);
    lecture.shortName = parseShortName(lecture.lsfName);
    lecture.hashCode = hashCode(lecture.lsfName+lecture.lsfDate+lecture.lsfTime+lecture.lsfRoom);

    lectures.push(lecture);
  });
  return lectures;
};

var parseGroup = function(s) {
    return s.split(" ")[0];
};

var parseShortName = function(s) {
    var parts = s.split(" ");
    parts.shift();
    var shortName = parts.join(" ");
    return shortName;
};

var parseStartEnd = function(date, time) {
    var start = null;
    var end = null;
    var timeString = time.split(" ")[0];
    var starTimeString = timeString.split("-")[0];
    var endTimeString = timeString.split("-")[1];

    var dateString = date.split(" ")[1];
    var startString = dateString + " " + starTimeString;
    var endString  = dateString + " " + endTimeString;

    start = moment(startString, "DD-MM-YYYY HH:mm");
    end = moment(endString, "DD-MM-YYYY HH:mm");

    return { start: start, end: end };
};

var trimProperty = function(s) {
  s = s.replace(/ /g, ""); // remove that whitespace
  s = s.replace(/\n/g, "");
  s = s.replace(/\r/g, "");
  s = s.trim();
  return s;
};

var outputAsTable = function(timeTableGrid, iteration) {
  for (var i = 0; i < timeTableGrid.length; i++) {
    var row = timeTableGrid[i];
    process.stdout.write(iteration + ". ROW: " + i + ":\t");

    for (var j = 0; j < row.length; j++) {
      process.stdout.write(row[j] + "  ");
    }

    process.stdout.write("\n");
  }
};

var getDaysInThisWeek = function($) {
  var days = [];
  var headerRow = $("th.plan_rahmen").closest("table").children("tr").first();
  headerRow.find("th").each(function(index) {
    var day = $(this).text();
    day = day.replace(/ /g, "");
    day = day.replace(/\n/g, " ");
    day = day.trim();
    days.push(day);
  });
  return days;
};

var hashCode = function(s){
	var hash = crypto.createHash('md5').update(s).digest('hex');
    return hash;
};

Object.defineProperty(Date.prototype, "getWeek", {
  value: function() {
    var determinedate = new Date();
    determinedate.setFullYear(this.getFullYear(), this.getMonth(), this.getDate());
    var D = determinedate.getDay();
    if(D === 0) { D = 7; }
    determinedate.setDate(determinedate.getDate() + (4 - D));
    var YN = determinedate.getFullYear();
    var ZBDoCY = Math.floor((determinedate.getTime() - new Date(YN, 0, 1, -6)) / 86400000);
    var WN = 1 + Math.floor(ZBDoCY / 7);
    return WN;
  }
});

// create urls including the week & year which we want to parse
var createUrls = function() {
  var urls = [];
  for(var i = 0; i < weeksToParse; i++) {
    var today = new Date();
    var currentWeek = today.getWeek() + i;
    var currentYear = today.getFullYear();
    var url = "https://lsf.hft-stuttgart.de/qisserver/rds?state=wplan&k_abstgv.abstgvnr=262&week=" + currentWeek + "_" + currentYear + "&act=stg&pool=stg&show=plan&P.vx=lang&P.Print=";
    urls.push(url);
  }
  return urls;
};

var startParser = function() {
  // connect to mongodb (if not already)
  if(mongoose.connection.readyState === 0) {
    mongoose.connect("mongodb://localhost:27017/wuw");
  }

  // create model from our schema (needed for drop)
  var Lecture = require("./model_lecture");

  // create the urls to parse
  var urls = createUrls();

  console.log("Started parsing of " + weeksToParse + " weeks (this inlcuded)...");

  // drop current lecture collection to get a fresh result
  mongoose.connection.collections.lectures.drop(function(err) {
    if(err) { console.log(err); }

    console.log("Dropped old \""+ Lecture.collection.name + "\" Collection, lets start clean...");

    // parse every url
    async.each(urls, function(url, cb) {
      request(url, function(error, response, html) {
        if(!error) {
          var lectures = parse(html);
          insertInDatabase(lectures, cb);
        }
      });
    }, function() {
      // everything done
      mongoose.disconnect();
      console.log("...done!");
    });
  });
};

// start the magic
startParser();

module.exports = { parse: parse, startParser: startParser };<commit_msg>prevent calendar weeks >52<commit_after>"use strict";

var request  = require("request");
var cheerio  = require("cheerio");
var moment   = require("moment");
var mongoose = require("mongoose");
var async    = require("async");
var crypto   = require("crypto");

// how many weeks should we parse?
var weeksToParse = 2;

var parse = function(html) {
  var table = null;
  var days = [];
  var timeTableGrid = [];
  var lectures = [];
  var $ = cheerio.load(html);

  // load days before we cut of td.plan_rahmen stuff
  days = getDaysInThisWeek($);

  // remove td.plan rahmen from the table, this removes a lot of trouble
  table = $("th.plan_rahmen").closest("table");
  table.find(".plan_rahmen").remove();

  // lets parse
  // get all the rows of the timetable (except the header (days) row)
  var rows = table.children("tr:not(:first-child)");

  // create and prefill the grid, which holds our whole timetable
  for (var i = 0; i < rows.length; i++) {     // rows
    timeTableGrid[i] = new Array(days.length);
    for (var j = 0; j < days.length; j++) {   // columns
      timeTableGrid[i][j] = ".";
    }
  }

  // iterate over each row and each cell
  rows.each(function(i) {
    var cells = $(this).children("td:not(:first-child)");

    // it seems like cheerio doesnt support collection.get(), so we just
    // bottle that cheerio collection into a native array.
    var cellsArray = [];
    cells.each(function(j) {
      cellsArray.push($(this));
    });

    for (var j = 0; j < days.length; j++) {
      if (timeTableGrid[i][j] === ".") { // here must be a td-(lecture)-element

        var currCell = cellsArray[0];
        cellsArray.shift(); // removes the first element, its a pseudo queue

        if (currCell.attr("class").indexOf("plan2") > -1) { // here we have a lecture
          // now mark the whole rowspan down as the same lecture
          var rowspan = currCell.attr("rowspan");
          for (var k = 0; k < rowspan; k++) {
            timeTableGrid[i+k][j] = j;
          }

          // parse the lectures td element
          var moreLectures = parseGroupsInLecture(currCell, days, j);
          lectures = lectures.concat(moreLectures);
        }
      }
    }
  });
  return lectures;
};

var insertInDatabase = function(lectures, cb) {
  // create models from our schemas
  var Lecture = require("./model_lecture");

  // push every lecture to our db
  async.each(lectures, function(lecture, cb) {
    // create Lecture from our Model
    var Lec = new Lecture();
    // set attributes
    Lec.date = lecture.start;
    Lec.fullLectureName = lecture.lsfName;
    Lec.shortLectureName = lecture.shortName;
    Lec.room = lecture.lsfRoom;
    Lec.startTime = lecture.start;
    Lec.endTime = lecture.end;
    Lec.group = lecture.group;
    Lec.hashCode = lecture.hashCode;
    // save lecture to db
    Lec.save(cb);
  }, function(err) {
    if (err) { console.log(err); }
    console.log("Added " + lectures.length + " lectures in the database...");
    cb();
  });
};

var parseGroupsInLecture = function(html, days, dayPos) {
  var lectures = [];
  var $ = cheerio.load(html);

  // there could be more then one groups in this lecture,
  // we create a lecture for every group
  html.find("table").each(function(i) {
    var time = trimProperty($(this).find("td.notiz").first().text());
    var date = days[dayPos];

    var lecture = {};
    lecture.lsfName = $(this).find("a").first().attr("title");
    lecture.start = parseStartEnd(date, time).start;
    lecture.end = parseStartEnd(date, time).end;
    lecture.lsfDate = date;
    lecture.lsfTime = time;
    lecture.lsfRoom = $(this).find("td.notiz a").first().text();
    lecture.group = parseGroup(lecture.lsfName);
    lecture.shortName = parseShortName(lecture.lsfName);
    lecture.hashCode = hashCode(lecture.lsfName+lecture.lsfDate+lecture.lsfTime+lecture.lsfRoom);

    lectures.push(lecture);
  });
  return lectures;
};

var parseGroup = function(s) {
    return s.split(" ")[0];
};

var parseShortName = function(s) {
    var parts = s.split(" ");
    parts.shift();
    var shortName = parts.join(" ");
    return shortName;
};

var parseStartEnd = function(date, time) {
    var start = null;
    var end = null;
    var timeString = time.split(" ")[0];
    var starTimeString = timeString.split("-")[0];
    var endTimeString = timeString.split("-")[1];

    var dateString = date.split(" ")[1];
    var startString = dateString + " " + starTimeString;
    var endString  = dateString + " " + endTimeString;

    start = moment(startString, "DD-MM-YYYY HH:mm");
    end = moment(endString, "DD-MM-YYYY HH:mm");

    return { start: start, end: end };
};

var trimProperty = function(s) {
  s = s.replace(/ /g, ""); // remove that whitespace
  s = s.replace(/\n/g, "");
  s = s.replace(/\r/g, "");
  s = s.trim();
  return s;
};

var outputAsTable = function(timeTableGrid, iteration) {
  for (var i = 0; i < timeTableGrid.length; i++) {
    var row = timeTableGrid[i];
    process.stdout.write(iteration + ". ROW: " + i + ":\t");

    for (var j = 0; j < row.length; j++) {
      process.stdout.write(row[j] + "  ");
    }

    process.stdout.write("\n");
  }
};

var getDaysInThisWeek = function($) {
  var days = [];
  var headerRow = $("th.plan_rahmen").closest("table").children("tr").first();
  headerRow.find("th").each(function(index) {
    var day = $(this).text();
    day = day.replace(/ /g, "");
    day = day.replace(/\n/g, " ");
    day = day.trim();
    days.push(day);
  });
  return days;
};

var hashCode = function(s){
	var hash = crypto.createHash('md5').update(s).digest('hex');
    return hash;
};

Object.defineProperty(Date.prototype, "getWeek", {
  value: function() {
    var determinedate = new Date();
    determinedate.setFullYear(this.getFullYear(), this.getMonth(), this.getDate());
    var D = determinedate.getDay();
    if(D === 0) { D = 7; }
    determinedate.setDate(determinedate.getDate() + (4 - D));
    var YN = determinedate.getFullYear();
    var ZBDoCY = Math.floor((determinedate.getTime() - new Date(YN, 0, 1, -6)) / 86400000);
    var WN = 1 + Math.floor(ZBDoCY / 7);
    return WN;
  }
});

// create urls including the week & year which we want to parse
var createUrls = function() {
  var urls = [];
  for(var i = 0; i < weeksToParse; i++) {
    var today = new Date();
    var currentWeek = (today.getWeek() + i) % 52;
    var currentYear = today.getFullYear();
    var url = "https://lsf.hft-stuttgart.de/qisserver/rds?state=wplan&k_abstgv.abstgvnr=262&week=" + currentWeek + "_" + currentYear + "&act=stg&pool=stg&show=plan&P.vx=lang&P.Print=";
    urls.push(url);
  }
  return urls;
};

var startParser = function() {
  // connect to mongodb (if not already)
  if(mongoose.connection.readyState === 0) {
    mongoose.connect("mongodb://localhost:27017/wuw");
  }

  // create model from our schema (needed for drop)
  var Lecture = require("./model_lecture");

  // create the urls to parse
  var urls = createUrls();

  console.log("Started parsing of " + weeksToParse + " weeks (this inlcuded)...");

  // drop current lecture collection to get a fresh result
  mongoose.connection.collections.lectures.drop(function(err) {
    if(err) { console.log(err); }

    console.log("Dropped old \""+ Lecture.collection.name + "\" Collection, lets start clean...");

    // parse every url
    async.each(urls, function(url, cb) {
      request(url, function(error, response, html) {
        if(!error) {
          var lectures = parse(html);
          insertInDatabase(lectures, cb);
        }
      });
    }, function() {
      // everything done
      mongoose.disconnect();
      console.log("...done!");
    });
  });
};

// start the magic
startParser();

module.exports = { parse: parse, startParser: startParser };<|endoftext|>


<commit_before>word_type = {}
word_type.verb = "verb"
word_type.subject = "subject"
actionz = {}
actionz.use = "use"
np = "npc"
actionz.walkd = "walk_direct"
actionz.walk = "walk"
actionz.examine = "examine"
actionz.examineRoom = "examineRoom"
actionz.eat = "eat"

sentences = []
sentences[0] = [[words.use], [words.the, null],[item, obj] ]
sentences[1] = [[direction]]
sentences[2] = [[words.go, words.jump, words.walk],[words.to, null], [direction]  ]
sentences[3] = [[words.examine, words.look],[words.the, null], [item,obj,]  ]
sentences[4] = [[words.master]]
sentences[5] = [[words.eat],[obj]]
sentences[6] = [[words.attack],[np]]
sentences[7] = [[words.stand, words.get],[words.up]]
sentences[8] = [[words.pick, words.get],[words.up, null],[item, obj]]
sentences[9] = [[words.help]]
sentences[10] = [[words.use], [words.knife], [words.on, null], [np] ]
sentences[11] = [[words.sit], [words.in, words.on, null],[obj] ]
sentences[12] = [[words.lie], [words.down]]
sentences[13] = [[words.open], [words.window, words.airduct, words["air duct"], words["air vent"]]]
sentences[14] = [[words.jump],[words.out,null],[words.of,null], [words.window]]
sentences[15] = [[words.unlock, words.open],[words.door]]
sentences[16] = [[words.stand]]
sentences[17] = [[words.lie]]
sentences[18] = [[words.brush],[words.teeth]]
sentences[19] = [[words.read],[item]]
sentences[20] = [[words.enter],[words.airduct, words["air duct"], words["air vent"]]]
sentences[21] = [[words.move],[words.painting]]
sentences[22] = [[words.open, words.unlock], [obj]]
sentences[23] = [[words.close], [obj]]
sentences[24] = [[words.examine]]
sentences[25] = [[words.credits]]
sentences[26] = [[words.license]]
sentences[27] = [[words.inventory]]
sentences[28] = [[words.turn, null], [words.on, null], [words.light,words.lights]]
sentences[29] = [[words.press],[words.red,null],[words.button]]

definitions = []
definitions[0] = {a:0,cmd: actions.use, 0: word_type.verb, 2: word_type.subject}
definitions[1] =  {a:1,cmd: actions.move, 0: word_type.subject, verb: words.walk}
definitions[2] = {a:2,cmd: actions.move, 0: word_type.verb, 2: word_type.subject}
definitions[3] =  {a:3,cmd: actions.examine, 0: word_type.verb, 2: word_type.subject}

definitions[5] =  {a:5,cmd: actions.eat, 0: word_type.verb, 1: word_type.subject}
definitions[6] =  {a:6,cmd: actions.kill, 0: word_type.verb, 1: word_type.subject}
definitions[7] =  {a:7,cmd: actions.stand, 0: word_type.verb}
definitions[8] =  {a:8,cmd: actions.pickup, 0: word_type.verb, 2: word_type.subject}
definitions[9] =  {a:7,cmd: actions.help,verb:words.help}
definitions[10] = {a: 10, cmd: actions.knife	, verb: words.attack, 3:word_type.subject}
definitions[11] =  {a:11,cmd: actions.sit, 0: word_type.verb, 2: word_type.subject}
definitions[12] =  {a:12,cmd: actions.lie, 0: word_type.verb}
definitions[13] =  {a:13,cmd: actions.window_text, 0: word_type.verb, 1: word_type.subject}
definitions[14] =  {a:14,cmd: actions.move, verb:words.jump, subject: words.west}
definitions[15] =  {a:15,cmd: actions.open, 0: word_type.verb, 1: word_type.subject}
definitions[16] =  {a:17,cmd: actions.stand, 0: word_type.verb}
definitions[17] =  {a:18,cmd: actions.lie, 0: word_type.verb}
definitions[18] =  {a:18,cmd: actions.brush, 0: word_type.verb, 1: word_type.subject}
definitions[19] =  {a:19,cmd: actions.read, 0: word_type.verb, 1: word_type.subject}
definitions[20] =  {a:20,cmd: actions.enter_obj, 0: word_type.verb, 1: word_type.subject}
definitions[21] =  {a:21,cmd: actions.move_obj, 0: word_type.verb, 1: word_type.subject}
definitions[22] =  {a:22,cmd: actions.open, verb: words.open, 1: word_type.subject}
definitions[23] =  {a:23,cmd: actions.close, 0: word_type.verb, 1: word_type.subject}
definitions[24] =  {a:4,cmd: actions.examine, 0: word_type.verb}
definitions[25] =  {a:7,cmd: actions.credits,verb:words.help}
definitions[26] =  {a:7,cmd: actions.license,verb:words.help}
definitions[27] =  {a:7,cmd: actions.inventory,verb:words.help}
definitions[28] =  {a:7,cmd: actions.turn_lights,verb:words.turn, subject: words.lights}
definitions[29] =  {a:7,cmd: actions.press,verb:words.help,subject:words.button}

parse_checks = {}
parse_checks.in_inventory = null
parse_checks.direction_available = null
parse_checks.visible = null
parse_checks.npc_here = null
parse_checks.has_knife = null

selectors = []
selectors [0] = {subject: parse_checks.in_inventory}
selectors [1] = {subject: parse_checks.direction_available}
selectors [2] = {subject: parse_checks.direction_available}
selectors [3] = {subject: parse_checks.visible}
selectors [4] = {}
selectors [5] = {subject: parse_checks.visible}
selectors [6] = {subject: parse_checks.npc_here}
selectors [7] = {subject: parse_checks.npc_here}
selectors [8] = {subject: parse_checks.on_floor}
selectors [9] = {}
selectors [10] = {subject: parse_checks.has_knife}
selectors [11] = {subject: parse_checks.visible}
selectors [12] = {}
selectors [13] = {subject: parse_checks.visible}
selectors [14] = {subject: parse_checks.visible}
selectors [15] = {subject: parse_checks.in_inventory}
selectors [16] = {subject: parse_checks.visible}
selectors [17] = {}
selectors [18] = {}

function spatify(prev, current, index, array){
	if ( index == 0)
		return current
	else
		return prev + " "+ current
}
function complete(sen, orig)
{

	var last = sen.pop()
	if(words[last])
		return sen + " "+ last
	var sentence = tokenize(sen.reduce(spatify, ""))
	var result = null
	var j=0
	var i=0
	possibles = []
	for(var i = 0; i < sentences.length; i++)
	{
		result = null

		var hit=true
		// check all words
		var sen_look = 0
		var sent = sentences[i]
		var j=0
		for(j=0;j< sent.length && hit;j++)
		{
			var result = definitions[i]
			//sentence
			var check_sen = sent[j]
			var found = false
			var has_null = false

			for(var k=0;k < check_sen.length;k++)
			{
				if(sen_look >= sentence.length)
				{
					possibles[possibles.length] = [i,j,k]
					hit = false
					break
				}

				if(!check_sen[k])
					has_null = true

				// TODO: synonym check
				var find = sentence[sen_look]
				while (true){
					if(find == check_sen[k] || find.type == check_sen[k] )
					{
						if(result[j])
							result[result[j]] = find
						found = true
					}
					if(find && (find.synonym!= null || find.synonym != undefined) && !found)
						find = find.synonym
					else
						break
				}

			}
			if((! found) && has_null)
				{sen_look --
					found=true
				}

				sen_look ++

				hit = hit && found
			}

		}
		var result = []
		if(  possibles.length > 10)
			return false
		if(possibles.length > 0)
		{
			var wordz =  ac(last)
			for(var i = 0; i< wordz.length; i++)
			{
				var found = false

				var j=0;
				for( j=0;j< possibles.length; j++)
				{
					var find = words[wordz[i]]
					var has_null=true
					while(has_null){
						has_null = false

						for(var k = 0 ; k < sentences[possibles[j][0]][possibles[j][1]].length; k++)
						{
							while (true){

								if(!sentences[possibles[j][0]][possibles[j][1]][k])
									has_null = true

								if(find == sentences[possibles[j][0]][possibles[j][1]][k] || find.type == sentences[possibles[j][0]][possibles[j][1]][k] )
								{
									found = true
								}
								if(find && (find.synonym!= null || find.synonym != undefined) && !found)
									find = find.synonym
								else
									break
							}

						}
						if(!found && has_null)
						{
							possibles[j][1] += 1

						}
					}

				}
				if(found)
					result[result.length] = wordz[i]
			}
			if(result.length > 1 && result.length < 10)
			{
				konsole.print("Options: " + result)
				return orig
			}
			if(result.length == 1)
			{
				konsole.print("only one option")
				sen[sen.length] = result[0]
				return sen.reduce(spatify, "")
			}

			}
		else
		{
			if(parse(sentence) != null)
				konsole.print("Sentence already complete as it is. Can't be extended.")
			else
				konsole.print("Sentence can't be completed, it's not a valid part of a sentence")
		}

		return orig
	}

	function parse(sentence)
	{
		if (!sentence )
			return false

		for(var i = 0; i < sentences.length; i++)
		{
			result = null

			var hit=true
		// check all words
		var sen_look = 0
		var sent = sentences[i]
		var j=0
		for(j=0;j< sent.length && hit;j++)
		{
			var result = definitions[i]
			//sentence
			var check_sen = sent[j]
			var found = false
			var has_null = false

			for(var k=0;k < check_sen.length;k++)
			{
				if(sen_look >= sentence.length)
				{
					hit = false

					break
				}

				if(!check_sen[k])
					has_null = true

				// TODO: synonym check
				var find = sentence[sen_look]
				while (true){

					if(find == check_sen[k] || find.type == check_sen[k] )
					{
						if(result[j])
							result[result[j]] = find
						found = true
					}
					if(find && (find.synonym!= null || find.synonym != undefined) && !found)
						find = find.synonym
					else
						break
				}

			}
			if((! found) && has_null)
				{sen_look --
					found=true
				}
				if(!found && k + sen_look[j]+1  >= sentences[i].length)
				{
					sen_look ++
					hit = true
					break
				}
				sen_look ++

				hit = hit && found
			}
			if(hit)

			if (hit && sen_look == sentence.length)
				return result
		}
		return null

	}
<commit_msg>push painting fixed.<commit_after>word_type = {}
word_type.verb = "verb"
word_type.subject = "subject"
actionz = {}
actionz.use = "use"
np = "npc"
actionz.walkd = "walk_direct"
actionz.walk = "walk"
actionz.examine = "examine"
actionz.examineRoom = "examineRoom"
actionz.eat = "eat"

sentences = []
sentences[0] = [[words.use], [words.the, null],[item, obj] ]
sentences[1] = [[direction]]
sentences[2] = [[words.go, words.jump, words.walk],[words.to, null], [direction]  ]
sentences[3] = [[words.examine, words.look],[words.the, null], [item,obj,]  ]
sentences[4] = [[words.master]]
sentences[5] = [[words.eat],[obj]]
sentences[6] = [[words.attack],[np]]
sentences[7] = [[words.stand, words.get],[words.up]]
sentences[8] = [[words.pick, words.get],[words.up, null],[item, obj]]
sentences[9] = [[words.help]]
sentences[10] = [[words.use], [words.knife], [words.on, null], [np] ]
sentences[11] = [[words.sit], [words.in, words.on, null],[obj] ]
sentences[12] = [[words.lie], [words.down]]
sentences[13] = [[words.open], [words.window, words.airduct, words["air duct"], words["air vent"]]]
sentences[14] = [[words.jump],[words.out,null],[words.of,null], [words.window]]
sentences[15] = [[words.unlock, words.open],[words.door]]
sentences[16] = [[words.stand]]
sentences[17] = [[words.lie]]
sentences[18] = [[words.brush],[words.teeth]]
sentences[19] = [[words.read],[item]]
sentences[20] = [[words.enter],[words.airduct, words["air duct"], words["air vent"]]]
sentences[21] = [[words.move,words.push],[words.painting]]
sentences[22] = [[words.open, words.unlock], [obj]]
sentences[23] = [[words.close], [obj]]
sentences[24] = [[words.examine]]
sentences[25] = [[words.credits]]
sentences[26] = [[words.license]]
sentences[27] = [[words.inventory]]
sentences[28] = [[words.turn, null], [words.on, null], [words.light,words.lights]]
sentences[29] = [[words.press],[words.red,null],[words.button]]

definitions = []
definitions[0] = {a:0,cmd: actions.use, 0: word_type.verb, 2: word_type.subject}
definitions[1] =  {a:1,cmd: actions.move, 0: word_type.subject, verb: words.walk}
definitions[2] = {a:2,cmd: actions.move, 0: word_type.verb, 2: word_type.subject}
definitions[3] =  {a:3,cmd: actions.examine, 0: word_type.verb, 2: word_type.subject}

definitions[5] =  {a:5,cmd: actions.eat, 0: word_type.verb, 1: word_type.subject}
definitions[6] =  {a:6,cmd: actions.kill, 0: word_type.verb, 1: word_type.subject}
definitions[7] =  {a:7,cmd: actions.stand, 0: word_type.verb}
definitions[8] =  {a:8,cmd: actions.pickup, 0: word_type.verb, 2: word_type.subject}
definitions[9] =  {a:7,cmd: actions.help,verb:words.help}
definitions[10] = {a: 10, cmd: actions.knife	, verb: words.attack, 3:word_type.subject}
definitions[11] =  {a:11,cmd: actions.sit, 0: word_type.verb, 2: word_type.subject}
definitions[12] =  {a:12,cmd: actions.lie, 0: word_type.verb}
definitions[13] =  {a:13,cmd: actions.window_text, 0: word_type.verb, 1: word_type.subject}
definitions[14] =  {a:14,cmd: actions.move, verb:words.jump, subject: words.west}
definitions[15] =  {a:15,cmd: actions.open, 0: word_type.verb, 1: word_type.subject}
definitions[16] =  {a:17,cmd: actions.stand, 0: word_type.verb}
definitions[17] =  {a:18,cmd: actions.lie, 0: word_type.verb}
definitions[18] =  {a:18,cmd: actions.brush, 0: word_type.verb, 1: word_type.subject}
definitions[19] =  {a:19,cmd: actions.read, 0: word_type.verb, 1: word_type.subject}
definitions[20] =  {a:20,cmd: actions.enter_obj, 0: word_type.verb, 1: word_type.subject}
definitions[21] =  {a:21,cmd: actions.move_obj, verb:words.move, 1: word_type.subject}
definitions[22] =  {a:22,cmd: actions.open, verb: words.open, 1: word_type.subject}
definitions[23] =  {a:23,cmd: actions.close, 0: word_type.verb, 1: word_type.subject}
definitions[24] =  {a:4,cmd: actions.examine, 0: word_type.verb}
definitions[25] =  {a:7,cmd: actions.credits,verb:words.help}
definitions[26] =  {a:7,cmd: actions.license,verb:words.help}
definitions[27] =  {a:7,cmd: actions.inventory,verb:words.help}
definitions[28] =  {a:7,cmd: actions.turn_lights,verb:words.turn, subject: words.lights}
definitions[29] =  {a:7,cmd: actions.press,verb:words.help,subject:words.button}

parse_checks = {}
parse_checks.in_inventory = null
parse_checks.direction_available = null
parse_checks.visible = null
parse_checks.npc_here = null
parse_checks.has_knife = null

selectors = []
selectors [0] = {subject: parse_checks.in_inventory}
selectors [1] = {subject: parse_checks.direction_available}
selectors [2] = {subject: parse_checks.direction_available}
selectors [3] = {subject: parse_checks.visible}
selectors [4] = {}
selectors [5] = {subject: parse_checks.visible}
selectors [6] = {subject: parse_checks.npc_here}
selectors [7] = {subject: parse_checks.npc_here}
selectors [8] = {subject: parse_checks.on_floor}
selectors [9] = {}
selectors [10] = {subject: parse_checks.has_knife}
selectors [11] = {subject: parse_checks.visible}
selectors [12] = {}
selectors [13] = {subject: parse_checks.visible}
selectors [14] = {subject: parse_checks.visible}
selectors [15] = {subject: parse_checks.in_inventory}
selectors [16] = {subject: parse_checks.visible}
selectors [17] = {}
selectors [18] = {}

function spatify(prev, current, index, array){
	if ( index == 0)
		return current
	else
		return prev + " "+ current
}
function complete(sen, orig)
{

	var last = sen.pop()
	if(words[last])
		return sen + " "+ last
	var sentence = tokenize(sen.reduce(spatify, ""))
	var result = null
	var j=0
	var i=0
	possibles = []
	for(var i = 0; i < sentences.length; i++)
	{
		result = null

		var hit=true
		// check all words
		var sen_look = 0
		var sent = sentences[i]
		var j=0
		for(j=0;j< sent.length && hit;j++)
		{
			var result = definitions[i]
			//sentence
			var check_sen = sent[j]
			var found = false
			var has_null = false

			for(var k=0;k < check_sen.length;k++)
			{
				if(sen_look >= sentence.length)
				{
					possibles[possibles.length] = [i,j,k]
					hit = false
					break
				}

				if(!check_sen[k])
					has_null = true

				// TODO: synonym check
				var find = sentence[sen_look]
				while (true){
					if(find == check_sen[k] || find.type == check_sen[k] )
					{
						if(result[j])
							result[result[j]] = find
						found = true
					}
					if(find && (find.synonym!= null || find.synonym != undefined) && !found)
						find = find.synonym
					else
						break
				}

			}
			if((! found) && has_null)
				{sen_look --
					found=true
				}

				sen_look ++

				hit = hit && found
			}

		}
		var result = []
		if(  possibles.length > 10)
			return false
		if(possibles.length > 0)
		{
			var wordz =  ac(last)
			for(var i = 0; i< wordz.length; i++)
			{
				var found = false

				var j=0;
				for( j=0;j< possibles.length; j++)
				{
					var find = words[wordz[i]]
					var has_null=true
					while(has_null){
						has_null = false

						for(var k = 0 ; k < sentences[possibles[j][0]][possibles[j][1]].length; k++)
						{
							while (true){

								if(!sentences[possibles[j][0]][possibles[j][1]][k])
									has_null = true

								if(find == sentences[possibles[j][0]][possibles[j][1]][k] || find.type == sentences[possibles[j][0]][possibles[j][1]][k] )
								{
									found = true
								}
								if(find && (find.synonym!= null || find.synonym != undefined) && !found)
									find = find.synonym
								else
									break
							}

						}
						if(!found && has_null)
						{
							possibles[j][1] += 1

						}
					}

				}
				if(found)
					result[result.length] = wordz[i]
			}
			if(result.length > 1 && result.length < 10)
			{
				konsole.print("Options: " + result)
				return orig
			}
			if(result.length == 1)
			{
				konsole.print("only one option")
				sen[sen.length] = result[0]
				return sen.reduce(spatify, "")
			}

			}
		else
		{
			if(parse(sentence) != null)
				konsole.print("Sentence already complete as it is. Can't be extended.")
			else
				konsole.print("Sentence can't be completed, it's not a valid part of a sentence")
		}

		return orig
	}

	function parse(sentence)
	{
		if (!sentence )
			return false

		for(var i = 0; i < sentences.length; i++)
		{
			result = null

			var hit=true
		// check all words
		var sen_look = 0
		var sent = sentences[i]
		var j=0
		for(j=0;j< sent.length && hit;j++)
		{
			var result = definitions[i]
			//sentence
			var check_sen = sent[j]
			var found = false
			var has_null = false

			for(var k=0;k < check_sen.length;k++)
			{
				if(sen_look >= sentence.length)
				{
					hit = false

					break
				}

				if(!check_sen[k])
					has_null = true

				// TODO: synonym check
				var find = sentence[sen_look]
				while (true){

					if(find == check_sen[k] || find.type == check_sen[k] )
					{
						if(result[j])
							result[result[j]] = find
						found = true
					}
					if(find && (find.synonym!= null || find.synonym != undefined) && !found)
						find = find.synonym
					else
						break
				}

			}
			if((! found) && has_null)
				{sen_look --
					found=true
				}
				if(!found && k + sen_look[j]+1  >= sentences[i].length)
				{
					sen_look ++
					hit = true
					break
				}
				sen_look ++

				hit = hit && found
			}
			if(hit)

			if (hit && sen_look == sentence.length)
				return result
		}
		return null

	}
<|endoftext|>


<commit_before>// =============================================================================
// This file handles generic routing and can be used for a multitude of purposes
// right now it is only used to serve up index.html, but that could easily be
// changed to other things. The API and the auth routes were moved to separate
// files to further modularize the code.
// =============================================================================
var path = require('path');
var router = require('express').Router();
var multiparty = require('multiparty');

app.get('/', function(req, res) {
  res.sendFile(path.resolve('./src/public/html/index.html'));
});

router.head('/mail', function(req, res) {
  console.log('Received mail HEAD request');

  res.sendStatus(200);
});

router.post('/mail', function(req, res) {
  console.log('Received Mail');
  console.log(req.body);

  var form = new multiparty.Form();

  form.parse(req, function(err, fields, files) {
    console.log(fields);
    console.log(files);
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });

  return;
});

module.exports = router;
<commit_msg>More logging<commit_after>// =============================================================================
// This file handles generic routing and can be used for a multitude of purposes
// right now it is only used to serve up index.html, but that could easily be
// changed to other things. The API and the auth routes were moved to separate
// files to further modularize the code.
// =============================================================================
var path = require('path');
var router = require('express').Router();
var multiparty = require('multiparty');

app.get('/', function(req, res) {
  res.sendFile(path.resolve('./src/public/html/index.html'));
});

router.head('/mail', function(req, res) {
  console.log('Received mail HEAD request');

  res.sendStatus(200);
});

router.post('/mail', function(req, res) {
  console.log('Received Mail');
  console.log(req.body);

  var form = new multiparty.Form();

  form.parse(req, function(err, fields, files) {
    console.log(fields.mailinMsg.text);
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });

  return;
});

module.exports = router;
<|endoftext|>


<commit_before>var util = require('util');
var events = require('events');
var SerialPort = require("serialport").SerialPort;
var fs = require('fs');
var path = require('path');
var glob = require('glob');

function LidarPacket(index, speed, dist1, dist2, dist3, dist4){
    this.index = index;
    this.speed = speed;
    this.dist1 = dist1;
    this.dist2 = dist2;
    this.dist3 = dist3;
    this.dist4 = dist4;
}

/**
 * Computes and returns the checksum as an int.
 * @param {Array} data - The data to compute the checksum for. Must be 20 bytes.
 * @return {Number} The checksum as an integer.
 */
var computeChecksum = function(data) {
    // group the data by word, little-endian
    var dataList = [];
    for (var i = 0; i < 10; i++){
        dataList.push(data[2*i] + (data[2*i+1]<<8));
    }

    // compute the checksum on 32 bits
    var chk32 = 0;
    dataList.forEach(function(item){
        chk32 = (chk32 << 1) + item;
    });

    // return a value wrapped around on 15 bits, and truncated to still fit into 15 bits
    var checksum = (chk32 & 0x7FFF) + (chk32 >> 15); // wrap around to fit into 15 bits
    checksum = checksum & 0x7FFF; // truncate to 15 bits
    return checksum;
};

/**
 * Gets the lidar distance reading from a 4 byte data section.
 * @param {Array} bytes - Buffer with 4 bytes of data.
 * @return {Number} The distance reading as an integer.
 */
var parsePacketDataSection = function(bytes) {
    // if the 8th bit of the byte 1 is set, the data is invalid; the 7th bit of byte 1 is a strength warning (varies with material sometimes) flag, but we are ignoring it for now
    if (bytes[1] & 128 === 128)
        return -1;

    // get distance from byte 0 and byte 1; byte 2 and byte 3 contain signal strength data, which we are ignoring for now
    return bytes[0] + ((bytes[1] & 63) << 8);
};

var parsePacket = function(bytes) {
    // first byte in the packet must be 0xFA
    if (bytes[0] != 0xFA)
        return null;

    // verify data via checksum
    if (computeChecksum(bytes.slice(0, 20)) !== (bytes[20] + (bytes[21] << 8)))
        return null;

    // packet index must be between 0xA0 and 0xF9
    var index = bytes[1];
    if (index < 0xA0 || index > 0xF9)
        return null;

    // adjust index to 0 based index
    index = index - 0xA0;

    // speed unit used by the LIDAR is in 64ths of an RPM; we want the speed in RPMs
    var speed = (bytes[2] + (bytes[3]<<8))/64.0;

    var distances = [];
    for (var i = 1; i < 5; i++)
        distances.push(parsePacketDataSection(bytes.slice(4*i, 4*i+4)));

    return new LidarPacket(index, speed, distances[0], distances[1], distances[2], distances[3]);
};

function Lidar(serialPath, pwmPath) {
    var self = this;
    self._pwmPath = pwmPath;
    self._pwmDuty = 380000;
    self._serialPort = new SerialPort(serialPath, {
        baudrate: 115200
    });

    self._serialPort.on("open", function () {
        console.log('serial port to lidar is open');

        var currentPacketData = [];
        self._serialPort.on('data', function(data) {
            console.log('data received from lidar: ' + data.toString('hex'));
            self.emit('raw data', data);

            for (var i = 0; i < data.length; i++) {
                var b = data[i];

                // if we're starting a new packet, but the current byte doesn't mark the beginning of the packet, skip it
                if (currentPacketData.length === 0 && b != 0xFA)
                    continue;

                currentPacketData.push(b);

                if (currentPacketData.length === 22) {
                    var packet = parsePacket(currentPacketData);
                    if (packet === null) {
                        // packet was invalid; check for 0xFA packet start marker in the last packet, as sometimes some bytes are dropped and the next packet is already started
                        var nextIndex = currentPacketData.slice(1).indexOf(0xFA) + 1;
                        if (nextIndex > 0)
                            currentPacketData = currentPacketData.slice(nextIndex);
                        else
                            currentPacketData = [];
                    }
                    else {
                        self.emit('packet', packet);

                        // reset currentPacketData
                        currentPacketData = [];
                    }
                }
            }
        });
    });

    var targetRPM = 240;
    var lastRPMs = [];
    self.on('packet', function(packet) {
        var rpm = packet.speed;
        var prevRPM = 0;

        lastRPMs.push(rpm);
        if (lastRPMs.length >= 30){
            var prevRPM = lastRPMs.shift();
        }

        if (rpm > 100 && rpm > targetRPM - 5 && rpm < targetRPM + 5 && prevRPM > 0 && Math.abs(rpm - prevRPM) < 1.5) {
            var deltaRPM = targetRPM - rpm;
            var deltaDuty = (deltaRPM / rpm) * self._pwmDuty * 0.5;

            self._pwmDuty += deltaDuty;

            console.log('Changed pwm duty to: ' + self._pwmDuty);
        }

        if (rpm < 100) {
            lastRPMs = [];
        }
    });
}

// Inherit the EventEmitter - this line must be before prototype declarations
util.inherits(Lidar, events.EventEmitter);

Lidar.prototype._sendPwmCommand = function (name, value) {
    var self = this;
    glob(path.join(self._pwmPath, name), { cwd: '/' }, function(err, files) {
        if (err || files.length < 1) {
            console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. Could not resolve the pwm path. ' + err);
            return;
        }

        if (files.length > 1) {
            console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. PWM path resolved to more than one location. ' + err);
            return;
        }

        fs.writeFile(files[0], value, function(writeError) {
            if (err) {
                console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. ' + writeError);
            }
        });
    });
};

// turns on the lidar motor
Lidar.prototype.start = function () {
    var self = this;

    console.log('Lidar starting.');

    self._sendPwmCommand('period', '1000000');
    self._sendPwmCommand('duty', self._pwmDuty.toString());
    self._sendPwmCommand('run', '1');
};

// turns off the lidar motor
Lidar.prototype.stop = function () {
    var self = this;

    console.log('Lidar stopping.');

    self._sendPwmCommand('run', '0');
};

module.exports = Lidar;<commit_msg>Tweaking lidar rpm adjustment code.<commit_after>var util = require('util');
var events = require('events');
var SerialPort = require("serialport").SerialPort;
var fs = require('fs');
var path = require('path');
var glob = require('glob');

function LidarPacket(index, speed, dist1, dist2, dist3, dist4){
    this.index = index;
    this.speed = speed;
    this.dist1 = dist1;
    this.dist2 = dist2;
    this.dist3 = dist3;
    this.dist4 = dist4;
}

/**
 * Computes and returns the checksum as an int.
 * @param {Array} data - The data to compute the checksum for. Must be 20 bytes.
 * @return {Number} The checksum as an integer.
 */
var computeChecksum = function(data) {
    // group the data by word, little-endian
    var dataList = [];
    for (var i = 0; i < 10; i++){
        dataList.push(data[2*i] + (data[2*i+1]<<8));
    }

    // compute the checksum on 32 bits
    var chk32 = 0;
    dataList.forEach(function(item){
        chk32 = (chk32 << 1) + item;
    });

    // return a value wrapped around on 15 bits, and truncated to still fit into 15 bits
    var checksum = (chk32 & 0x7FFF) + (chk32 >> 15); // wrap around to fit into 15 bits
    checksum = checksum & 0x7FFF; // truncate to 15 bits
    return checksum;
};

/**
 * Gets the lidar distance reading from a 4 byte data section.
 * @param {Array} bytes - Buffer with 4 bytes of data.
 * @return {Number} The distance reading as an integer.
 */
var parsePacketDataSection = function(bytes) {
    // if the 8th bit of the byte 1 is set, the data is invalid; the 7th bit of byte 1 is a strength warning (varies with material sometimes) flag, but we are ignoring it for now
    if (bytes[1] & 128 === 128)
        return -1;

    // get distance from byte 0 and byte 1; byte 2 and byte 3 contain signal strength data, which we are ignoring for now
    return bytes[0] + ((bytes[1] & 63) << 8);
};

var parsePacket = function(bytes) {
    // first byte in the packet must be 0xFA
    if (bytes[0] != 0xFA)
        return null;

    // verify data via checksum
    if (computeChecksum(bytes.slice(0, 20)) !== (bytes[20] + (bytes[21] << 8)))
        return null;

    // packet index must be between 0xA0 and 0xF9
    var index = bytes[1];
    if (index < 0xA0 || index > 0xF9)
        return null;

    // adjust index to 0 based index
    index = index - 0xA0;

    // speed unit used by the LIDAR is in 64ths of an RPM; we want the speed in RPMs
    var speed = (bytes[2] + (bytes[3]<<8))/64.0;

    var distances = [];
    for (var i = 1; i < 5; i++)
        distances.push(parsePacketDataSection(bytes.slice(4*i, 4*i+4)));

    return new LidarPacket(index, speed, distances[0], distances[1], distances[2], distances[3]);
};

function Lidar(serialPath, pwmPath) {
    var self = this;
    self._pwmPath = pwmPath;
    self._pwmDuty = 380000;
    self._serialPort = new SerialPort(serialPath, {
        baudrate: 115200
    });

    self._serialPort.on("open", function () {
        console.log('serial port to lidar is open');

        var currentPacketData = [];
        self._serialPort.on('data', function(data) {
            console.log('data received from lidar: ' + data.toString('hex'));
            self.emit('raw data', data);

            for (var i = 0; i < data.length; i++) {
                var b = data[i];

                // if we're starting a new packet, but the current byte doesn't mark the beginning of the packet, skip it
                if (currentPacketData.length === 0 && b != 0xFA)
                    continue;

                currentPacketData.push(b);

                if (currentPacketData.length === 22) {
                    var packet = parsePacket(currentPacketData);
                    if (packet === null) {
                        // packet was invalid; check for 0xFA packet start marker in the last packet, as sometimes some bytes are dropped and the next packet is already started
                        var nextIndex = currentPacketData.slice(1).indexOf(0xFA) + 1;
                        if (nextIndex > 0)
                            currentPacketData = currentPacketData.slice(nextIndex);
                        else
                            currentPacketData = [];
                    }
                    else {
                        self.emit('packet', packet);

                        // reset currentPacketData
                        currentPacketData = [];
                    }
                }
            }
        });
    });

    var targetRPM = 240;
    var lastRPMs = [];
    var rpmAdjustInProgress = false;
    self.on('packet', function(packet) {

        if (rpm.index != 0){
            return;
        }

        var rpm = packet.speed;
        var prevRPM = 0;

        lastRPMs.push(rpm);
        if (lastRPMs.length >= 30){
            var prevRPM = lastRPMs.shift();
        }

        if (rpm > 100 && rpm > targetRPM - 5 && rpm < targetRPM + 5 && prevRPM > 0 && Math.abs(rpm - prevRPM) < 2 && !rpmAdjustInProgress) {
            var deltaRPM = targetRPM - rpm;
            var deltaDuty = Math.round((deltaRPM / rpm) * self._pwmDuty * 0.5);

            self._pwmDuty += deltaDuty;

            rpmAdjustInProgress = true;
            setTimeout(function(){ rpmAdjustInProgress = false; }, 3000);

            console.log('Changed pwm duty to: ' + self._pwmDuty);
        }

        if (rpm < 100) {
            lastRPMs = [];
        }
    });
}

// Inherit the EventEmitter - this line must be before prototype declarations
util.inherits(Lidar, events.EventEmitter);

Lidar.prototype._sendPwmCommand = function (name, value) {
    var self = this;
    glob(path.join(self._pwmPath, name), { cwd: '/' }, function(err, files) {
        if (err || files.length < 1) {
            console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. Could not resolve the pwm path. ' + err);
            return;
        }

        if (files.length > 1) {
            console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. PWM path resolved to more than one location. ' + err);
            return;
        }

        fs.writeFile(files[0], value, function(writeError) {
            if (err) {
                console.log('An error occurred sending pwm command \'' + name + '\' with value \'' + value + '\'. ' + writeError);
            }
        });
    });
};

// turns on the lidar motor
Lidar.prototype.start = function () {
    var self = this;

    console.log('Lidar starting.');

    self._sendPwmCommand('period', '1000000');
    self._sendPwmCommand('duty', self._pwmDuty.toString());
    self._sendPwmCommand('run', '1');
};

// turns off the lidar motor
Lidar.prototype.stop = function () {
    var self = this;

    console.log('Lidar stopping.');

    self._sendPwmCommand('run', '0');
};

module.exports = Lidar;<|endoftext|>


<commit_before>'use strict'

module.exports = createLinePlot

var createBuffer  = require('gl-buffer')
var createVAO     = require('gl-vao')
var createTexture = require('gl-texture2d')
var glslify       = require('glslify')
var unpackFloat   = require('glsl-read-float')
var bsearch       = require('binary-search-bounds')
var ndarray       = require('ndarray')

var createShader = glslify({
  vert: './shaders/vertex.glsl',
  frag: './shaders/fragment.glsl'
})

var createPickShader = glslify({
  vert: './shaders/vertex.glsl',
  frag: './shaders/pick.glsl'
})

var identity = [1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                0,0,0,1]

function distance(a, b) {
  var s = 0.0
  for(var i=0; i<3; ++i) {
    var d = a[i] - b[i]
    s += d*d
  }
  return Math.sqrt(s)
}

function filterClipBounds(bounds) {
  var result = [[-1e6,-1e6,-1e6], [1e6,1e6,1e6]]
  for(var i=0; i<3; ++i) {
    result[0][i] = Math.max(bounds[0][i], result[0][i])
    result[1][i] = Math.min(bounds[1][i], result[1][i])
  }
  return result
}

function PickResult(tau, position) {
  this.arcLength = tau
  this.position = position
}

function LinePlot(gl, shader, pickShader, buffer, vao, texture) {
  this.gl           = gl
  this.shader       = shader
  this.pickShader   = pickShader
  this.buffer       = buffer
  this.vao          = vao
  this.clipBounds   = [[-Infinity,-Infinity,-Infinity],
                       [ Infinity, Infinity, Infinity]]
  this.points       = []
  this.arcLength    = []
  this.vertexCount  = 0
  this.bounds       = [[0,0,0],[0,0,0]]
  this.pickId       = 0
  this.lineWidth    = 1
  this.texture      = texture
  this.dashScale    = 1
}

var proto = LinePlot.prototype

proto.draw = function(camera) {
  var gl      = this.gl
  var shader  = this.shader
  var vao     = this.vao
  shader.bind()
  shader.uniforms = {
    model:        camera.model      || identity,
    view:         camera.view       || identity,
    projection:   camera.projection || identity,
    clipBounds:   filterClipBounds(this.clipBounds),
    dashTexture:  this.texture.bind(),
    dashScale:    this.dashScale / this.arcLength[this.arcLength.length-1]
  }
  vao.bind()
  vao.draw(gl.LINES, this.vertexCount)
}

proto.drawPick = function(camera) {
  var gl      = this.gl
  var shader  = this.pickShader
  var vao     = this.vao
  shader.bind()
  shader.uniforms = {
    model:      camera.model      || identity,
    view:       camera.view       || identity,
    projection: camera.projection || identity,
    pickId:     this.pickId,
    clipBounds: filterClipBounds(this.clipBounds)
  }
  vao.bind()
  vao.draw(gl.LINES, this.vertexCount)
}

proto.update = function(options) {
  if("pickId" in options) {
    this.pickId = options.pickId
  }
  if("lineWidth" in options) {
    this.lineWidth = options.lineWidth
  }
  if("dashScale" in options) {
    this.dashScale = options.dashScale
  }

  var positions = options.position || options.positions
  if(!positions) {
    return
  }

  //Default color
  var colors = options.color || options.colors || [0,0,0]

  //Recalculate buffer data
  var buffer = []
  var arcLengthArray = []
  var pointArray = []
  var arcLength = 0.0
  var vertexCount = 0
  var bounds = [[ Infinity, Infinity, Infinity],
                [-Infinity,-Infinity,-Infinity]]
  for(var i=1; i<positions.length; ++i) {
    var a = positions[i-1]
    var b = positions[i]

    arcLengthArray.push(arcLength)
    pointArray.push(a.slice())

    for(var j=0; j<3; ++j) {
      bounds[0][j] = Math.min(bounds[0][j], a[j], b[j])
      bounds[1][j] = Math.max(bounds[1][j], a[j], b[j])
    }

    var acolor, bcolor
    if(Array.isArray(colors[0])) {
      acolor = colors[i-1]
      bcolor = colors[i]
    } else {
      acolor = bcolor = colors
    }

    var t0 = arcLength
    arcLength += distance(a, b)
    buffer.push(a[0], a[1], a[2], t0,        acolor[0], acolor[1], acolor[2],
                b[0], b[1], b[2], arcLength, bcolor[0], bcolor[1], bcolor[2])

    vertexCount += 2
  }
  this.buffer.update(buffer)

  arcLengthArray.push(arcLength)
  pointArray.push(positions[positions.length-1].slice())

  this.bounds = bounds

  this.vertexCount = vertexCount

  this.points = pointArray
  this.arcLength = arcLengthArray

  if('dashes' in options) {
    var dashArray = options.dashes

    //Calculate prefix sum
    var prefixSum = dashArray.slice()
    prefixSum.unshift(0)
    for(var i=1; i<prefixSum.length; ++i) {
      prefixSum[i] = prefixSum[i-1] + prefixSum[i]
    }

    var dashTexture = ndarray(new Array(256*4), [256, 1, 4])
    for(var i=0; i<256; ++i) {
      for(var j=0; j<4; ++j) {
        dashTexture.set(i,0,j, 0)
      }
      if(bsearch.le(prefixSum, prefixSum[prefixSum.length-1]*i/255.0) & 1) {
        dashTexture.set(i,0,0, 0)
      } else {
        dashTexture.set(i,0,0, 255)
      }
    }

    this.texture.setPixels(dashTexture)
  }
}

proto.dispose = function() {
  this.shader.dispose()
  this.vao.dispose()
  this.buffer.dispose()
}

proto.pick = function(selection) {
  if(!selection) {
    return null
  }
  if(selection.id !== this.pickId) {
    return null
  }
  var tau = unpackFloat(
    selection.value[0],
    selection.value[1],
    selection.value[2],
    0)
  var index = bsearch.le(this.arcLength, tau)
  if(index < 0) {
    return null
  }
  if(index === this.arcLength.length-1) {
    return new PickResult(
      this.arcLength[this.arcLength.length-1],
      this.points[this.points.length-1].slice())
  }
  var a = this.points[index]
  var b = this.points[index+1]
  var t = (tau - this.arcLength[index]) / (this.arcLength[index+1] - this.arcLength[index])
  var ti = 1.0 - t
  var x = [0,0,0]
  for(var i=0; i<3; ++i) {
    x[i] = ti * a[i] + t * b[i]
  }
  return new PickResult(tau, x)
}

function createLinePlot(gl, options) {
  var shader = createShader(gl)
  shader.attributes.position.location   = 0
  shader.attributes.arcLength.location  = 1
  shader.attributes.color.location      = 2

  var pickShader = createPickShader(gl)
  pickShader.attributes.position.location   = 0
  pickShader.attributes.arcLength.location  = 1
  pickShader.attributes.color.location      = 2

  var buffer = createBuffer(gl)
  var vao = createVAO(gl, [
      {
        'buffer': buffer,
        'size': 3,
        'offset': 0,
        'stride': 28
      },
      {
        'buffer': buffer,
        'size': 1,
        'offset': 12,
        'stride': 28
      },
      {
        'buffer': buffer,
        'size': 3,
        'offset': 16,
        'stride': 28
      }
    ])

  //Create texture for dash pattern
  var defaultTexture = ndarray(new Array(256*4), [256,1,4])
  for(var i=0; i<256*4; ++i) {
    defaultTexture.data[i] = 255
  }
  var texture = createTexture(gl, defaultTexture)
  texture.wrap = gl.REPEAT

  var linePlot = new LinePlot(gl, shader, pickShader, buffer, vao, texture)
  linePlot.update(options)
  return linePlot
}<commit_msg>fixed line width parameter<commit_after>'use strict'

module.exports = createLinePlot

var createBuffer  = require('gl-buffer')
var createVAO     = require('gl-vao')
var createTexture = require('gl-texture2d')
var glslify       = require('glslify')
var unpackFloat   = require('glsl-read-float')
var bsearch       = require('binary-search-bounds')
var ndarray       = require('ndarray')

var createShader = glslify({
  vert: './shaders/vertex.glsl',
  frag: './shaders/fragment.glsl'
})

var createPickShader = glslify({
  vert: './shaders/vertex.glsl',
  frag: './shaders/pick.glsl'
})

var identity = [1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                0,0,0,1]

function distance(a, b) {
  var s = 0.0
  for(var i=0; i<3; ++i) {
    var d = a[i] - b[i]
    s += d*d
  }
  return Math.sqrt(s)
}

function filterClipBounds(bounds) {
  var result = [[-1e6,-1e6,-1e6], [1e6,1e6,1e6]]
  for(var i=0; i<3; ++i) {
    result[0][i] = Math.max(bounds[0][i], result[0][i])
    result[1][i] = Math.min(bounds[1][i], result[1][i])
  }
  return result
}

function PickResult(tau, position) {
  this.arcLength = tau
  this.position = position
}

function LinePlot(gl, shader, pickShader, buffer, vao, texture) {
  this.gl           = gl
  this.shader       = shader
  this.pickShader   = pickShader
  this.buffer       = buffer
  this.vao          = vao
  this.clipBounds   = [[-Infinity,-Infinity,-Infinity],
                       [ Infinity, Infinity, Infinity]]
  this.points       = []
  this.arcLength    = []
  this.vertexCount  = 0
  this.bounds       = [[0,0,0],[0,0,0]]
  this.pickId       = 0
  this.lineWidth    = 1
  this.texture      = texture
  this.dashScale    = 1
}

var proto = LinePlot.prototype

proto.draw = function(camera) {
  var gl      = this.gl
  var shader  = this.shader
  var vao     = this.vao
  gl.lineWidth(this.lineWidth)
  shader.bind()
  shader.uniforms = {
    model:        camera.model      || identity,
    view:         camera.view       || identity,
    projection:   camera.projection || identity,
    clipBounds:   filterClipBounds(this.clipBounds),
    dashTexture:  this.texture.bind(),
    dashScale:    this.dashScale / this.arcLength[this.arcLength.length-1]
  }
  vao.bind()
  vao.draw(gl.LINES, this.vertexCount)
}

proto.drawPick = function(camera) {
  var gl      = this.gl
  var shader  = this.pickShader
  var vao     = this.vao
  gl.lineWidth(this.lineWidth)
  shader.bind()
  shader.uniforms = {
    model:      camera.model      || identity,
    view:       camera.view       || identity,
    projection: camera.projection || identity,
    pickId:     this.pickId,
    clipBounds: filterClipBounds(this.clipBounds)
  }
  vao.bind()
  vao.draw(gl.LINES, this.vertexCount)
}

proto.update = function(options) {
  if("pickId" in options) {
    this.pickId = options.pickId
  }
  if("lineWidth" in options) {
    this.lineWidth = options.lineWidth
  }
  if("dashScale" in options) {
    this.dashScale = options.dashScale
  }

  var positions = options.position || options.positions
  if(!positions) {
    return
  }

  //Default color
  var colors = options.color || options.colors || [0,0,0]

  //Recalculate buffer data
  var buffer = []
  var arcLengthArray = []
  var pointArray = []
  var arcLength = 0.0
  var vertexCount = 0
  var bounds = [[ Infinity, Infinity, Infinity],
                [-Infinity,-Infinity,-Infinity]]
  for(var i=1; i<positions.length; ++i) {
    var a = positions[i-1]
    var b = positions[i]

    arcLengthArray.push(arcLength)
    pointArray.push(a.slice())

    for(var j=0; j<3; ++j) {
      bounds[0][j] = Math.min(bounds[0][j], a[j], b[j])
      bounds[1][j] = Math.max(bounds[1][j], a[j], b[j])
    }

    var acolor, bcolor
    if(Array.isArray(colors[0])) {
      acolor = colors[i-1]
      bcolor = colors[i]
    } else {
      acolor = bcolor = colors
    }

    var t0 = arcLength
    arcLength += distance(a, b)
    buffer.push(a[0], a[1], a[2], t0,        acolor[0], acolor[1], acolor[2],
                b[0], b[1], b[2], arcLength, bcolor[0], bcolor[1], bcolor[2])

    vertexCount += 2
  }
  this.buffer.update(buffer)

  arcLengthArray.push(arcLength)
  pointArray.push(positions[positions.length-1].slice())

  this.bounds = bounds

  this.vertexCount = vertexCount

  this.points = pointArray
  this.arcLength = arcLengthArray

  if('dashes' in options) {
    var dashArray = options.dashes

    //Calculate prefix sum
    var prefixSum = dashArray.slice()
    prefixSum.unshift(0)
    for(var i=1; i<prefixSum.length; ++i) {
      prefixSum[i] = prefixSum[i-1] + prefixSum[i]
    }

    var dashTexture = ndarray(new Array(256*4), [256, 1, 4])
    for(var i=0; i<256; ++i) {
      for(var j=0; j<4; ++j) {
        dashTexture.set(i,0,j, 0)
      }
      if(bsearch.le(prefixSum, prefixSum[prefixSum.length-1]*i/255.0) & 1) {
        dashTexture.set(i,0,0, 0)
      } else {
        dashTexture.set(i,0,0, 255)
      }
    }

    this.texture.setPixels(dashTexture)
  }
}

proto.dispose = function() {
  this.shader.dispose()
  this.vao.dispose()
  this.buffer.dispose()
}

proto.pick = function(selection) {
  if(!selection) {
    return null
  }
  if(selection.id !== this.pickId) {
    return null
  }
  var tau = unpackFloat(
    selection.value[0],
    selection.value[1],
    selection.value[2],
    0)
  var index = bsearch.le(this.arcLength, tau)
  if(index < 0) {
    return null
  }
  if(index === this.arcLength.length-1) {
    return new PickResult(
      this.arcLength[this.arcLength.length-1],
      this.points[this.points.length-1].slice())
  }
  var a = this.points[index]
  var b = this.points[index+1]
  var t = (tau - this.arcLength[index]) / (this.arcLength[index+1] - this.arcLength[index])
  var ti = 1.0 - t
  var x = [0,0,0]
  for(var i=0; i<3; ++i) {
    x[i] = ti * a[i] + t * b[i]
  }
  return new PickResult(tau, x)
}

function createLinePlot(gl, options) {
  var shader = createShader(gl)
  shader.attributes.position.location   = 0
  shader.attributes.arcLength.location  = 1
  shader.attributes.color.location      = 2

  var pickShader = createPickShader(gl)
  pickShader.attributes.position.location   = 0
  pickShader.attributes.arcLength.location  = 1
  pickShader.attributes.color.location      = 2

  var buffer = createBuffer(gl)
  var vao = createVAO(gl, [
      {
        'buffer': buffer,
        'size': 3,
        'offset': 0,
        'stride': 28
      },
      {
        'buffer': buffer,
        'size': 1,
        'offset': 12,
        'stride': 28
      },
      {
        'buffer': buffer,
        'size': 3,
        'offset': 16,
        'stride': 28
      }
    ])

  //Create texture for dash pattern
  var defaultTexture = ndarray(new Array(256*4), [256,1,4])
  for(var i=0; i<256*4; ++i) {
    defaultTexture.data[i] = 255
  }
  var texture = createTexture(gl, defaultTexture)
  texture.wrap = gl.REPEAT

  var linePlot = new LinePlot(gl, shader, pickShader, buffer, vao, texture)
  linePlot.update(options)
  return linePlot
}<|endoftext|>


<commit_before>/*
INSTRUCTIONS
Copy/paste this whole section into console & hit enter. It should track gather rates (%) for Gathering, Fishing,
Mining and Woodcutting. It will print the results after every gather; please note that this will never give
exact values, and you should probably wait for 300-500+ attempts on a tile to draw conclusions.

To reset the values (necessary if you move tiles) you can copy/paste the whole script again or just enter
this into the console:  "clearTSResults();".

This *should* be compatible with Ryalane's script if Ryalane's script loads first.
TODO:
	total amount of resources gained
	taxes
//*/
// preferences; data is still tracked, this only affects output
var outputToConsole = false;
var outputTiers = true;
var outputQuests = true;
var outputScouting = false;
var saveLogText = false;

var logText = [];
var questItemRegex = /(\d+) \/ (\d+)/;
var resourceListId = 'resourceLogList';
// results aren't stored under the resource because we aren't tracking tile changes/types
var tsResults = {
	actions: 0,
	items: 0,
	regularItems: 0,
	scoutingItems: 0,
	questActive: false,
	questActions: 0,
	questItems: 0,
	1: 0,
	2: 0,
	3: 0,
	4: 0,
	5: 0,
	lumber:{
		items: ['Pine', 'Oak', 'Maple', 'Ironwood', 'Yggdrasil']
	},
	ore: {
		items: ['Iron', 'Silver', 'Obsidian', 'Mythril', 'Ethernium']
	},
	plant: {
		items: ['Plant Stem', 'Cotton', 'Living Leather', 'Silver Vine', 'Nimbus Fruit']
	},
	fish: {
		items: ['Tuna', 'Salmon', 'Flyfish', 'Marlin', 'Dragonfish']
	}
};
function clearTSResults() {
	tsResults.actions = 0;
	tsResults.items = 0;
	tsResults.regularItems = 0;
	tsResults.scoutingItems = 0;
	tsResults.questActive = false;
	tsResults.questActions = 0;
	tsResults.questItems = 0;

	for(var i=1, iLen=6; i<iLen;i++) {
		tsResults[i] = 0;
	}
}
function handleQuestItem(msg) {
	var matches = msg.match(questItemRegex);
	var current = parseInt(matches[1]);
	var total = parseInt(matches[2]);

	if(current == total) {
		// this message is sent after the channel2 message, so we won't lose an action here
		tsResults.questActive = false;
	} else {
		tsResults.questActive = true;
		tsResults.questItems += 1;
	}
}
function parseTSLog(datum) {
	var arr = datum.split('|');
	if (arr[0] != 'NLOG') {return;}

	var channel = arr[1];
	var msg = arr[2];

	// save all lines of text if requested
	if(saveLogText) {
		logText.push(msg);
	}

	// track relic, item, gem, gold drops
	if(channel == 3) {
		// track quest drops separately
		if(msg.indexOf('quest') > 0) return handleQuestItem(msg);
		// scouting vs normal
		if(msg.indexOf('*') > -1) tsResults.scoutingItems += 1;
		else tsResults.regularItems += 1;

		// track the total in addition to ts/scout
		tsResults.items += 1;
		return;
	} else if(channel == 2) {
		// skip level up message before counting the action
		if(msg.indexOf('gained a new tradeskill level') > -1) return;

		tsResults.actions += 1;
		if(tsResults.questActive) tsResults.questActions += 1;

		// which array should be used?
		var itemTypes = null;
		if(msg.indexOf('You cut') >= 0) {
			itemTypes = tsResults.lumber.items;
		} else if(msg.indexOf('You mined') >= 0) {
			itemTypes = tsResults.ore.items;
		} else if(msg.indexOf('You gathered') >= 0) {
			itemTypes = tsResults.plant.items;
		} else if(msg.indexOf('You caught') >= 0) {
			itemTypes = tsResults.fish.items;
		} else {
			itemTypes = [];
		}

		for(var i=0,iLen=itemTypes.length;i<iLen;i++) {
			if(msg.indexOf(itemTypes[i]) >= 0) {
				tsResults[i+1] += 1;
			}
		}

		updateOutput(tsResults, msg);
	}
}
function updateOutput(results, msg) {
	var outputArgs = [];
	if(outputTiers) {
		outputArgs = outputArgs.concat([
			't1:', (100*tsResults[1]/tsResults.actions).toFixed(2),
			't2:', (100*tsResults[2]/tsResults.actions).toFixed(2),
			't3:', (100*tsResults[3]/tsResults.actions).toFixed(2),
			't4:', (100*tsResults[4]/tsResults.actions).toFixed(2),
			't5:', (100*tsResults[5]/tsResults.actions).toFixed(2)]);
	}
	outputArgs = outputArgs.concat(['item:', (100*tsResults.items/tsResults.actions).toFixed(2)]);
	if(outputScouting)
		outputArgs = outputArgs.concat([
			'regularItem:', (100*tsResults.regularItems/tsResults.actions).toFixed(2),
			'scoutItem:', (100*tsResults.scoutingItems/tsResults.actions).toFixed(2)]);
	if(outputQuests)
		outputArgs = outputArgs.concat(['quest:', (100*tsResults.questItems/tsResults.questActions).toFixed(2)]);

	outputArgs = outputArgs.concat([
		'actions:', tsResults.actions,
		'\tmsg:', msg]);

	if(outputToConsole) console.info.apply(console, outputArgs);

	// skip posting the message to the UI
	outputArgs.pop();
	outputArgs.pop();
	updateUI(outputArgs);
}
function formatResource(label, value) {
	return '

' + label + ' ' + value + '

';
}
function updateUI(outputArgs) {
	$('#'+resourceListId).empty();
	for(var i=0,iLen=outputArgs.length;i<iLen;i+=2) {
		$('#'+resourceListId).append(formatResource(outputArgs[i], outputArgs[i+1]));
	}
}
function initializeUI() {
	jQuery('
', {
	    id: 'resourceLogContainer',
	    text: 'Resource Log',
	    style: 'float: right;'
	}).prependTo('body');
	jQuery('
', {
	    id: resourceListId,
	}).appendTo('div#resourceLogContainer');
}
function track_resources_onmsg(evt) {
	track_resources_original_msg(evt);
	parseTSLog(evt.data);
}
// set up handler & hook original game handler in
if(typeof track_resources_original_msg === 'undefined') {
	console.info('resource tracker not yet loaded, loading...');
	var track_resources_original_msg = ws.onmessage;
	ws.onmessage=track_resources_onmsg;
	initializeUI();
} else {
	ws.onmessage=track_resources_onmsg;
}
<commit_msg>track tax occurrences<commit_after>/*
INSTRUCTIONS
Copy/paste this whole section into console & hit enter. It should track gather rates (%) for Gathering, Fishing,
Mining and Woodcutting. It will print the results after every gather; please note that this will never give
exact values, and you should probably wait for 300-500+ attempts on a tile to draw conclusions.

To reset the values (necessary if you move tiles) you can copy/paste the whole script again or just enter
this into the console:  "clearTSResults();".

This *should* be compatible with Ryalane's script if Ryalane's script loads first.
TODO:
	total amount of resources gained
	taxes
//*/
// preferences; data is still tracked, this only affects output
var outputTaxes = true;
var outputTiers = true;
var outputQuests = true;
var outputScouting = true;
var outputToConsole = false;
var saveLogText = false;

var logText = [];
var questItemRegex = /(\d+) \/ (\d+)/;
var resourceListId = 'resourceLogList';
// results aren't stored under the resource because we aren't tracking tile changes/types
var tsResults = {
	actions: 0,
	taxedActions: 0,
	items: 0,
	regularItems: 0,
	scoutingItems: 0,
	questActive: false,
	questActions: 0,
	questItems: 0,
	1: 0,
	2: 0,
	3: 0,
	4: 0,
	5: 0,
	lumber:{
		items: ['Pine', 'Oak', 'Maple', 'Ironwood', 'Yggdrasil']
	},
	ore: {
		items: ['Iron', 'Silver', 'Obsidian', 'Mythril', 'Ethernium']
	},
	plant: {
		items: ['Plant Stem', 'Cotton', 'Living Leather', 'Silver Vine', 'Nimbus Fruit']
	},
	fish: {
		items: ['Tuna', 'Salmon', 'Flyfish', 'Marlin', 'Dragonfish']
	}
};
function clearTSResults() {
	tsResults.actions = 0;
	tsResults.items = 0;
	tsResults.regularItems = 0;
	tsResults.scoutingItems = 0;
	tsResults.questActive = false;
	tsResults.questActions = 0;
	tsResults.questItems = 0;

	for(var i=1, iLen=6; i<iLen;i++) {
		tsResults[i] = 0;
	}
}
function handleQuestItem(msg) {
	var matches = msg.match(questItemRegex);
	var current = parseInt(matches[1]);
	var total = parseInt(matches[2]);

	if(current == total) {
		// this message is sent after the channel2 message, so we won't lose an action here
		tsResults.questActive = false;
	} else {
		tsResults.questActive = true;
		tsResults.questItems += 1;
	}
}
function parseTSLog(datum) {
	var arr = datum.split('|');
	if (arr[0] != 'NLOG') {return;}

	var channel = arr[1];
	var msg = arr[2];

	// save all lines of text if requested
	if(saveLogText) {
		logText.push(msg);
	}

	// track relic, item, gem, gold drops
	if(channel == 3) {
		// track quest drops separately
		if(msg.indexOf('quest') > 0) return handleQuestItem(msg);
		// scouting vs normal
		if(msg.indexOf('*') > -1) tsResults.scoutingItems += 1;
		else tsResults.regularItems += 1;

		// track the total in addition to ts/scout
		tsResults.items += 1;
		return;
	} else if(channel == 2) {
		// skip level up message before counting the action
		if(msg.indexOf('gained a new tradeskill level') > -1) return;

		tsResults.actions += 1;
		if(tsResults.questActive) tsResults.questActions += 1;
		if(msg.indexOf('to taxes') > -1) tsResults.taxedActions += 1;

		// which array should be used?
		var itemTypes = null;
		if(msg.indexOf('You cut') >= 0) {
			itemTypes = tsResults.lumber.items;
		} else if(msg.indexOf('You mined') >= 0) {
			itemTypes = tsResults.ore.items;
		} else if(msg.indexOf('You gathered') >= 0) {
			itemTypes = tsResults.plant.items;
		} else if(msg.indexOf('You caught') >= 0) {
			itemTypes = tsResults.fish.items;
		} else {
			itemTypes = [];
		}

		for(var i=0,iLen=itemTypes.length;i<iLen;i++) {
			if(msg.indexOf(itemTypes[i]) >= 0) {
				tsResults[i+1] += 1;
			}
		}

		updateOutput(tsResults, msg);
	}
}
function updateOutput(results, msg) {
	var outputArgs = [];
	if(outputTiers) {
		outputArgs = outputArgs.concat([
			't1:', (100*tsResults[1]/tsResults.actions).toFixed(2),
			't2:', (100*tsResults[2]/tsResults.actions).toFixed(2),
			't3:', (100*tsResults[3]/tsResults.actions).toFixed(2),
			't4:', (100*tsResults[4]/tsResults.actions).toFixed(2),
			't5:', (100*tsResults[5]/tsResults.actions).toFixed(2)]);
	}
	outputArgs = outputArgs.concat(['item:', (100*tsResults.items/tsResults.actions).toFixed(2)]);
	if(outputScouting)
		outputArgs = outputArgs.concat([
			'regularItem:', (100*tsResults.regularItems/tsResults.actions).toFixed(2),
			'scoutItem:', (100*tsResults.scoutingItems/tsResults.actions).toFixed(2)]);
	if(outputQuests)
		outputArgs = outputArgs.concat(['quest:', (100*tsResults.questItems/tsResults.questActions).toFixed(2)]);
	if(outputTaxes)
		outputArgs = outputArgs.concat(['tax:', (100*tsResults.taxedActions/tsResults.actions).toFixed(2)]);

	outputArgs = outputArgs.concat([
		'actions:', tsResults.actions,
		'\tmsg:', msg]);

	if(outputToConsole) console.info.apply(console, outputArgs);

	// skip posting the message to the UI
	outputArgs.pop();
	outputArgs.pop();
	updateUI(outputArgs);
}
function formatResource(label, value) {
	return '

' + label + ' ' + value + '

';
}
function updateUI(outputArgs) {
	$('#'+resourceListId).empty();
	for(var i=0,iLen=outputArgs.length;i<iLen;i+=2) {
		$('#'+resourceListId).append(formatResource(outputArgs[i], outputArgs[i+1]));
	}
}
function initializeUI() {
	jQuery('
', {
	    id: 'resourceLogContainer',
	    text: 'Resource Log',
	    style: 'float: right;'
	}).prependTo('body');
	jQuery('
', {
	    id: resourceListId,
	}).appendTo('div#resourceLogContainer');
}
function track_resources_onmsg(evt) {
	track_resources_original_msg(evt);
	parseTSLog(evt.data);
}
// set up handler & hook original game handler in
if(typeof track_resources_original_msg === 'undefined') {
	console.info('resource tracker not yet loaded, loading...');
	var track_resources_original_msg = ws.onmessage;
	ws.onmessage=track_resources_onmsg;
	initializeUI();
} else {
	ws.onmessage=track_resources_onmsg;
}
<|endoftext|>


<commit_before>//Construction logic goes there
var mod = {};
module.exports = mod;

function randomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
}

mod.saveStructure = function(room, pos, type) {
    if(pos) {
        const entry = room.layout[type];
        if(entry) {
            entry.push(pos);
        }
    }
};

//** Dissi Flower **
// 60 extensions, 6 towers and 1 link
const dissiFlower = {
    vector: [
        [2,1,1,0,0,0,0,0,1,1,2],
        [1,0,1,1,0,0,0,1,1,0,1],
        [1,1,0,1,1,0,1,1,0,1,1],
        [0,1,1,0,1,0,1,0,1,1,0],
        [0,0,1,1,0,3,0,1,1,0,0],
        [0,0,0,1,2,0,2,1,0,0,0],
        [0,0,1,1,0,1,0,1,1,0,0],
        [0,1,1,0,1,1,1,0,1,1,0],
        [1,1,0,1,1,0,1,1,0,1,1],
        [1,0,1,1,0,0,0,1,1,0,1],
        [2,1,1,0,0,0,0,0,1,1,2]
    ],
    extension: 1,
    tower: 2,
    link: 3
};

mod.showRoomPlan = function(room) {
    for(const type in room.layout) {
        if(type === 'init') continue; //skip init flag
        const entry = room.layout[type];
        for(const pos of entry) {
            const x = pos[0];
            const y = pos[1];
            room.visual.structure(x, y, type);
        }
    }
};

function wrap2DArray(array) {
    return function(x, y) {
        return array[y][x];
    };
}

//Calculate a layout for the room and save to memory
//FIXME Structures may overlapped
mod.init = function(room) {
    delete room.layout; //re init
    const terrain = room.getTerrain();
    //== Container ==
    //Firstly, we need a container for upgrader so it won't move
    this.saveStructure(room, posNearby(terrain, room.controller, 2), STRUCTURE_CONTAINER);
    //Then we need a container per source
    for(let source of room.sources) {
        this.saveStructure(room, posNearby(terrain, source, 1), STRUCTURE_CONTAINER);
    }
    //== Extension ==
    //Dissi flower here!
    const xlen = dissiFlower.vector[0].length;
    const ylen = dissiFlower.vector.length;
    const blockStartPos = Util.Helper.findBlockInRoom(room.name, xlen, ylen);
    if(blockStartPos) {
        //We can build dissi flower!
        const arrayWraper = wrap2DArray(dissiFlower.vector);
        for(let x=0; x<xlen; x++) {
            for(let y=0; y<ylen; y++) {
                const actualX = blockStartPos[0] + x;
                const actualY = blockStartPos[1] + y;
                switch(arrayWraper(x,y)) {
                case dissiFlower.extension:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_EXTENSION);
                    break;
                case dissiFlower.link:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_LINK);
                    break;
                case dissiFlower.tower:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_TOWER);
                    break;
                }
            }
        }
    } else {
        console.log('Failed to find start pos for dissi flower!');
        //TODO some fallback layout for extensions
    }
    //== Storage ==
    this.saveStructure(room, posNearby(terrain, room.spawns[0], 4), STRUCTURE_STORAGE);
    //== Road ==
    this.initRoad(room, room.sources);
    //== Link ==
    if(blockStartPos) {
        //We have dissi flower
        // so we don't need to place spawnLink
    } else {
        //A output link to match input link
        this.saveStructure(room, posNearby(terrain, room.spawns[0], 3), STRUCTURE_LINK);
    }
    //Source links first, it is input link
    for(let source of room.sources) {
        this.saveStructure(room, posNearby(terrain, source, 2), STRUCTURE_LINK);
    }
    //Controller link is also a output link
    this.saveStructure(room, posNearby(terrain, room.controller, 3), STRUCTURE_LINK);
    //TODO 2 links for remote mining or mineral mining
    //====== Wait to be implement ======
    //=== Defense ===
    //== Wall & Rampart ==
    //=== Technology ===
    //== Lab ==
    //== Extractor ==
    //== Spawn ==
    //=== Outside world ===
    //== Nuker ==
    //== PowerSpawn ==
    //== Observer ==
    //== Terminal ==

    //Set init flag
    room.layout.init = true;
};

mod.loop = function(room, forceRun=false) {
    //Do not init twice
    if(!room.layout.init) this.init(room);
    //Show room plan if ask
    const show = room.memory.showRoomPlan;
    if(show) this.showRoomPlan(room);
    //Do not loop room.layout every tick
    const lastConstruct = room.memory.lastConstruct || 0;
    if(!forceRun && (Game.time-lastConstruct) < 40) return;

    //one site at one time
    const sites = room.cachedFind(FIND_CONSTRUCTION_SITES);
    const noSite = sites.length === 0;
    if(!noSite) {
        // actually not loop room.layout
        //  so we don not update lastConstruct here
        return;
    }

    console.log(`Loop construction of room ${room.name}`);
    room.memory.lastConstruct = Game.time;

    //== Common iterate function ==
    let success = false;
    const iterateAndPlace = function(type) {
        for(const pos of room.layout[type]) {
            //Iterate
            const x = pos[0];
            const y = pos[1];
            const arr = room.lookForAt(LOOK_STRUCTURES, x, y);
            if(arr.length > 0) {
                //There is a structure on the pos
                // TODO check if type is equal and do properly rebuild
                continue;
            } else {
                room.createConstructionSite(x, y, type);
                return true;
            }
        }
        return false;
    };

    //=== Container ===
    success = iterateAndPlace(STRUCTURE_CONTAINER);
    if(success) return;
    //=== Extension ===
    //Build extension if we can
    const extensionLimit = CONTROLLER_STRUCTURES[STRUCTURE_EXTENSION][room.controller.level];
    var extensions = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_EXTENSION;
        }
    });
    if(extensions.length<extensionLimit) {
        success = iterateAndPlace(STRUCTURE_EXTENSION);
    }
    if(success) return;
    //== Tower ==
    //Build tower if we can
    const towerLimit = CONTROLLER_STRUCTURES[STRUCTURE_TOWER][room.controller.level];
    var towers = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_TOWER;
        }
    });
    if(towers.length<towerLimit) {
        success = iterateAndPlace(STRUCTURE_TOWER);
    }
    if(success) return;
    //== Storage ==
    if(!room.storage) {
        success = iterateAndPlace(STRUCTURE_STORAGE);
    }
    if(success) return;
    //== Link ==
    const linkLimit = CONTROLLER_STRUCTURES[STRUCTURE_LINK][room.controller.level];
    var links = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_LINK;
        }
    });
    if(links.length<linkLimit) {
        success = iterateAndPlace(STRUCTURE_LINK);
    }
    if(success) return;
    //== Road ==
    iterateAndPlace(STRUCTURE_ROAD);
};

mod.initRoad = function(room, sources) {
    const self = this;
    const spawns = room.spawns;
    _.forEach(spawns, spawn => {
        //Spawn to Source
        _.forEach(sources, source => self.buildRoad(room, spawn.pos, source.pos));
        //Spawn to Controller
        self.buildRoad(room, spawn.pos, room.controller.pos);
    });
    //Spawn to Exit
    //const exitDir = creep.room.findExitTo(anotherCreep.room);
    //const exit = creep.pos.findClosestByRange(exitDir);
    //creep.moveTo(exit);
};

mod.placeSiteAt = function(room, x, y, structureType) {
    const terrain = new Room.Terrain(room.name);
    if(0 === terrain.get(xi, yi)) {
        //Plain Terrain is Best!
        room.createConstructionSite(xi, yi, structureType);
        return true;
    } else {
        return false;
    }
};

function posNearby(terrain, obj, range) {
    const y = obj.pos.y;
    const x = obj.pos.x;
    let swamps = [];
    for(let xi=x-range; xi<=x+range; xi++) {
        for(let yi=y-range; yi<=y+range; yi++) {
            //traverse fields nearby
            if(xi===x && yi===y) continue;
            if(0 === terrain.get(xi, yi)) {
                //Plain Terrain is Best!
                return [xi, yi];
            }
            if(TERRAIN_MASK_SWAMP === terrain.get(xi, yi)) {
                //Store pos and continue finding
                swamps.push({x: xi, y: yi});
            }
        }
    }
    if(swamps.length >= 1) {
        //So we must choose from swamps since there is no plain terrain as alternative
        return [swamps[0].x, swamps[0].y];
    }
    return false;
};

mod.showRoadPath = function(room, from, to) {
    const path = room.findPath(from, to);
    const points = _.map(path, o => [o.x, o.y]);
    new RoomVisual(room.name).poly(points, {
        fill: 'transparent',
        stroke: '#fff',
        lineStyle: 'dashed',
        strokeWidth: .15,
        opacity: .1
    });
};
mod.buildRoad = function(room, from, to) {
    const path = room.findPath(from, to);
    //Only build road between from and to, so we remove these two pos from path
    path.pop();
    path.shift();
    const self = this;
    _.forEach(path, o => self.saveStructure(room, [o.x, o.y], STRUCTURE_ROAD));
};

//Layout extension
<commit_msg>[bugfix] delete interName and memory of room.layout<commit_after>//Construction logic goes there
var mod = {};
module.exports = mod;

function randomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
}

mod.saveStructure = function(room, pos, type) {
    if(pos) {
        const entry = room.layout[type];
        if(entry) {
            entry.push(pos);
        }
    }
};

//** Dissi Flower **
// 60 extensions, 6 towers and 1 link
const dissiFlower = {
    vector: [
        [2,1,1,0,0,0,0,0,1,1,2],
        [1,0,1,1,0,0,0,1,1,0,1],
        [1,1,0,1,1,0,1,1,0,1,1],
        [0,1,1,0,1,0,1,0,1,1,0],
        [0,0,1,1,0,3,0,1,1,0,0],
        [0,0,0,1,2,0,2,1,0,0,0],
        [0,0,1,1,0,1,0,1,1,0,0],
        [0,1,1,0,1,1,1,0,1,1,0],
        [1,1,0,1,1,0,1,1,0,1,1],
        [1,0,1,1,0,0,0,1,1,0,1],
        [2,1,1,0,0,0,0,0,1,1,2]
    ],
    extension: 1,
    tower: 2,
    link: 3
};

mod.showRoomPlan = function(room) {
    for(const type in room.layout) {
        if(type === 'init') continue; //skip init flag
        const entry = room.layout[type];
        for(const pos of entry) {
            const x = pos[0];
            const y = pos[1];
            room.visual.structure(x, y, type);
        }
    }
};

function wrap2DArray(array) {
    return function(x, y) {
        return array[y][x];
    };
}

//Calculate a layout for the room and save to memory
//FIXME Structures may overlapped
mod.init = function(room) {
    delete room._layout; //re init
    delete room.memory.layout; //re init
    const terrain = room.getTerrain();
    //== Container ==
    //Firstly, we need a container for upgrader so it won't move
    this.saveStructure(room, posNearby(terrain, room.controller, 2), STRUCTURE_CONTAINER);
    //Then we need a container per source
    for(let source of room.sources) {
        this.saveStructure(room, posNearby(terrain, source, 1), STRUCTURE_CONTAINER);
    }
    //== Extension ==
    //Dissi flower here!
    const xlen = dissiFlower.vector[0].length;
    const ylen = dissiFlower.vector.length;
    const blockStartPos = Util.Helper.findBlockInRoom(room.name, xlen, ylen);
    if(blockStartPos) {
        //We can build dissi flower!
        const arrayWraper = wrap2DArray(dissiFlower.vector);
        for(let x=0; x<xlen; x++) {
            for(let y=0; y<ylen; y++) {
                const actualX = blockStartPos[0] + x;
                const actualY = blockStartPos[1] + y;
                switch(arrayWraper(x,y)) {
                case dissiFlower.extension:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_EXTENSION);
                    break;
                case dissiFlower.link:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_LINK);
                    break;
                case dissiFlower.tower:
                    this.saveStructure(room, [actualX,actualY], STRUCTURE_TOWER);
                    break;
                }
            }
        }
    } else {
        console.log('Failed to find start pos for dissi flower!');
        //TODO some fallback layout for extensions
    }
    //== Storage ==
    this.saveStructure(room, posNearby(terrain, room.spawns[0], 4), STRUCTURE_STORAGE);
    //== Road ==
    this.initRoad(room, room.sources);
    //== Link ==
    if(blockStartPos) {
        //We have dissi flower
        // so we don't need to place spawnLink
    } else {
        //A output link to match input link
        this.saveStructure(room, posNearby(terrain, room.spawns[0], 3), STRUCTURE_LINK);
    }
    //Source links first, it is input link
    for(let source of room.sources) {
        this.saveStructure(room, posNearby(terrain, source, 2), STRUCTURE_LINK);
    }
    //Controller link is also a output link
    this.saveStructure(room, posNearby(terrain, room.controller, 3), STRUCTURE_LINK);
    //TODO 2 links for remote mining or mineral mining
    //====== Wait to be implement ======
    //=== Defense ===
    //== Wall & Rampart ==
    //=== Technology ===
    //== Lab ==
    //== Extractor ==
    //== Spawn ==
    //=== Outside world ===
    //== Nuker ==
    //== PowerSpawn ==
    //== Observer ==
    //== Terminal ==

    //Set init flag
    room.layout.init = true;
};

mod.loop = function(room, forceRun=false) {
    //Do not init twice
    if(!room.layout.init) this.init(room);
    //Show room plan if ask
    const show = room.memory.showRoomPlan;
    if(show) this.showRoomPlan(room);
    //Do not loop room.layout every tick
    const lastConstruct = room.memory.lastConstruct || 0;
    if(!forceRun && (Game.time-lastConstruct) < 40) return;

    //one site at one time
    const sites = room.cachedFind(FIND_CONSTRUCTION_SITES);
    const noSite = sites.length === 0;
    if(!noSite) {
        // actually not loop room.layout
        //  so we don not update lastConstruct here
        return;
    }

    console.log(`Loop construction of room ${room.name}`);
    room.memory.lastConstruct = Game.time;

    //== Common iterate function ==
    let success = false;
    const iterateAndPlace = function(type) {
        for(const pos of room.layout[type]) {
            //Iterate
            const x = pos[0];
            const y = pos[1];
            const arr = room.lookForAt(LOOK_STRUCTURES, x, y);
            if(arr.length > 0) {
                //There is a structure on the pos
                // TODO check if type is equal and do properly rebuild
                continue;
            } else {
                room.createConstructionSite(x, y, type);
                return true;
            }
        }
        return false;
    };

    //=== Container ===
    success = iterateAndPlace(STRUCTURE_CONTAINER);
    if(success) return;
    //=== Extension ===
    //Build extension if we can
    const extensionLimit = CONTROLLER_STRUCTURES[STRUCTURE_EXTENSION][room.controller.level];
    var extensions = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_EXTENSION;
        }
    });
    if(extensions.length<extensionLimit) {
        success = iterateAndPlace(STRUCTURE_EXTENSION);
    }
    if(success) return;
    //== Tower ==
    //Build tower if we can
    const towerLimit = CONTROLLER_STRUCTURES[STRUCTURE_TOWER][room.controller.level];
    var towers = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_TOWER;
        }
    });
    if(towers.length<towerLimit) {
        success = iterateAndPlace(STRUCTURE_TOWER);
    }
    if(success) return;
    //== Storage ==
    if(!room.storage) {
        success = iterateAndPlace(STRUCTURE_STORAGE);
    }
    if(success) return;
    //== Link ==
    const linkLimit = CONTROLLER_STRUCTURES[STRUCTURE_LINK][room.controller.level];
    var links = room.find(FIND_MY_STRUCTURES, {
        filter: (structure) => {
            return structure.structureType == STRUCTURE_LINK;
        }
    });
    if(links.length<linkLimit) {
        success = iterateAndPlace(STRUCTURE_LINK);
    }
    if(success) return;
    //== Road ==
    iterateAndPlace(STRUCTURE_ROAD);
};

mod.initRoad = function(room, sources) {
    const self = this;
    const spawns = room.spawns;
    _.forEach(spawns, spawn => {
        //Spawn to Source
        _.forEach(sources, source => self.buildRoad(room, spawn.pos, source.pos));
        //Spawn to Controller
        self.buildRoad(room, spawn.pos, room.controller.pos);
    });
    //Spawn to Exit
    //const exitDir = creep.room.findExitTo(anotherCreep.room);
    //const exit = creep.pos.findClosestByRange(exitDir);
    //creep.moveTo(exit);
};

mod.placeSiteAt = function(room, x, y, structureType) {
    const terrain = new Room.Terrain(room.name);
    if(0 === terrain.get(xi, yi)) {
        //Plain Terrain is Best!
        room.createConstructionSite(xi, yi, structureType);
        return true;
    } else {
        return false;
    }
};

function posNearby(terrain, obj, range) {
    const y = obj.pos.y;
    const x = obj.pos.x;
    let swamps = [];
    for(let xi=x-range; xi<=x+range; xi++) {
        for(let yi=y-range; yi<=y+range; yi++) {
            //traverse fields nearby
            if(xi===x && yi===y) continue;
            if(0 === terrain.get(xi, yi)) {
                //Plain Terrain is Best!
                return [xi, yi];
            }
            if(TERRAIN_MASK_SWAMP === terrain.get(xi, yi)) {
                //Store pos and continue finding
                swamps.push({x: xi, y: yi});
            }
        }
    }
    if(swamps.length >= 1) {
        //So we must choose from swamps since there is no plain terrain as alternative
        return [swamps[0].x, swamps[0].y];
    }
    return false;
};

mod.showRoadPath = function(room, from, to) {
    const path = room.findPath(from, to);
    const points = _.map(path, o => [o.x, o.y]);
    new RoomVisual(room.name).poly(points, {
        fill: 'transparent',
        stroke: '#fff',
        lineStyle: 'dashed',
        strokeWidth: .15,
        opacity: .1
    });
};
mod.buildRoad = function(room, from, to) {
    const path = room.findPath(from, to);
    //Only build road between from and to, so we remove these two pos from path
    path.pop();
    path.shift();
    const self = this;
    _.forEach(path, o => self.saveStructure(room, [o.x, o.y], STRUCTURE_ROAD));
};

//Layout extension
<|endoftext|>