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