Day 6 was fun
This commit is contained in:
parent
698c19395f
commit
9d973baabd
5 changed files with 406 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
.idea
|
**/inputs
|
||||||
|
*.txt
|
1
.tool-versions
Normal file
1
.tool-versions
Normal file
|
@ -0,0 +1 @@
|
||||||
|
python 3.12.6
|
154
2024/day06.py
Normal file
154
2024/day06.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import collections
|
||||||
|
|
||||||
|
class Direction:
|
||||||
|
def __init__(self, pos_change_x, pos_change_y):
|
||||||
|
self.pos_change_x = pos_change_x
|
||||||
|
self.pos_change_y = pos_change_y
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, Direction):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.pos_change_x == other.pos_change_x and self.pos_change_y == other.pos_change_y
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.pos_change_x, self.pos_change_y))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Direction({self.pos_change_x}, {self.pos_change_y})"
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, Point):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.x, self.y))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Point({self.x}, {self.y})"
|
||||||
|
|
||||||
|
class Guard:
|
||||||
|
def __init__(self, pos, direction):
|
||||||
|
self.pos = pos
|
||||||
|
self.direction = direction
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Guard({self.pos}, {self.direction})"
|
||||||
|
|
||||||
|
def move(self, obstacles):
|
||||||
|
if self.direction == UP:
|
||||||
|
if (Point(self.pos.x, self.pos.y - 1) in obstacles):
|
||||||
|
self.direction = RIGHT
|
||||||
|
return Point(self.pos.x, self.pos.y - 1)
|
||||||
|
else:
|
||||||
|
self.pos.y -= 1
|
||||||
|
elif self.direction == DOWN:
|
||||||
|
if (Point(self.pos.x, self.pos.y + 1) in obstacles):
|
||||||
|
self.direction = LEFT
|
||||||
|
return Point(self.pos.x, self.pos.y + 1)
|
||||||
|
else:
|
||||||
|
self.pos.y += 1
|
||||||
|
elif self.direction == LEFT:
|
||||||
|
if (Point(self.pos.x - 1, self.pos.y) in obstacles):
|
||||||
|
self.direction = UP
|
||||||
|
return Point(self.pos.x - 1, self.pos.y)
|
||||||
|
else:
|
||||||
|
self.pos.x -= 1
|
||||||
|
elif self.direction == RIGHT:
|
||||||
|
if (Point(self.pos.x + 1, self.pos.y) in obstacles):
|
||||||
|
self.direction = DOWN
|
||||||
|
return Point(self.pos.x + 1, self.pos.y)
|
||||||
|
else:
|
||||||
|
self.pos.x += 1
|
||||||
|
|
||||||
|
with open("./2024/inputs/day06.txt") as f:
|
||||||
|
m = [l.strip() for l in f.readlines()]
|
||||||
|
|
||||||
|
UP = Direction(0, -1)
|
||||||
|
DOWN = Direction(0, 1)
|
||||||
|
LEFT = Direction(-1, 0)
|
||||||
|
RIGHT = Direction(1, 0)
|
||||||
|
|
||||||
|
guard_starting_pos = None
|
||||||
|
guard_starting_direction = None
|
||||||
|
guard = None
|
||||||
|
obstacle_pos = set()
|
||||||
|
visited_pos = set()
|
||||||
|
|
||||||
|
for y in range(len(m)):
|
||||||
|
for x in range(len(m[y])):
|
||||||
|
if m[y][x] in ["^", "v", "<", ">"]:
|
||||||
|
guard_pos = Point(x, y)
|
||||||
|
|
||||||
|
if m[y][x] == "^":
|
||||||
|
guard_direction = UP
|
||||||
|
elif m[y][x] == "v":
|
||||||
|
guard_direction = DOWN
|
||||||
|
elif m[y][x] == "<":
|
||||||
|
guard_direction = LEFT
|
||||||
|
elif m[y][x] == ">":
|
||||||
|
guard_direction = RIGHT
|
||||||
|
|
||||||
|
guard = Guard(guard_pos, guard_direction)
|
||||||
|
|
||||||
|
elif m[y][x] == "#":
|
||||||
|
obstacle_pos.add(Point(x, y))
|
||||||
|
|
||||||
|
guard_starting_pos = Point(guard.pos.x, guard.pos.y)
|
||||||
|
guard_starting_direction = Direction(guard.direction.pos_change_x, guard.direction.pos_change_y)
|
||||||
|
|
||||||
|
# if the guard is still in the map, move him and register his position in the
|
||||||
|
# visited_pos set if he hasn't been there before. to move him, check if he has
|
||||||
|
# an obstacle right in front of him: if he does he moves 90 degrees to the right,
|
||||||
|
# if he doesn't, he takes a step forward.
|
||||||
|
while guard.pos.x >= 0 and guard.pos.x < len(m[0]) and guard.pos.y >= 0 and guard.pos.y < len(m):
|
||||||
|
if guard.pos not in visited_pos:
|
||||||
|
visited_pos.add(guard.pos)
|
||||||
|
|
||||||
|
guard.move(obstacle_pos)
|
||||||
|
|
||||||
|
print(len(visited_pos))
|
||||||
|
|
||||||
|
# now we have to find the number of positions where an obstacle can be added so that
|
||||||
|
# the guard is forced in a loop and never exits the map.
|
||||||
|
# we can do this by saving a set of the last 4 obstacles hit, and checking if a new set of 4 obstacles is
|
||||||
|
# the same 4 obstacles. if it is, we have found a loop.
|
||||||
|
loop_obstacle_pos = set()
|
||||||
|
|
||||||
|
for y in range(len(m)):
|
||||||
|
for x in range(len(m[y])):
|
||||||
|
if Point(x, y) in obstacle_pos or Point(x, y) == guard_starting_pos:
|
||||||
|
continue
|
||||||
|
|
||||||
|
last_visited_obstacles = collections.deque()
|
||||||
|
shadow_visited_obstacles = collections.deque()
|
||||||
|
possible_obstacle_pos = Point(x, y)
|
||||||
|
new_obstacles = obstacle_pos.copy()
|
||||||
|
new_obstacles.add(possible_obstacle_pos)
|
||||||
|
guard = Guard(Point(guard_starting_pos.x, guard_starting_pos.y), guard_starting_direction)
|
||||||
|
|
||||||
|
# now solve the map checking for the last 4 obstacles, if a loop is found add the new possible obstacle to the set
|
||||||
|
while guard.pos.x >= 0 and guard.pos.x < len(m[0]) and guard.pos.y >= 0 and guard.pos.y < len(m):
|
||||||
|
# save the last 4 obstacles
|
||||||
|
current_obstacle = guard.move(new_obstacles)
|
||||||
|
if current_obstacle is not None:
|
||||||
|
if current_obstacle in last_visited_obstacles:
|
||||||
|
if current_obstacle in shadow_visited_obstacles:
|
||||||
|
# guard is in a loop!
|
||||||
|
loop_obstacle_pos.add(possible_obstacle_pos)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
shadow_visited_obstacles.append(current_obstacle)
|
||||||
|
else:
|
||||||
|
last_visited_obstacles.append(current_obstacle)
|
||||||
|
shadow_visited_obstacles = collections.deque()
|
||||||
|
|
||||||
|
print(len(loop_obstacle_pos))
|
108
viz2024-06-01.js
Normal file
108
viz2024-06-01.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
let inputData = ``;
|
||||||
|
let m = [];
|
||||||
|
let guard, UP, DOWN, LEFT, RIGHT;
|
||||||
|
let obstacles = new Set();
|
||||||
|
let visitedPos = new Set();
|
||||||
|
let cellSize = 800 / 130;
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(800, 800);
|
||||||
|
frameRate(120);
|
||||||
|
|
||||||
|
// Parse input data
|
||||||
|
m = inputData.split('\n').map(line => line.trim());
|
||||||
|
|
||||||
|
// Define directions
|
||||||
|
UP = { x: 0, y: -1 };
|
||||||
|
DOWN = { x: 0, y: 1 };
|
||||||
|
LEFT = { x: -1, y: 0 };
|
||||||
|
RIGHT = { x: 1, y: 0 };
|
||||||
|
|
||||||
|
// Initialize guard and obstacles
|
||||||
|
for (let y = 0; y < m.length; y++) {
|
||||||
|
for (let x = 0; x < m[y].length; x++) {
|
||||||
|
let cell = m[y][x];
|
||||||
|
if (["^", "v", "<", ">"].includes(cell)) {
|
||||||
|
guard = {
|
||||||
|
pos: { x, y },
|
||||||
|
direction:
|
||||||
|
cell === "^" ? UP :
|
||||||
|
cell === "v" ? DOWN :
|
||||||
|
cell === "<" ? LEFT :
|
||||||
|
RIGHT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell === "#") {
|
||||||
|
obstacles.add(`${x},${y}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
background(220);
|
||||||
|
|
||||||
|
moveGuard();
|
||||||
|
|
||||||
|
drawGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveGuard() {
|
||||||
|
if (!visitedPos.has(`${guard.pos.x},${guard.pos.y}`)) {
|
||||||
|
visitedPos.add(`${guard.pos.x},${guard.pos.y}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextPos = {
|
||||||
|
x: guard.pos.x + guard.direction.x,
|
||||||
|
y: guard.pos.y + guard.direction.y
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isObstacle(nextPos)) {
|
||||||
|
// Change direction if obstacle encountered
|
||||||
|
if (guard.direction === UP) guard.direction = RIGHT;
|
||||||
|
else if (guard.direction === RIGHT) guard.direction = DOWN;
|
||||||
|
else if (guard.direction === DOWN) guard.direction = LEFT;
|
||||||
|
else if (guard.direction === LEFT) guard.direction = UP;
|
||||||
|
} else {
|
||||||
|
// Move guard
|
||||||
|
guard.pos = nextPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isObstacle(pos) {
|
||||||
|
return obstacles.has(`${pos.x},${pos.y}`) ||
|
||||||
|
pos.x < 0 || pos.x >= m[0].length ||
|
||||||
|
pos.y < 0 || pos.y >= m.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrid() {
|
||||||
|
// Draw visited positions
|
||||||
|
fill(255, 255, 0, 100); // Transparent yellow
|
||||||
|
visitedPos.forEach(pos => {
|
||||||
|
let [x, y] = pos.split(',').map(Number);
|
||||||
|
rect(x * cellSize, y * cellSize, cellSize, cellSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw obstacles
|
||||||
|
fill(255, 0, 0, 100); // Transparent red
|
||||||
|
obstacles.forEach(pos => {
|
||||||
|
let [x, y] = pos.split(',').map(Number);
|
||||||
|
rect(x * cellSize, y * cellSize, cellSize, cellSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw guard
|
||||||
|
fill(0, 0, 255); // Blue
|
||||||
|
rect(guard.pos.x * cellSize, guard.pos.y * cellSize, cellSize, cellSize);
|
||||||
|
|
||||||
|
// Draw guard direction
|
||||||
|
fill(0);
|
||||||
|
textAlign(CENTER, CENTER);
|
||||||
|
text(
|
||||||
|
guard.direction === UP ? "^" :
|
||||||
|
guard.direction === DOWN ? "v" :
|
||||||
|
guard.direction === LEFT ? "<" : ">",
|
||||||
|
guard.pos.x * cellSize + cellSize/2,
|
||||||
|
guard.pos.y * cellSize + cellSize/2
|
||||||
|
);
|
||||||
|
}
|
141
viz2024-06-01.py
Normal file
141
viz2024-06-01.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Direction:
|
||||||
|
def __init__(self, pos_change_x, pos_change_y):
|
||||||
|
self.pos_change_x = pos_change_x
|
||||||
|
self.pos_change_y = pos_change_y
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, Direction):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.pos_change_x == other.pos_change_x and self.pos_change_y == other.pos_change_y
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.pos_change_x, self.pos_change_y))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Direction({self.pos_change_x}, {self.pos_change_y})"
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, Point):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.x, self.y))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Point({self.x}, {self.y})"
|
||||||
|
|
||||||
|
class Guard:
|
||||||
|
def __init__(self, pos, direction):
|
||||||
|
self.pos = pos
|
||||||
|
self.direction = direction
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Guard({self.pos}, {self.direction})"
|
||||||
|
|
||||||
|
def move(self, obstacles):
|
||||||
|
if self.direction == UP:
|
||||||
|
if (Point(self.pos.x, self.pos.y - 1) in obstacles):
|
||||||
|
self.direction = RIGHT
|
||||||
|
return Point(self.pos.x, self.pos.y - 1)
|
||||||
|
else:
|
||||||
|
self.pos.y -= 1
|
||||||
|
elif self.direction == DOWN:
|
||||||
|
if (Point(self.pos.x, self.pos.y + 1) in obstacles):
|
||||||
|
self.direction = LEFT
|
||||||
|
return Point(self.pos.x, self.pos.y + 1)
|
||||||
|
else:
|
||||||
|
self.pos.y += 1
|
||||||
|
elif self.direction == LEFT:
|
||||||
|
if (Point(self.pos.x - 1, self.pos.y) in obstacles):
|
||||||
|
self.direction = UP
|
||||||
|
return Point(self.pos.x - 1, self.pos.y)
|
||||||
|
else:
|
||||||
|
self.pos.x -= 1
|
||||||
|
elif self.direction == RIGHT:
|
||||||
|
if (Point(self.pos.x + 1, self.pos.y) in obstacles):
|
||||||
|
self.direction = DOWN
|
||||||
|
return Point(self.pos.x + 1, self.pos.y)
|
||||||
|
else:
|
||||||
|
self.pos.x += 1
|
||||||
|
|
||||||
|
def draw(m, visited_pos, obstacle_pos, guard):
|
||||||
|
print("\033c") # clear screen
|
||||||
|
|
||||||
|
# draw the guard in blue, the obstacles in red, and the path in yellow
|
||||||
|
for y in range(len(m)):
|
||||||
|
for x in range(len(m[y])):
|
||||||
|
if guard.pos.x == x and guard.pos.y == y:
|
||||||
|
print("\033[34m", end="")
|
||||||
|
if guard.direction == UP:
|
||||||
|
print("^", end="")
|
||||||
|
elif guard.direction == DOWN:
|
||||||
|
print("v", end="")
|
||||||
|
elif guard.direction == LEFT:
|
||||||
|
print("<", end="")
|
||||||
|
elif guard.direction == RIGHT:
|
||||||
|
print(">", end="")
|
||||||
|
print("\033[0m", end="")
|
||||||
|
else:
|
||||||
|
if Point(x, y) in visited_pos:
|
||||||
|
print("\033[33m", end="")
|
||||||
|
print(".", end="")
|
||||||
|
print("\033[0m", end="")
|
||||||
|
elif Point(x, y) in obstacle_pos:
|
||||||
|
print("\033[31m", end="")
|
||||||
|
print("#", end="")
|
||||||
|
print("\033[0m", end="")
|
||||||
|
else:
|
||||||
|
print(" ", end="")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
with open("./2024/inputs/day06.txt") as f:
|
||||||
|
m = [l.strip() for l in f.readlines()]
|
||||||
|
|
||||||
|
UP = Direction(0, -1)
|
||||||
|
DOWN = Direction(0, 1)
|
||||||
|
LEFT = Direction(-1, 0)
|
||||||
|
RIGHT = Direction(1, 0)
|
||||||
|
|
||||||
|
guard = None
|
||||||
|
obstacle_pos = set()
|
||||||
|
visited_pos = set()
|
||||||
|
|
||||||
|
for y in range(len(m)):
|
||||||
|
for x in range(len(m[y])):
|
||||||
|
if m[y][x] in ["^", "v", "<", ">"]:
|
||||||
|
guard_pos = Point(x, y)
|
||||||
|
|
||||||
|
if m[y][x] == "^":
|
||||||
|
guard_direction = UP
|
||||||
|
elif m[y][x] == "v":
|
||||||
|
guard_direction = DOWN
|
||||||
|
elif m[y][x] == "<":
|
||||||
|
guard_direction = LEFT
|
||||||
|
elif m[y][x] == ">":
|
||||||
|
guard_direction = RIGHT
|
||||||
|
|
||||||
|
guard = Guard(guard_pos, guard_direction)
|
||||||
|
|
||||||
|
elif m[y][x] == "#":
|
||||||
|
obstacle_pos.add(Point(x, y))
|
||||||
|
|
||||||
|
while guard.pos.x >= 0 and guard.pos.x < len(m[0]) and guard.pos.y >= 0 and guard.pos.y < len(m):
|
||||||
|
if guard.pos not in visited_pos:
|
||||||
|
visited_pos.add(guard.pos)
|
||||||
|
|
||||||
|
guard.move(obstacle_pos)
|
||||||
|
draw(m, visited_pos, obstacle_pos, guard)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
Loading…
Reference in a new issue