| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- function readAll(cb) {
- require("fs").readFile('./input', 'utf8', (err, data) => {
- var world = new World(),
- j =0;
- data.split("\n").forEach(l => {
- if (l.length) {
- var line = [];
- for (var i =0, len = l.length; i < len; ++i) {
- var c = l.charAt(i);
- line.push(c == '#' ? '#' : '.');
- if ("#.".indexOf(c) == -1) {
- var u = new Unit(c, i, j);
- world.units.push(u);
- }
- }
- world.area.push(line);
- ++j;
- }
- });
- cb(world);
- });
- }
- function World() {
- this.area = [];
- this.units = [];
- this.age = -1;
- }
- World.prototype.sortUnits = function() {
- this.units.sort((a, b) => (a.py == b.py ? a.px -b.px : a.py -b.py));
- }
- World.prototype.step = function() {
- this.sortUnits();
- this.units.forEach(u => u.hp ? u.step(this) : null);
- ++this.age;
- }
- World.prototype.isEmpty = function(px, py) {
- if (this.area[py][px] == '#')
- return false;
- return !this.units.some(u => u.hp && u.px == px && u.py == py);
- }
- World.prototype.coordExists = function(px, py) {
- return px >= 0 && py >= 0 &&
- px < this.area[0].length &&
- py < this.area.length;
- }
- World.prototype.isReachable = function(from, to) {
- var map = [],
- toGo = [{ pos: from, dist: 0}],
- done = {};
- if (from[0] == to[0] && from[1] == to[1])
- return { step: from, dist: 0 };
- while (toGo.length) {
- var pos = toGo[0].pos,
- dist = toGo[0].dist,
- moves = [[ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ]];
- for (var i =0; i < moves.length; ++i) {
- var newCoords = [pos[0] +moves[i][0], pos[1] +moves[i][1]];
- if (this.coordExists(newCoords[0], newCoords[1])) {
- if (newCoords[0] == to[0] && newCoords[1] == to[1])
- return { step: pos, dist: dist +1 };
- toGo.push({pos: newCoords, dist: dist +1})
- }
- }
- done[pos[0]+'x'+pos[1]] = dist;
- toGo.splice(0, 1);
- toGo = toGo.filter(c =>
- !done[c.pos[0]+'x'+c.pos[1]] &&
- this.isEmpty(c.pos[0], c.pos[1]));
- toGo.sort((a, b) => a.dist -b.dist);
- }
- return false;
- }
- World.prototype.print = function() {
- var w = [];
- this.sortUnits();
- this.area.forEach(l => w.push(l.slice()));
- this.units.forEach(u => u.hp ? w[u.py][u.px] = u.type : null);
- w.forEach(l => {
- var lStr = "";
- l.forEach(c => lStr += c);
- console.log(lStr);
- });
- console.log(this.units.filter(u => u.hp));
- console.log("Age: ", this.age);
- var sumHp = 0;
- this.units.forEach(u => sumHp += u.hp);
- console.log("Sum hp: ", sumHp);
- console.log("Outcome: ", sumHp *this.age);
- }
- World.prototype.remainingTypes = function() {
- var types = {};
- this.units.forEach(u => u.hp ? types[u.type] = true : false);
- return Object.keys(types);
- }
- World.prototype.hasOpponents = function() {
- return this.remainingTypes().length > 1;
- }
- World.prototype.run = function(stopFnc) {
- while (stopFnc(this))
- this.step();
- }
- function Unit(type, px, py) {
- this.type = type;
- this.px = px;
- this.py = py;
- this.hp = 200;
- this.atk = 3;
- }
- Unit.prototype.step = function(world) {
- this.move(world);
- this.attack(world);
- }
- Unit.prototype.range = function(world, except) {
- var range = [],
- moves = [[ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ]];
- moves.forEach(m => {
- if (world.coordExists(this.px +m[0], this.py +m[1]) && (world.isEmpty(this.px +m[0], this.py +m[1]) || (except && (except.px == this.px +m[0] && except.py == this.py +m[1]))))
- range.push([this.px +m[0], this.py +m[1]]);
- });
- return range;
- }
- Unit.prototype.move = function(world) {
- var inRange = [];
- world.units.filter(u => u.hp && u.type !== this.type).forEach(t => t.range(world, this).forEach(r => inRange.push({ unit: t, pos: r })));
- var reachable = inRange.filter(r => { r.dist = world.isReachable(r.pos, [this.px, this.py]); return r.dist !== false; });
- var dest = reachable.sort((a, b) => {
- if (a.dist.dist != b.dist.dist) return a.dist.dist -b.dist.dist;
- if (a.pos[1] == b.pos[1]) return a.pos[0] -b.pos[0];
- return a.pos[1] -b.pos[1];
- })[0];
- if (dest) {
- this.px = dest.dist.step[0];
- this.py = dest.dist.step[1];
- }
- }
- Unit.prototype.attack = function(world) {
- var units = world.units.filter(u => u.hp && u.type !== this.type && (Math.abs(u.px -this.px) +Math.abs(u.py -this.py)) == 1);
- if (units.length) {
- units.sort((a, b) => {
- var diff = a.hp -b.hp;
- if (diff) return diff;
- diff = a.py -b.py;
- if (diff) return diff;
- return a.px -b.px;
- });
- units[0].takeDamage(this);
- }
- }
- Unit.prototype.takeDamage = function(from) {
- this.hp = Math.max(0, this.hp -from.atk);
- }
- function ex1() {
- readAll(world => {
- world.run(world => world.hasOpponents());
- world.print();
- });
- }
- function ex2(min, max) {
- readAll(world => {
- var stopReason = -1,
- current = Math.floor((min +max) /2);
- world.units.forEach(u => u.type === 'E' ? u.atk = current : 0);
- world.run(world => {
- if (!world.hasOpponents()) {
- stopReason = 'E';
- return false;
- }
- if (world.units.filter(u => u.hp === 0 && u.type === 'E').length) {
- stopReason = 'G';
- return false;
- }
- return true;
- });
- if (max == min || (min +1 == current && stopReason === 'E')) {
- console.log("Min atk: " +(stopReason === 'E' ? min : min +1));
- world.print();
- } else if (stopReason == 'G') {
- console.log("Atk " +current +" not enough");
- ex2(current +1, max);
- } else {
- console.log("Atk " +current +" too much");
- ex2(min, current);
- }
- });
- }
- ex1();
- ex2(4, 300);
|