Shawn Blanc’s favourite novels
Ooh another Andy Weir fan. I might have to check out some of the other books on his list…
Since 2010
Ooh another Andy Weir fan. I might have to check out some of the other books on his list…
A little update on my rebuild of zerosleeps.com:
Transform
in Django to avoid storing post title and slug, but safer (and more flexible) to just add a slug field to my Post model.TemplateView
.Oh and I’m using Bulma for the whole lot - I am not a designer!
Once I get post URLs sorted I reckon I’ll “go live” as they say in the business. If I don’t then I’ll never deploy the thing. Everything else can follow later: search, tags and categories (maybe?), visitor statistics, JSON and XML feeds, maybe a sitemap. There’s more to a stupid blog than you’d think.
I think I’m done with Advent of Code for the year. I enjoyed the first couple of weeks: the problems were all distinct from each other and had well defined solutions so - for my taste/abilities - were largely about crafting pleasant code that is totally different to the kind of stuff I work on 09:00–17:00.
I could solve these puzzles in less than an hour, too. Perfect little brain-tickers.
But days 15 (for which I have a solution that works for the example but not my actual input), 16, and 17 are all way too computer science/mathematics for me. Pattern matching and algorithms and path-finding - they’re just not my thing.
And that’s okay! Of course I could teach myself the tools and tricks Advent of Code is so great at hinting at, I just don’t want to! I’ve got enough life experience and introspection to know what I enjoy and what I don’t enjoy. This is something I chose to do in my spare time, it stopped making me happy, and it wasn’t helping me to improve the personal traits I care about.
Let it be said, whatever your reasons for partaking, Advent of Code is magnificently well done. The guy behind it is a legend.
Now… I should probably set aside an hour each day for “Advent of fixing my damned website” 😬
Completed day 14 today as well. It’s hideously inefficient but the code fits my brain, so adapting it for part 2 was nice and quick and worked first time 🥳
from itertools import pairwise
from unittest import TestCase
from utils import get_raw_input
class Cave:
def __init__(self, raw_input):
self.build(raw_input)
self.sand = set()
self.overflowing = False
def plot_rocks(self, x1, y1, x2, y2):
if y1 == y2:
return [(i, y1) for i in range(min([x1, x2]), max([x1, x2]) + 1)]
else:
return [(x1, i) for i in range(min([y1, y2]), max([y1, y2]) + 1)]
def build(self, raw_input):
self.rocks = set()
for line in raw_input:
for pair in pairwise(line.split(" -> ")):
self.rocks.update(
self.plot_rocks(
int(pair[0].split(",")[0]),
int(pair[0].split(",")[1]),
int(pair[1].split(",")[0]),
int(pair[1].split(",")[1]),
)
)
@property
def boundaries(self):
# y_min is highest rock, y_max is lowest rock
return {
"x_min": min([rock[0] for rock in self.rocks]),
"x_max": max([rock[0] for rock in self.rocks]),
"y_min": min([rock[1] for rock in self.rocks]),
"y_max": max([rock[1] for rock in self.rocks]),
}
def drop_sand(self, floor=False):
sand_position = (500, 0)
while True:
if (
not floor
and (
sand_position[1] > self.boundaries["y_max"]
or sand_position[0] < self.boundaries["x_min"]
or sand_position[0] > self.boundaries["x_max"]
)
) or (floor and (500, 0) in self.sand):
# Out of bounds
self.overflowing = True
return
elif (
(sand_position[0], sand_position[1] + 1) not in self.rocks
and (sand_position[0], sand_position[1] + 1) not in self.sand
and (
not floor
or (floor and sand_position[1] + 1 < self.boundaries["y_max"] + 2)
)
):
# Can fall straight down
sand_position = (sand_position[0], sand_position[1] + 1)
elif (
(sand_position[0] - 1, sand_position[1] + 1) not in self.rocks
and (sand_position[0] - 1, sand_position[1] + 1) not in self.sand
and (
(not floor)
or (floor and sand_position[1] + 1 < self.boundaries["y_max"] + 2)
)
):
# Can fall down-and-left
sand_position = (sand_position[0] - 1, sand_position[1] + 1)
elif (
(sand_position[0] + 1, sand_position[1] + 1) not in self.rocks
and (sand_position[0] + 1, sand_position[1] + 1) not in self.sand
and (
(not floor)
or (floor and sand_position[1] + 1 < self.boundaries["y_max"] + 2)
)
):
# Can fall down-and-right
sand_position = (sand_position[0] + 1, sand_position[1] + 1)
else:
# Can't move
self.sand.add(sand_position)
return
class TestExamples(TestCase):
def setUp(self):
self.example_input = [
"498,4 -> 498,6 -> 496,6",
"503,4 -> 502,4 -> 502,9 -> 494,9",
]
self.cave = Cave(self.example_input)
def test_part_one(self):
self.assertEqual(run(self.cave), 24)
def test_part_two(self):
self.assertEqual(run(self.cave, True), 93)
def run(cave, floor=False):
while not cave.overflowing:
cave.drop_sand(floor)
return len(cave.sand)
if __name__ == "__main__":
print(f"Part one: {run(Cave(get_raw_input('day_14_input.txt')))}")
print(f"Part two: {run(Cave(get_raw_input('day_14_input.txt')), True)}")
I figured out my problem: I wasn’t correctly handing the scenario where a list had been exhausted with no result. I was incorrectly assuming that when either side of the list had been exhausted, a result must have been obtained.
Final code:
from functools import cmp_to_key
from utils import get_raw_input_as_str
def parse_raw_input(raw_input):
return [[eval(packet) for packet in pair.splitlines()] for pair in raw_input.split("\n\n")]
def compare(left, right):
if type(left) == type(right) == list:
result = None
i = 0
while result is None:
if len(left) != len(right) and len(left) <= i:
return 1
elif len(left) != len(right) and len(right) <= i:
return -1
elif len(left) == i and len(right) == i:
return None
else:
result = compare(left[i], right[i])
i += 1
return result
elif type(left) == type(right) == int:
if left < right:
return 1
elif left > right:
return -1
else:
return None
elif type(left) == int and type(right) != int:
return compare([left], right)
elif type(right) == int and type(left) != int:
return compare(left, [right])
else:
raise Exception
def part_one(input):
return sum([i + 1 for i, pair in enumerate(input) if compare(pair[0], pair[1]) == 1])
def part_two(input):
flattened_input = [packet for pair in input for packet in pair] + [[[2]]] + [[[6]]]
sorted_packets = sorted(flattened_input, key=cmp_to_key(compare), reverse=True)
return (sorted_packets.index([[2]]) + 1) * (sorted_packets.index([[6]]) + 1)
if __name__ == '__main__':
print(f"Part one: {part_one(parse_raw_input(get_raw_input_as_str('day_13_input.txt')))}")
print(f"Part one: {part_two(parse_raw_input(get_raw_input_as_str('day_13_input.txt')))}")