Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (function (AOF) {
- function Puzzle (input, code, config) {
- if (!config) {
- config = {};
- config.day = 1;
- config.part = 1;
- }
- this.day = config.day || 1;
- this.part = config.part || 1;
- this.input = input;
- this.code = code;
- this.solution = null;
- }
- Puzzle.prototype.constructor = Puzzle;
- Puzzle.prototype.solve = function () {
- if (this.code && typeof this.code === "function") {
- this.solution = this.code(this.input);
- }
- return this.solution;
- }
- AOF.Puzzle = Puzzle;
- return AOF;
- })(window.AdventOfCode || (AdventOfCode = {}))
- /**/
- var input = `################################
- ##########################..####
- #########################...####
- #########################..#####
- ########################G..#####
- #####################.#.....##.#
- #####################..........#
- ##############.#####...........#
- ########G...G#.####............#
- #######......G....#.....#......#
- #######...G....GG.#............#
- #######G.G.............####....#
- #######.#.....#####....E.....###
- #######......#######.G.......###
- #..####..G..#########.###..#####
- #........G..#########.##########
- #..#..#G....#########.##########
- #.###...E...#########.##########
- #####...G.G.#########.##########
- ########G....#######..##########
- ####..........#####...##########
- ####......E........G..##########
- #.G..................###########
- #G...................###########
- ###.....##E.......E..###########
- ###....#............############
- ###.................############
- ##G.....#.............##########
- ###########...#E..##..##########
- ###########.E...###.E.EE.#######
- ###########......#.......#######
- ################################`;
- /**/
- /*
- var input = `#########
- #G......#
- #.E.#...#
- #..##..G#
- #...##..#
- #...#...#
- #.G...G.#
- #.....G.#
- #########`;
- */
- var puzzle = new AdventOfCode.Puzzle(
- input,
- function (input) {
- /*
- Utility functions
- */
- const sortByPosition = (a,b) => {
- if (a.y < b.y) {
- return -1;
- }
- if (a.y > b.y) {
- return 1;
- }
- if (a.x < b.x) {
- return -1;
- }
- if (a.x > b.x) {
- return 1;
- }
- return 0;
- }
- const sortByHitPoints = (a,b) => {
- if (a.hp < b.hp) {
- return -1;
- }
- if (a.hp > b.hp) {
- return 1;
- }
- return sortByPosition(a,b);
- }
- const sortReachableByLength = (a,b) => {
- if (a.length !== b.length) {
- return a.length - b.length;
- }
- return sortByPosition(a[a.length-1],b[b.length-1]);
- }
- const getPropSum = (array, prop, filter) => {
- let sum = 0;
- for (let i = 0; i < array.length; i++) {
- let toSum = true;
- if (filter) {
- for (let p in filter) {
- if (!(p in array[i]) || ((p in array[i]) && array[i][p] !== filter[p])) {
- toSum = false;
- break;
- }
- }
- if (!toSum) {
- continue;
- }
- }
- if (toSum) {
- sum += array[i][prop];
- }
- }
- return sum;
- }
- const sortPathsByLength = (a,b) => {
- if (a.length !== b.length) {
- return a.length - b.length;
- }
- return sortByPosition(a[0],b[0]);
- }
- const findByProps = (haystack, props) => {
- for (let i = 0; i < haystack.length; i++) {
- let found = true;
- for (let prop in props) {
- if (haystack[i][prop] !== props[prop]) {
- found = false;
- break;
- }
- }
- if (found) {
- return haystack[i];
- }
- }
- return false;
- }
- class Node {
- constructor(x, y, occupied) {
- this.x = x;
- this.y = y;
- this.neighbours = {};
- this.occupied = !!occupied;
- }
- addNeighbour(dir, node) {
- this.neighbours[dir] = node;
- node.neighbours[3 - dir] = this;
- }
- }
- class Graph {
- constructor () {
- this.nodes = {};
- }
- addNode (node) {
- this.nodes[`${node.y}|${node.x}`] = node;
- return this;
- }
- pathExists (node1, node2) {
- if (
- !this.hasNode(node1)
- || !this.hasNode(node2)
- ) {
- return false;
- }
- let visited = [];
- let queue = [node1];
- while (queue.length) {
- let cur = queue.shift();
- if (cur === node2) {
- return true;
- }
- visited.push(cur);
- for (let prop in cur.neighbours) {
- if (!~visited.indexOf(cur.neighbours[prop])) {
- visited.push(cur.neighbours[prop]);
- queue.push(cur.neighbours[prop])
- }
- }
- }
- return false;
- }
- hasNode (node) {
- if (!(node instanceof Node)) {
- return false;
- }
- if (
- !this.nodes[`${node.y}|${node.x}`]
- || this.nodes[`${node.y}|${node.x}`] !== node
- ) {
- return false;
- }
- return true;
- }
- getPath (node1, node2) {
- if (!this.pathExists(node1,node2)) {
- return [];
- }
- let visited = [];
- let queue = [{node : node1, level : 0, prev : null}];
- let cur = null;
- while (queue.length) {
- cur = queue.shift();
- if (cur.node === node2) {
- break;
- }
- visited.push(cur.node);
- for (let prop in cur.node.neighbours) {
- if (!~visited.indexOf(cur.node.neighbours[prop])) {
- visited.push(cur.node.neighbours[prop]);
- queue.push({node : cur.node.neighbours[prop], level:cur.level+1, prev : cur});
- }
- }
- }
- let path = [];
- while (cur) {
- path.push(cur.node);
- cur = cur.prev;
- }
- return path.reverse();
- }
- getNodeFromCoordinates(point) {
- return this.nodes[`${point.y}|${point.x}`];
- }
- }
- class Entity {
- constructor(map, x, y, type, hp, power) {
- this.map = map;
- this.x = +x;
- this.y = +y;
- this.type = type;
- this.hp = hp;
- this.power = power;
- this.target = null;
- this.alive = true;
- }
- attack (target) {
- target.hp -= this.power;
- if (target.hp <= 0) {
- target.alive = false;
- }
- }
- getTargets(sortFn) {
- let entities = this.map.entities;
- let targets = [];
- for (let i = 0; i < entities.length; i++) {
- if (entities[i].type !== this.type && entities[i].alive) {
- targets.push(entities[i]);
- }
- }
- return sortFn ? targets.sort(sortFn) : targets;
- }
- getRangeCells() {
- let cells = [];
- let diffs = [[0,-1],[-1,0],[1,0],[0,1]];
- let entities = this.map.entities;
- for (let pos in diffs) {
- let x = +this.x + diffs[pos][0];
- let y = +this.y + diffs[pos][1];
- if (this.map.cells[y] && this.map.cells[y][x] == '.') {
- let entityFound = false;
- for (let i in entities) {
- if (entities[i].x === x && entities[i].y === y && entities[i].alive) {
- entityFound = true;
- break;
- }
- }
- if (!entityFound) {
- cells.push({x : x, y : y});
- }
- }
- }
- return cells;
- }
- getEnemyRangeCells() {
- let targets = this.getTargets();
- let cells = [];
- for (let i = 0; i < targets.length; i++) {
- let curCells = targets[i].getRangeCells();
- for (let j = 0; j < curCells.length; j++) {
- if (findByProps(cells,curCells[j]) === false) {
- cells.push(curCells[j]);
- }
- }
- }
- return cells.sort(sortByPosition);
- }
- getEnemyReachableRangeCells() {
- let cells = this.getEnemyRangeCells();
- let self = this;
- cells = cells.filter(function (el) {
- return self.map.graph.pathExists(
- self.map.graph.nodes[`${self.y}|${self.x}`],
- self.map.graph.nodes[`${el.y}|${el.x}`]
- );
- });
- return cells;
- }
- getCurrentNode() {
- return this.map.graph.nodes[`${this.y}|${this.x}`];
- }
- getShortestPathToReach() {
- let toReach = this.getEnemyReachableRangeCells();
- let paths = [];
- for (let i=0; i<toReach.length; i++) {
- let node = this.map.graph.nodes[`${toReach[i].y}|${toReach[i].x}`];
- let curPath = this.map.graph.getPath(this.getCurrentNode(),node);
- if (curPath.length) {
- paths.push(curPath);
- }
- }
- paths.sort(sortReachableByLength);
- let path = paths[0] || [];
- if (path) {
- let target = path[path.length - 1]
- let neighbours = this.getRangeCells();
- paths = [];
- for (let prop in neighbours) {
- /* TODO */
- let nodeNeighbour = this.map.graph.getNodeFromCoordinates(neighbours[prop]);
- let curPath = this.map.graph.getPath(nodeNeighbour, target);
- if (curPath.length) {
- paths.push(curPath);
- }
- }
- }
- return paths.sort(sortPathsByLength)[0];
- }
- getReachableTarget() {
- let targets = this.getTargets(sortByHitPoints);
- let reachable = [];
- for (let i = 0; i < targets.length; i++) {
- if (
- (targets[i].y === this.y && (targets[i].x == this.x-1 || targets[i].x == this.x+1))
- || (targets[i].x === this.x && (targets[i].y == this.y-1 || targets[i].y == this.y+1))
- ) {
- reachable.push(targets[i]);
- }
- }
- if (!reachable.length) {
- this.target = null;
- } else {
- reachable.sort(sortByHitPoints);
- this.target = reachable.shift();
- }
- return this.target;
- }
- }
- class Map {
- constructor(cells, entities) {
- this.cells = cells;
- this.entities = entities;
- this.graph = null;
- }
- static create (string) {
- string = string.split("\n");
- let entities = [];
- let map = new this([],[]);
- for (let y in string) {
- string[y] = string[y].split('');
- for (let x in string[y]) {
- switch(string[y][x]) {
- case 'G':
- case 'E':
- let type = string[y][x] === 'G' ? 'Goblin' : 'Elf';
- entities.push(new Entity(map, x, y, type, 200, 3));
- string[y][x] = '.';
- break;
- }
- }
- }
- for (var i = 0; i < entities.length; i++) {
- entities.map = map;
- }
- map.entities = entities;
- map.cells = string;
- return map;
- }
- resetGraph() {
- let graph = this.graph = new Graph();
- let cells = this.cells;
- let entities = Array.prototype.slice.call(this.entities);
- for (let y = 0; y < cells.length; y++) {
- for (let x = 0; x < cells[y].length; x++) {
- let cell = cells[y][x];
- if (cell === '.') {
- let node = new Node(x,y);
- graph.nodes[`${y}|${x}`] = node;
- if (findByProps(entities, {x, y, alive: true})) {
- node.occupied = true;
- if (graph.nodes[`${y-1}|${x}`] && !graph.nodes[`${y-1}|${x}`].occupied) {
- node.neighbours[0] = graph.nodes[`${y-1}|${x}`];
- }
- if (graph.nodes[`${y}|${x-1}`] && !graph.nodes[`${y}|${x-1}`].occupied) {
- node.neighbours[1] = graph.nodes[`${y}|${x-1}`];
- }
- } else {
- if (graph.nodes[`${y-1}|${x}`] && !graph.nodes[`${y-1}|${x}`].occupied) {
- node.addNeighbour(0,graph.nodes[`${y-1}|${x}`]);
- } else if (graph.nodes[`${y-1}|${x}`]) {
- graph.nodes[`${y-1}|${x}`].neighbours[3] = node;
- }
- if (graph.nodes[`${y}|${x-1}`] && !graph.nodes[`${y}|${x-1}`].occupied) {
- node.addNeighbour(1,graph.nodes[`${y}|${x-1}`]);
- } else if (graph.nodes[`${y}|${x-1}`]) {
- graph.nodes[`${y}|${x-1}`].neighbours[2] = node;
- }
- }
- }
- }
- }
- this.graph = graph;
- return graph;
- }
- print (entity, filter, filteredArray, chr) {
- let char = '.';
- let filtered = [];
- let string = '';
- if (filteredArray) {
- filtered = filteredArray;
- if (chr) {
- char = chr;
- }
- } else if (entity) {
- switch (filter) {
- case 'getEnemyRangeCells':
- char = '?';
- break;
- case 'getEnemyRangeCells':
- char = '@';
- break;
- }
- filtered = entity[filter]();
- }
- for (let y = 0; y < this.cells.length; y++) {
- for (let x = 0; x < this.cells[y].length; x++) {
- let ent = null;
- if (findByProps(filtered, {x, y})) {
- string += char;
- } else if (ent = findByProps(this.entities, {x, y, alive: true})) {
- string += ent.type === 'Goblin' ? 'G' : 'E';
- } else {
- string += this.cells[y][x];
- }
- }
- string += "\n"
- }
- return string;
- }
- }
- /* BEFORE */
- let map = Map.create(input);
- /* IN THE LOOP */
- let newPrinted = null;
- let rounds = 0;
- let ended = false;
- let sum = 0;
- let solution = NaN;
- while (!ended) {
- rounds++;
- let entities = map.entities.sort(sortByPosition);
- let graph = map.resetGraph();
- for (let i = 0; i < entities.length; i++) {
- if (!entities[i].alive) {
- continue;
- }
- let rangeCells = entities[i].getRangeCells();
- let curTarget = entities[i].getReachableTarget();
- console.log(map.print(null,null,rangeCells,'$'));
- if (curTarget === null) {
- let path = entities[i].getShortestPathToReach();
- console.log(map.print(null,null,path,'w'));
- if (path) {
- entities[i].x = path[0].x;
- entities[i].y = path[0].y;
- curTarget = entities[i].getReachableTarget();
- if (curTarget) {
- entities[i].attack(curTarget);
- }
- }
- } else {
- entities[i].attack(curTarget);
- }
- let targets = entities[i].getTargets();
- if (targets.length === 0) {
- sum = getPropSum(entities,'hp', {alive : true});
- solution = sum * (rounds - 1);
- console.log('solution', rounds - 1, sum, solution, entities);
- ended = true;
- break;
- }
- }
- }
- },
- {
- day : 15,
- part : 1
- }
- );
- console.log(puzzle.solve());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement