it's not fast but it works
This commit is contained in:
		
							
								
								
									
										177
									
								
								06/index.ts
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								06/index.ts
									
									
									
									
									
								
							@@ -1,37 +1,55 @@
 | 
				
			|||||||
import fs from "node:fs";
 | 
					import fs from "node:fs";
 | 
				
			||||||
import { printTime, now } from "../util";
 | 
					import { printTime, now } from "../util";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tile = { guard: boolean; blocked: boolean; visited: boolean };
 | 
					type tile = {
 | 
				
			||||||
 | 
					  guard: boolean;
 | 
				
			||||||
 | 
					  blocked: boolean;
 | 
				
			||||||
 | 
					  visited: boolean;
 | 
				
			||||||
 | 
					  hit: { north: number; east: number; south: number; west: number };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const test = true;
 | 
					const test = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const data = fs.readFileSync(
 | 
					const data = fs.readFileSync(
 | 
				
			||||||
  test ? "./inputs/testinput" : "./inputs/input",
 | 
					  test ? "./inputs/testinput" : "./inputs/input",
 | 
				
			||||||
  "utf8"
 | 
					  "utf8"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const generateFloor = (data: string): Record<string, tile> => {
 | 
				
			||||||
 | 
					  return data
 | 
				
			||||||
 | 
					    .split("\n")
 | 
				
			||||||
 | 
					    .slice(0, -1)
 | 
				
			||||||
 | 
					    .reduce((floor, row, i) => {
 | 
				
			||||||
 | 
					      row.split("").forEach((v, col) => {
 | 
				
			||||||
 | 
					        floor[`${i}|${col}`] = {
 | 
				
			||||||
 | 
					          guard: v === "^",
 | 
				
			||||||
 | 
					          blocked: v === "#",
 | 
				
			||||||
 | 
					          visited: false,
 | 
				
			||||||
 | 
					          hit: {
 | 
				
			||||||
 | 
					            north: 0,
 | 
				
			||||||
 | 
					            south: 0,
 | 
				
			||||||
 | 
					            east: 0,
 | 
				
			||||||
 | 
					            west: 0,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return floor;
 | 
				
			||||||
 | 
					    }, {} as Record<string, tile>);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const keyToCoord = (string): [number, number] => {
 | 
				
			||||||
 | 
					  return string.split("|").map((v) => parseInt(v, 10));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let timer = now.instant();
 | 
					let timer = now.instant();
 | 
				
			||||||
console.log(
 | 
					console.log(
 | 
				
			||||||
  "part one:",
 | 
					  "part one:",
 | 
				
			||||||
  (() => {
 | 
					  (() => {
 | 
				
			||||||
    let floor = data
 | 
					    let floor = generateFloor(data);
 | 
				
			||||||
      .split("\n")
 | 
					 | 
				
			||||||
      .slice(0, -1)
 | 
					 | 
				
			||||||
      .reduce((floor, row, i) => {
 | 
					 | 
				
			||||||
        row.split("").forEach((v, col) => {
 | 
					 | 
				
			||||||
          floor[`${i}|${col}`] = {
 | 
					 | 
				
			||||||
            guard: v === "^",
 | 
					 | 
				
			||||||
            blocked: v === "#",
 | 
					 | 
				
			||||||
            visited: false,
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        return floor;
 | 
					 | 
				
			||||||
      }, {} as Record<string, tile>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let direction = "north";
 | 
					    let direction = "north";
 | 
				
			||||||
    let [guard] = Object.entries(floor).filter((tile) => tile[1].guard)[0];
 | 
					    let [guard] = Object.entries(floor).filter((tile) => tile[1].guard)[0];
 | 
				
			||||||
    while (true) {
 | 
					    while (true) {
 | 
				
			||||||
      let [x, y] = guard.split("|").map((v) => parseInt(v, 10));
 | 
					      let [x, y] = keyToCoord(guard);
 | 
				
			||||||
      floor[guard].visited = true;
 | 
					      floor[guard].visited = true;
 | 
				
			||||||
      let next = "";
 | 
					      let next = "";
 | 
				
			||||||
      switch (direction) {
 | 
					      switch (direction) {
 | 
				
			||||||
@@ -73,3 +91,128 @@ console.log(
 | 
				
			|||||||
  })(),
 | 
					  })(),
 | 
				
			||||||
  printTime(now.instant().since(timer))
 | 
					  printTime(now.instant().since(timer))
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					timer = now.instant();
 | 
				
			||||||
 | 
					console.log(
 | 
				
			||||||
 | 
					  "part two:",
 | 
				
			||||||
 | 
					  (() => {
 | 
				
			||||||
 | 
					    const floor = generateFloor(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let direction = "north";
 | 
				
			||||||
 | 
					    let guard = Object.entries(floor).filter((tile) => tile[1].guard)[0][0];
 | 
				
			||||||
 | 
					    const initialGuardPosition = guard;
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					      let [x, y] = keyToCoord(guard);
 | 
				
			||||||
 | 
					      floor[guard].visited = true;
 | 
				
			||||||
 | 
					      let next = "";
 | 
				
			||||||
 | 
					      switch (direction) {
 | 
				
			||||||
 | 
					        case "north":
 | 
				
			||||||
 | 
					          next = `${x - 1}|${y}`;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case "south":
 | 
				
			||||||
 | 
					          next = `${x + 1}|${y}`;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case "east":
 | 
				
			||||||
 | 
					          next = `${x}|${y + 1}`;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case "west":
 | 
				
			||||||
 | 
					          next = `${x}|${y - 1}`;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (floor[next] === undefined) {
 | 
				
			||||||
 | 
					        // guard left the floor, exit
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!floor[next].blocked) {
 | 
				
			||||||
 | 
					        // move the guard
 | 
				
			||||||
 | 
					        floor[next].guard = true;
 | 
				
			||||||
 | 
					        floor[guard].guard = false;
 | 
				
			||||||
 | 
					        guard = next;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // guard is blocked, rotate
 | 
				
			||||||
 | 
					        if (direction === "north") {
 | 
				
			||||||
 | 
					          direction = "east";
 | 
				
			||||||
 | 
					        } else if (direction === "east") {
 | 
				
			||||||
 | 
					          direction = "south";
 | 
				
			||||||
 | 
					        } else if (direction === "south") {
 | 
				
			||||||
 | 
					          direction = "west";
 | 
				
			||||||
 | 
					        } else if (direction === "west") {
 | 
				
			||||||
 | 
					          direction = "north";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let newObstructions = Object.entries(floor).reduce((total, tile) => {
 | 
				
			||||||
 | 
					      if (tile[1].visited && tile[0] !== initialGuardPosition) {
 | 
				
			||||||
 | 
					        return [...total, tile[0]];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return total;
 | 
				
			||||||
 | 
					    }, [] as string[]);
 | 
				
			||||||
 | 
					    return newObstructions.reduce((total, curr) => {
 | 
				
			||||||
 | 
					      const floor = generateFloor(data);
 | 
				
			||||||
 | 
					      floor[curr].blocked = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // iterate as above, but this time we tract how many times the guard hits any obstruction from the same direction.
 | 
				
			||||||
 | 
					      // if the guard ever hits one from the same direction twice, we know they're in a loop.
 | 
				
			||||||
 | 
					      let direction = "north";
 | 
				
			||||||
 | 
					      let guard = Object.entries(floor).filter((tile) => tile[1].guard)[0][0];
 | 
				
			||||||
 | 
					      while (true) {
 | 
				
			||||||
 | 
					        let [x, y] = keyToCoord(guard);
 | 
				
			||||||
 | 
					        let next = "";
 | 
				
			||||||
 | 
					        switch (direction) {
 | 
				
			||||||
 | 
					          case "north":
 | 
				
			||||||
 | 
					            next = `${x - 1}|${y}`;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case "south":
 | 
				
			||||||
 | 
					            next = `${x + 1}|${y}`;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case "east":
 | 
				
			||||||
 | 
					            next = `${x}|${y + 1}`;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case "west":
 | 
				
			||||||
 | 
					            next = `${x}|${y - 1}`;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (floor[next] === undefined) {
 | 
				
			||||||
 | 
					          // guard left the floor, exit
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!floor[next].blocked) {
 | 
				
			||||||
 | 
					          // move the guard
 | 
				
			||||||
 | 
					          floor[next].guard = true;
 | 
				
			||||||
 | 
					          floor[guard].guard = false;
 | 
				
			||||||
 | 
					          guard = next;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // guard is blocked, log impact and rotate
 | 
				
			||||||
 | 
					          if (direction === "north") {
 | 
				
			||||||
 | 
					            floor[next].hit.north++;
 | 
				
			||||||
 | 
					            if (floor[next].hit.north === 2) {
 | 
				
			||||||
 | 
					              return total + 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            direction = "east";
 | 
				
			||||||
 | 
					          } else if (direction === "east") {
 | 
				
			||||||
 | 
					            floor[next].hit.east++;
 | 
				
			||||||
 | 
					            if (floor[next].hit.east === 2) {
 | 
				
			||||||
 | 
					              return total + 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            direction = "south";
 | 
				
			||||||
 | 
					          } else if (direction === "south") {
 | 
				
			||||||
 | 
					            floor[next].hit.south++;
 | 
				
			||||||
 | 
					            if (floor[next].hit.south === 2) {
 | 
				
			||||||
 | 
					              return total + 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            direction = "west";
 | 
				
			||||||
 | 
					          } else if (direction === "west") {
 | 
				
			||||||
 | 
					            floor[next].hit.west++;
 | 
				
			||||||
 | 
					            if (floor[next].hit.west === 2) {
 | 
				
			||||||
 | 
					              return total + 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            direction = "north";
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return total;
 | 
				
			||||||
 | 
					    }, 0);
 | 
				
			||||||
 | 
					  })(),
 | 
				
			||||||
 | 
					  printTime(now.instant().since(timer))
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user