I
such the sum difference of each number and I is minimized.Okay. Start with the data structures:
@dataclass(frozen=True)
class Point:
x: int
y: int
@dataclass(frozen=True)
class Line:
p1: Point
p2: Point
def is_vertical(self):
return self.p1.x == self.p2.x
def is_horizontal(self):
return self.p1.y == self.p2.y
def is_orthagonal(self):
return self.is_vertical() or self.is_horizontal()
def points(self):
# TODO: handle lines that aren't vertical, horizontal, or diagonal
xd = 0 if self.p1.x == self.p2.x else (1 if self.p1.x < self.p2.x else -1)
yd = 0 if self.p1.y == self.p2.y else (1 if self.p1.y < self.p2.y else -1)
p = self.p1
while p != self.p2:
yield p
p = Point(p.x + xd, p.y + yd)
yield p
Dataclasses are great. They give you constructors and a bunch of other things for free. On top of that, if you specify frozen=True
, making them immutable, you also get hashable
types for free (which I’ll use in the problem).
Perhaps the most interesting bit here is the function that will iterate through the points
in a List
. Specifically, it will figure out the x and y delta (xd
and yd
) and repeatedly add that until you hit the end point.
Note: this only works for lines that are vertical, horizontal, or diagonal (at 45 degrees). Anything else needs a better line drawing algorithm (of which there are a few). If we need it, I’ll implement it.
Next, use that to parse:
def parse(file: TextIO) -> List[Line]:
result = []
for line in file:
x1, y1, x2, y2 = [int(v) for v in line.replace(' -> ', ',').split(',')]
result.append(Line(Point(x1, y1), Point(x2, y2)))
return result
The input format is x1,y1 -> x2,y2
, but it’s easier to split and convert if we do it all directly. There are a few other ways we could have done this: splitting on anything non-numeric or using a regular expression / something else for parsing directly. But I think this is clear enough.
And with all that, the problem is actually pretty short:
def part1(file: typer.FileText):
lines = parse(file)
counter: MutableMapping[Point, int] = collections.Counter()
for line in lines:
if not line.is_orthagonal():
continue
for point in line.points():
counter[point] += 1
print(sum(1 if count > 1 else 0 for point, count in counter.items()))
We’ll use the built in collections.Counter
datatype, since that’s exactly what we’re doing: counting things. Then just iterate over every line, skip the non-orthagonal ones, iterate over every point, and count them up. At the end, print the number that we saw more than once. Et voila.
$ python3 linear-avoidinator.py part1 input.txt
5632
forward N
, down N
, and up N
that move forward, increase depth, and decrease depth in that order. Calculate the product of the final position and depth.Been a while since I’ve done an advent of code! I’ll probably backfill a few years eventually, but for now, let’s just write some code!
As always, these problems are wonderful to try to solve yourself. If you agree, stop reading now. This post isn’t going anywhere.
If you’d like to see the full form of any particular solution, you can do so on GitHub (including previous years and possibly some I haven’t written up yet): jpverkamp/advent-of-code