Commit graph

57 commits

Author SHA1 Message Date
c113042e17 Solve 2016:11 p1-2 "Radioisotope Thermoelectric Generators"
Hard one, an infamous AoC puzzle according to
Reddit.

Apparently, this is a classic logic problem named
"Missionaries and cannibals", or "Jealous husbands".

Hard lessons:

- Always use set() for visited nodes during BFS.
- Always use collections.queue() to create to queue
  traversals for BFS.
- itertools permutations(), combinations() and
pairwise() may look similar, but they are not.
- Learned to use (?:chunk of text)? in regex.

Test data was achieved without bigger hazzles, but
to optimize code required a lot of Reddit browsing
and code reading from blogs. I have mentioned the
sources in a doc string.
2024-12-12 13:53:43 +01:00
b1b5cda1e3 Solve 2016:12 p1-2 "Leonardo's Monorail" 2024-12-12 13:53:43 +01:00
8d4af7e6e9 Solve 2016:13 p1-2 "A Maze of Twisty Little Cubicles"
BFS baby.
2024-12-12 13:53:43 +01:00
c46c2c5126 Run code in container 2024-12-03 01:41:40 +01:00
2d51468eb1 Add leaderboard http service 2024-12-03 01:41:40 +01:00
dd756cb9eb
Update README.md 2024-11-29 11:58:57 +01:00
28af87c00b Add the initial 2015 days in Python
These are already done in Elixir, so this is
just done for the flex.

Also, coming from Day 16-18 from 2023 calendar,
it is safe to say 2015 puzzles are easier and more
manageable.
2023-12-19 23:55:47 +01:00
a48f3608b2 Cleanup 2015 solutions 2023-12-19 14:47:50 +01:00
5a5e843129
2015, day 16-25 (first completed calendar!) (#11)
* Solve 2015:16 "Aunt Sue"

* Make 2023:08 future compatible

Code used to work with another version of python.

* Solve 2015:17 "No such Thing as Too much"

* Solve 2015:18 "Like a GIF For Your Yard"

Also solve 2015:06 just in case, was just a ref
in the end.

* Solve 2015:19 "Medicine for Rudolph"

* Solve 2015:20 "Infinite Elves and Infinite Houses"

* Solve 2023:21 "RPG Simulator 20XX"

* Solve 2015:22 "Wizard Simulator 20XX"

* Solve 2015:23 "Opening the Turing Lock"

* Solve 2015:25 "Let it Snow"

Wrote p2rc and rc2p just for academic purposes.
Puzzles could be solved anyway.

* Solve 2015:24 "Hangs in the Balance"

---------

Co-authored-by: Anders Englöf Ytterström <anders@playmaker.ai>
2023-12-19 14:24:28 +01:00
b6e048e4a7
2023 solutions (#6)
* Prep Advent of Code 2023

* Solve 2023:01 "Trebuchet?!"

Turns out re methods are non-overlapping. And in
true AoC manners, no provided test cases had
overlaps.

Luckily for me, some of the last lines in the input
contained the string "oneight", so I was able to
find it out quite fast.

Revisions:

1) Reverse strings to find last digit
2) Use isdigit() and skip regex.
3) Use regexp with positive look-ahead.

* Solve 2023:02 "Cube Conundrum"

Very intermediate pythonic solution,
regex would have made the code more compact.

But since 2023:01 decreased the regex courage,
This code will do.

* Solve 2023:03 "Gear Ratios"

Overslept, took about 55 mins.

* Solve 2023:04 "Scratchcards"

On a train that according to swedish tradition
was late. Not a good environment to focus.

Got stuck 2 times:

- Initial code asumed the | was always after the 5th
  number, because of the example. Puzzle input had
  it at pos 10. Classic AoC mistake.
- I had a hard time trying to understand the score
  count, I insisted there was meant to be a +1 at
  some point.

  > That means card 1 is worth 8 points (1 for
  > the first match, then doubled three times for
  > each of the three matches after the first)

I should instead have just looked at the numbers.

* Solve 2023:05 "If You Give A Seed A Fertilizer"

Part 2 takes 66 minutes to run. There is some smart
things to realize here.

* Solve 2023:06 "Wait for it"

* Solve 2023:07 "Camel Cards"

* Solve 2023:08 "Haunted Wasteland"

Part 2 would have taken 10-15 hours with brute force.

After I figured out the puzzle input had circular
A-Z paths, it was plain as day that LCM was the
solution to the problem.

https://en.wikipedia.org/wiki/Least_common_multiple

* Solve 2023:09 "Mirage Maintenance"

* Remove parse_input helper

* Refactor 2023:05

Increasing speed from 66mins to 4mins. Caching the
location value in the code to keep things at highest
speed.

From the subreddit, the algorithm looks like this.

1. Start att location 0
2. Traverse the whole process backwards, by
   reversing process steps and flipping dest/src
   positions.
3. Output is not a location, instead it's a seed.
4. if seed is in any seed range, use seed to get
   location as in step 1.
5. If not, increase location by 1 and repeat 2-4.

* Solve 2023:10 "Pipe Maze"

Got completely stuck on part 2. Tried some polygon
area calculations, but none provided the correct
answer, most likely due to the unorthodox polygon
points.

I also tried _shoelace method_ without any luck.
Had not heard about that one earlier, it was a good
learning experience even though I vould not use it
here.

By the subreddit, several people had had luck
using the ray method.

> Part 2 using one of my favorite facts from
> graphics engineering: lets say you have an
> enclosed shape, and you want to color every
> pixel inside of it. How do you know if a given
> pixel is inside the shape or not? Well, it
> turns out: if you shoot a ray in any direction
> from the pixel and it crosses the boundary an
> _odd number_ of times, it's _inside_. if it crosses
> an even number of times, it's outside. Works
> for all enclosed shapes, even self-intersecting
> and non-convex ones.

* Fix flake8 errors for 2023:1-10

* Solve 2023:11 "Cosmic Expansion"

* Solve 2023:12 "Hot Springs"

* Solve 2023:13 "Point of Incidence"

* Solve 2023:14 "Parabolic Reflector Dish"

* Solve 2023:15 "Lens Library"

WALLOFTEXT for part 2, took me 90 minutes to find
this important text:

> Each step begins with a sequence of letters that
> indicate the label of the lens on which the step
> operates. The result of running the HASH algorithm
> on the label indicates the correct box for that
> step.

It also clarifies how part 2 and part 1 relates.

* Solve 2023:16 "The Floor Will Be Lava"

---------

Co-authored-by: Anders Englöf Ytterström <anders@playmaker.ai>
2023-12-18 13:08:28 +01:00
a153d0e09e Ignore .ropeproject 2023-12-15 17:22:09 +01:00
5e8eebb952 Ignore input files 2023-12-09 21:25:35 +01:00
afeae6623d Add scripts to fetch single or calendar inputs 2023-12-09 21:25:35 +01:00
fb5a1b9381 Solve 2015:15 "Science for Hungry People" 2023-11-20 20:54:43 +01:00
cfd2c0f079 Solve 2015:14 "Reindeer Olympics" 2023-11-20 20:54:43 +01:00
e859324030 Solve 2015:13 "Knights of the Dinner Table" 2023-11-20 20:54:43 +01:00
6a1b231552 Solve 2015:12 "JSAbacusFramework.io" 2023-11-20 20:54:43 +01:00
761d00b552 Cleanup aoc runner
- Remove deleted twitter handle
- Correct year
- Add new surname
2023-11-20 20:54:43 +01:00
838d06100b Solve 2015:11 "Corporate Policy"
I brainfarted and had a hard time trying to
understand the instructions.

> Incrementing is just like counting with numbers: xx, xy, xz, ya, yb, and so on. Increase the rightmost letter one step; if it was z, it wraps around to a, and repeat with the next letter to the left until one doesn't wrap around.

I only managed to understand it by looking at solutions on the
subreddit, figuring out the correct behavior:

az -> ba, azzz -> baaa, azzzzz -> baaaaa etc.

I also sped up the test case containing `ghi` as initial password,
by looking for the leftmost invalid I, L or O and increase it,
replacing all following chars with `a`.

ghijklmn -> ghjaaaaa.
2023-11-20 20:54:43 +01:00
4742910afd Solve 2015:10 "Elves Look, Elves Say" 2023-11-20 20:54:43 +01:00
84d33a8b26 Remove not started 2015:6 2023-11-20 20:54:43 +01:00
8f5d73b4bf Solve 2015:9 "All in a Single Night" 2023-11-20 20:54:43 +01:00
5aba2fbed8 Solve 2015:8 "Machinesticks" 2023-11-20 20:54:43 +01:00
2dff9463ed Ignore __pycache__ 2023-11-20 20:54:43 +01:00
db91df6cfc Solve 2015:7 "Some Assembly Required"
With all new bootstrap script
2023-11-20 20:54:43 +01:00
102982a6f0 Add WIP for 2022:11 "Monkey in the Middle" 2023-11-20 20:53:58 +01:00
4420dfa522 Add solutions for 2022:10 "Cathode-Ray Tube" 2023-11-20 20:53:58 +01:00
e2da7dc954 Add WIP solution for 2022:9 "Rope Bridge" 2023-11-20 20:53:58 +01:00
c864b26770 Add solutions for 2022:8 "Treetop Tree House" 2023-11-20 20:53:58 +01:00
bb708a5e58 Add solutions for 2022:7 "No Space Left On Device"
tried to solve this one using only incrementing sums, which
worked fine for the test input but not the actual puzzle input.

By a complete rewrite to actually render the tree as a map,
it worked. The spontanious data store is a mess and cost me
much time to work around, since I wanted to have a list of tuples
with all the sizes for each directory.

Took 2 days to figure this one out. Not proud.
2023-11-20 20:53:58 +01:00
67ff3997f1 Add solutions for 2022:6 Tuning Trouble 2023-11-20 20:53:58 +01:00
c98303d0c1 Add solutions for 2022:4-5 2023-11-20 20:53:58 +01:00
93d014893f Add solutions for 2022:3 Rucksack Reorganization
Cleaned up version. First one was a bit more spontanious.
2023-11-20 20:53:58 +01:00
df5cfbd030 Add solutions for 2022:2 Rock Paper Scissors
Verbose code, and with bad naming. Good enough for now.
2023-11-20 20:53:58 +01:00
fddb9ec042 Add solutions for 2022:1 Calorie Counting
Lost a couple of minutes due to Elixir lang server in my editor fucked up.
2023-11-20 20:53:58 +01:00
5929ae11f6 Prepare for Advent of Code 2022 2023-11-20 20:53:58 +01:00
Anders Ytterström
5ed6e5e8ce Solve day 5-6 2015 2022-11-25 12:03:57 +01:00
93c3322770 Solve 2021:14 "Extended Polymerization" part 2
Solved by total rewrite, since part 1 solution was too slow and was
killed by the OS. I tried to use deque and prepending instead of
appending, but no luck.

I looked for inspiration on AOC subreddit, and realized I could
solve the puzzle without constructing a new polymer string.

Instead, **it should be solved by counting pairs**.

First of all, a little zip trick is used to construct an initial list of
pairs from the initial polymer.

collections.defaultdicts are excellent tools for storing the counts,
since they will default to a value of 0 for any key that is added.

the initial count of the initial polymer is this:

    {
        ('N', 'N'): 1,
        ('N', 'C'): 1,
        ('C', 'B'): 1
    }

And the initial character count looks like this, and was constructed by
collections.Counter. This is what we increase and use after the 40 steps.

    {
        'N': 2,
        'C': 1,
        'B': 1
    }

So, lets see what happens after the first step.

First of all, all existing pair counts should be reset each step, since
this will be faster. nxt is used to store the next step pairs.

the current pairs are iterated.

 - for NN, the new pairs NC and CN are added for the next step. Since NN exists
   1 time, the pairs NC and CN also will exist exactly 1 more time each. The char C will also
     exist 1 more time, so add it to the char counter.
 - for NC, the new pairs NB and BC are added for the next step. Pair
   counter is once again synced with NC, and B is added to the char
counter.
 - and the same for CB, where CH and HB is added.

The count looks like this after step 1.

    {
        ('N', 'C'): 1,
        ('C', 'N'): 1,
        ('N', 'B'): 1,
        ('B', 'C'): 1,
        ('C', 'H'): 1,
        ('H', 'B'): 1,
    }

The pair counts are all 1 for step 2, since **no pairs have reoccured yet**.

The char counts looks like this, and matches polymer 'NCNBCHB', even
though it does not matter in which order the chars are written:

    {
        'N': 2,
        'C': 2,
        'B': 2,
        'H': 1
    }

Lets see the counts for step 2.

    {
        ('N', 'B'): 2,
        ('B', 'C'): 2,
        ('C', 'C'): 1,
        ('C', 'N'): 1,
        ('B', 'B'): 2,
        ('C', 'B'): 2,
        ('B', 'H'): 1,
        ('H', 'C'): 1
    }

Polymer NBCCNBBBCBHCB are present in the char count:

    {
        'N': 2,
        'C': 4,
        'B': 6,
        'H': 1
    }

Here some pairs have started to reoccour, and the char counts have
increased. So it will continue up to 40 steps, smooth and fast.

It works, since the char count are increased to match the polymer, and
it is fast, since the program does not care about the order of the
chars.
2021-12-14 08:56:57 +01:00
c54b92ebc9 Solve 2021:14 "Extended Polymerization" part 1
Solves 10 fine, but is sigkilled when running 40. refactoring needed.
2021-12-14 07:03:44 +01:00
89299cefa1 Solve 2021:13 "Transparent Origami"
finally sub 60 minutes again.
2021-12-13 07:09:56 +01:00
c0d352cdf1 Solve 2021:12 "Passage Pathing"
I struggled a lot with the direction, but realised way too much later
that the mappings are bidirectional.

Part 2 was exceptionally hard, but I realised early that the order did
not matter and that a simple boolean lock was all that was needed. The
challenge was to know when to watch for duplicates.

The seen was initially a set, but since new sets must be sent instead of
references, a list is used instead to be able to use the seen + [] hack.
2021-12-12 14:39:11 +01:00
b53c50addc Solve 2021:11 "Dumbo Octopus" 2021-12-11 09:36:09 +01:00
801f977e92 Solve 2021:10 "Syntax Scoring"
Spent over an hour trying to figure out the incomplete sequenses, only
to realize it was easier to begin from the other way around.

After that, solution came out nice and clean.
2021-12-10 13:27:55 +01:00
95c244102f Solve 2021:9 "Smoke Basin" part 2
Yet again I got stuck in the common AOC trap: The example input worked,
but not the actual input.

2 things got me stuck.

> The size of a basin is the number of locations within the basin, including the low point. The example above has four basins.

1. I missed the obvious part to only check the low points.

2. Based on the example, I asumed that the adjacent locations would
   increase by one to count. This is wrong: What matters is that their
   height is a larger value.

Way too long time for a simple problem. Never read puzzles sloppy.
2021-12-09 11:00:28 +01:00
c7753f58fc Solve 2021:9 "Smoke Basin"
Had to rewrite whole solution due to getting the example input right,
but wrong on the actual puzzle input. Off by like 20.
2021-12-09 07:39:53 +01:00
811d2cb224 Solve 2021:8 "Seven Segment Search"
For part 1, it took way longer than necessary since I read the
instructions over and over again. Gotta love AOC WOT.

Part 2 was a PITA. I got stuck at the last 2 examples since I at the
time did not find any clever way to determine which of the 2 outputs for
'1' mapped to c and f.

Since I already spent far too much time and effort, a try/except with
ValueError (digits.index will fail if c and f are flipped wrong) will do
for now.

There is probably a more efficient and smart way to do this.
2021-12-08 11:35:18 +01:00
69c4518f29 Solve 2021:7 "The Treachery of Whales" 2021-12-07 09:47:54 +01:00
bfe94864e9 Solve 2021:6 "Lanternfish" part 2
Part 2 requires more clever code. In the first part, I kept track on
every single fish on order, much like the output of the example in the
puzzle.

However, by increasing the day count to 256, my code became obese and
would not run to the end: the OS killed it before it was done. It halted
around 150 days.

Browsing the sub reddit, I quickly realised that the current state of
each fish individually did not matter. For example:

    3,4,3,1,2

This says 2 fishes has a timer set to 3, and 1 fish each has a timer of
1, 2 and 4.

Add one day, and we have

    2,3,2,0,1

This says 2 fishes has a timer set to 2, and 1 fish each has a timer of
0, 1 and 3.

Notice how **the relative count does not change** (2, 1, 1, 1).

By using the excellent collections.counter, you get

    >>> from collections import Counter
    >>> Counter([3,4,3,1,2])
    Counter({3: 2, 4: 1, 1: 1, 2: 1})
    >>> Counter([2,3,2,0,1])
    Counter({2: 2, 3: 1, 0: 1, 1: 1})
    >>>

If visualized in a tabulat view, you get this:

                   0  1  2  3  4  5  6
    ----------------------------------
    Initial           1  1  2  1
    Day 1          1  1  2  1

The sequence moves **one step to the left each day**. Add another day,
and something extra happens.

                   0  1  2  3  4  5  6  7  8
    ----------------------------------------
    Initial           1  1  2  1
    Day 1          1  1  2  1
    Day 2          1  2  1           1     1

2 things:

 * A value higher than zero pops out on the left. By rules, 1 fish
   has created 1 new fish, and resets its timer to 6.
 * The new fish created has a timer of 8, since it will need 2 days
   before it is able to start the create timer.

There are now 6 fishes.

Now, what happens on day 3?

                   0  1  2  3  4  5  6  7  8
    ----------------------------------------
    Initial           1  1  2  1
    Day 1          1  1  2  1
    Day 2          1  2  1           1     1
    Day 3          2  1           1  1  1  1

Now this happened:

 * The fish on 0 creates a new fish, resets to 6.
 * The created fish, once again, has a timer of 8.
 * The fish created on day 2 decreases timer to 7.

There are now 7 fishes.

And so it continues. In Python, the most memory efficient and performant
method of leftpadding a list of values is to use collections.deque, with
the functions popleft and append.
2021-12-06 07:49:46 +01:00
2f3f892c0e Solve 2021:6 "Lanternfish" part 1 2021-12-06 07:15:37 +01:00
f82b6cd9b5 Solve 2021:5 "Hydrothermal Venture"
Tricky part, got the test data correct at part 1 long before the actual input gace the correct answers. Pure luck I guess.

Part 2 I got stuck on, and resolved to visualization by a loop of
prints.
2021-12-05 08:30:54 +01:00