-
Notifications
You must be signed in to change notification settings - Fork 0
/
day17.py
137 lines (115 loc) · 3.77 KB
/
day17.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from pathlib import Path
from dataclasses import dataclass
from itertools import cycle
movements = Path("input17.txt").read_text().strip()
shapes = [
["@@@@"],
[".@.", "@@@", ".@."],
["..@", "..@", "@@@"],
["@", "@", "@", "@"],
["@@", "@@"],
]
start_positions = [()]
def shape_len(shape: list[str]):
return max([s.rindex("@") for s in shape]) + 1
@dataclass
class Sprite:
x: int
y: int
shape: list[str]
class Game:
board = []
current_sprite: Sprite | None
def __init__(self):
self.board = []
self.current_sprite = None
def freepos_y(self):
for i, row in enumerate(self.board):
if "#" in row or "-" in row:
return i - 1
return len(self.board) - 1
def place_item(self, shape: list[str]):
if not self.board or self.freepos_y() - len(shape) < 10: # Ensure enough space
self.board = [" "] * 7 + self.board # extend board
y = self.freepos_y() - 2 - len(shape)
x = 2
self.current_sprite = Sprite(x, y, shape)
def push(self, s: str):
match s:
case "<":
if self.current_sprite.x > 0:
self.current_sprite.x -= 1
if self.colides():
self.current_sprite.x += 1 # revert
case ">":
if self.current_sprite.x + shape_len(self.current_sprite.shape) < 7:
self.current_sprite.x += 1
if self.colides():
self.current_sprite.x -= 1 # revert
case _:
raise ValueError(f"Invalid movement {s}")
def colides(self) -> bool:
s = self.current_sprite
for y in range(0, len(s.shape)):
if y + s.y >= len(self.board):
return True
for x in range(len(s.shape[y])):
if s.shape[y][x] == "@" and self.board[y + s.y][x + s.x] == "#":
return True
return False
def freeze(self):
s = self.current_sprite
for y in range(0, len(s.shape)):
self.board[y + s.y] = (
self.board[y + s.y][: s.x]
+ "".join(
[
"#" if s == "@" else b
for b, s in zip(self.board[y + s.y][s.x :], s.shape[y])
]
)
+ self.board[y + s.y][s.x + len(s.shape[y]) :]
)
self.current_sprite = None
def fall(self) -> bool:
self.current_sprite.y += 1
if self.colides():
self.current_sprite.y -= 1 # revert
self.freeze()
return False
return True
def height(self) -> int:
return len(self.board) - self.freepos_y() - 1
def __str__(self):
merged_rows = []
for y, row in enumerate(self.board):
s = self.current_sprite
if s and y in range(s.y, s.y + len(s.shape)):
current_line = (
self.board[y][: s.x]
+ "".join(
[
b if s == "." else s
for (b, s) in zip(self.board[y][s.x :], s.shape[y - s.y])
]
)
+ self.board[y][s.x + len(s.shape[y - s.y]) :]
)
else:
current_line = self.board[y]
merged_rows.append(current_line)
return "\n".join(merged_rows)
g = Game()
cycled_shapes = cycle(shapes)
i = 0
for move in cycle(movements):
if not g.current_sprite:
shape = next(cycled_shapes)
if i == 2022:
break
i += 1
g.place_item(shape)
g.push(move)
g.fall()
part1 = g.height()
print(f"Part1: {part1}")