main.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. function readAll(cb) {
  2. require("fs").readFile('./input', 'utf8', (err, data) => {
  3. var world = new World(),
  4. j =0;
  5. data.split("\n").forEach(l => {
  6. if (l.length) {
  7. var line = [];
  8. for (var i =0, len = l.length; i < len; ++i) {
  9. var c = l.charAt(i);
  10. line.push(c == '#' ? '#' : '.');
  11. if ("#.".indexOf(c) == -1) {
  12. var u = new Unit(c, i, j);
  13. world.units.push(u);
  14. }
  15. }
  16. world.area.push(line);
  17. ++j;
  18. }
  19. });
  20. cb(world);
  21. });
  22. }
  23. function World() {
  24. this.area = [];
  25. this.units = [];
  26. this.age = -1;
  27. }
  28. World.prototype.sortUnits = function() {
  29. this.units.sort((a, b) => (a.py == b.py ? a.px -b.px : a.py -b.py));
  30. }
  31. World.prototype.step = function() {
  32. this.sortUnits();
  33. this.units.forEach(u => u.hp ? u.step(this) : null);
  34. ++this.age;
  35. }
  36. World.prototype.isEmpty = function(px, py) {
  37. if (this.area[py][px] == '#')
  38. return false;
  39. return !this.units.some(u => u.hp && u.px == px && u.py == py);
  40. }
  41. World.prototype.coordExists = function(px, py) {
  42. return px >= 0 && py >= 0 &&
  43. px < this.area[0].length &&
  44. py < this.area.length;
  45. }
  46. World.prototype.isReachable = function(from, to) {
  47. var map = [],
  48. toGo = [{ pos: from, dist: 0}],
  49. done = {};
  50. if (from[0] == to[0] && from[1] == to[1])
  51. return { step: from, dist: 0 };
  52. while (toGo.length) {
  53. var pos = toGo[0].pos,
  54. dist = toGo[0].dist,
  55. moves = [[ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ]];
  56. for (var i =0; i < moves.length; ++i) {
  57. var newCoords = [pos[0] +moves[i][0], pos[1] +moves[i][1]];
  58. if (this.coordExists(newCoords[0], newCoords[1])) {
  59. if (newCoords[0] == to[0] && newCoords[1] == to[1])
  60. return { step: pos, dist: dist +1 };
  61. toGo.push({pos: newCoords, dist: dist +1})
  62. }
  63. }
  64. done[pos[0]+'x'+pos[1]] = dist;
  65. toGo.splice(0, 1);
  66. toGo = toGo.filter(c =>
  67. !done[c.pos[0]+'x'+c.pos[1]] &&
  68. this.isEmpty(c.pos[0], c.pos[1]));
  69. toGo.sort((a, b) => a.dist -b.dist);
  70. }
  71. return false;
  72. }
  73. World.prototype.print = function() {
  74. var w = [];
  75. this.sortUnits();
  76. this.area.forEach(l => w.push(l.slice()));
  77. this.units.forEach(u => u.hp ? w[u.py][u.px] = u.type : null);
  78. w.forEach(l => {
  79. var lStr = "";
  80. l.forEach(c => lStr += c);
  81. console.log(lStr);
  82. });
  83. console.log(this.units.filter(u => u.hp));
  84. console.log("Age: ", this.age);
  85. var sumHp = 0;
  86. this.units.forEach(u => sumHp += u.hp);
  87. console.log("Sum hp: ", sumHp);
  88. console.log("Outcome: ", sumHp *this.age);
  89. }
  90. World.prototype.remainingTypes = function() {
  91. var types = {};
  92. this.units.forEach(u => u.hp ? types[u.type] = true : false);
  93. return Object.keys(types);
  94. }
  95. World.prototype.hasOpponents = function() {
  96. return this.remainingTypes().length > 1;
  97. }
  98. World.prototype.run = function(stopFnc) {
  99. while (stopFnc(this))
  100. this.step();
  101. }
  102. function Unit(type, px, py) {
  103. this.type = type;
  104. this.px = px;
  105. this.py = py;
  106. this.hp = 200;
  107. this.atk = 3;
  108. }
  109. Unit.prototype.step = function(world) {
  110. this.move(world);
  111. this.attack(world);
  112. }
  113. Unit.prototype.range = function(world, except) {
  114. var range = [],
  115. moves = [[ 0, -1 ], [ -1, 0 ], [ 1, 0 ], [ 0, 1 ]];
  116. moves.forEach(m => {
  117. 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]))))
  118. range.push([this.px +m[0], this.py +m[1]]);
  119. });
  120. return range;
  121. }
  122. Unit.prototype.move = function(world) {
  123. var inRange = [];
  124. world.units.filter(u => u.hp && u.type !== this.type).forEach(t => t.range(world, this).forEach(r => inRange.push({ unit: t, pos: r })));
  125. var reachable = inRange.filter(r => { r.dist = world.isReachable(r.pos, [this.px, this.py]); return r.dist !== false; });
  126. var dest = reachable.sort((a, b) => {
  127. if (a.dist.dist != b.dist.dist) return a.dist.dist -b.dist.dist;
  128. if (a.pos[1] == b.pos[1]) return a.pos[0] -b.pos[0];
  129. return a.pos[1] -b.pos[1];
  130. })[0];
  131. if (dest) {
  132. this.px = dest.dist.step[0];
  133. this.py = dest.dist.step[1];
  134. }
  135. }
  136. Unit.prototype.attack = function(world) {
  137. 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);
  138. if (units.length) {
  139. units.sort((a, b) => {
  140. var diff = a.hp -b.hp;
  141. if (diff) return diff;
  142. diff = a.py -b.py;
  143. if (diff) return diff;
  144. return a.px -b.px;
  145. });
  146. units[0].takeDamage(this);
  147. }
  148. }
  149. Unit.prototype.takeDamage = function(from) {
  150. this.hp = Math.max(0, this.hp -from.atk);
  151. }
  152. function ex1() {
  153. readAll(world => {
  154. world.run(world => world.hasOpponents());
  155. world.print();
  156. });
  157. }
  158. function ex2(min, max) {
  159. readAll(world => {
  160. var stopReason = -1,
  161. current = Math.floor((min +max) /2);
  162. world.units.forEach(u => u.type === 'E' ? u.atk = current : 0);
  163. world.run(world => {
  164. if (!world.hasOpponents()) {
  165. stopReason = 'E';
  166. return false;
  167. }
  168. if (world.units.filter(u => u.hp === 0 && u.type === 'E').length) {
  169. stopReason = 'G';
  170. return false;
  171. }
  172. return true;
  173. });
  174. if (max == min || (min +1 == current && stopReason === 'E')) {
  175. console.log("Min atk: " +(stopReason === 'E' ? min : min +1));
  176. world.print();
  177. } else if (stopReason == 'G') {
  178. console.log("Atk " +current +" not enough");
  179. ex2(current +1, max);
  180. } else {
  181. console.log("Atk " +current +" too much");
  182. ex2(min, current);
  183. }
  184. });
  185. }
  186. ex1();
  187. ex2(4, 300);