Day 12: Garden Groups
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
27 Comments
mykl@lemmy.world · 5 pts · 1y
Uiua
Takes about 3 seconds to solve both parts for live data, caused primarily by my terrible fill function inFieldCoordswhich repeatedly refills and dedups already discovered cells. I promised myself when I wrote it that I would revisit it, but I really can't be bothered right now. Sorry Kai.LATE EDIT: Thanks to Quant for the inspiration to revisit this. With his code snippet and the realisation that I should normalise all fields to remove wasted space, runtime is now down to 55ms.
Quant@programming.dev · 2 pts · 1y
I found multidimensional markers for partition to work really well for finding the fields:
Areas β ββ‘:β‘β³.+1βββIt just groups the other array's contents according to adjacent markers, horizontally and vertically. Took me quite a bit to figure out what's actually happening in the example in the documentation ^^'mykl@lemmy.world · 2 pts · 1y
Ooh, interesting, I'll have to give that a try. Thanks!
(edit) Wow, that replaced my three lines of overly complex code without a hitch.
classifyis an operator I never really got the point of before. Beautiful.Quant@programming.dev · 2 pts · 1y
Nice :D
How's the speed now?
mykl@lemmy.world · 2 pts · 1y
1.8s now. 99% of that in
Sides. I've just had an idea though...maybe too late for today though!edit: prepending
β‘β(-Β€βΈ/β§)toFieldsspared me from manipulating hundreds of irrelevant 0's, so time is very good now at 55ms.Quant@programming.dev · 2 pts · 1y
Damn that's a lot time saved. I love how unassuming the addition looks for how great an effect it has
mykl@lemmy.world · 1 pts · 1y
It was a real D'oh! moment when I visualised the data I was generating and saw all the zeros stretching across the page.
sjmulder@lemmy.sdf.org · 4 pts · 1y
C
No big trouble today, just a bit of careful debugging of my part 2 logic for which I was greatly helped by some Redditorβs testcase π
Happy to have gotten it all in the single flood fill function without any extra passes.
::: spoiler Code
:::
https://github.com/sjmulder/aoc/blob/master/2024/c/day12.c
some_random_nick@lemmy.world · 3 pts · 1y
Clean and concise. Admirable!
lwhjp@lemmy.sdf.org · 2 pts · 1y
Woah! That solution is a work of art!
mykl@lemmy.world · 3 pts · 1y
Dart
Filling to find regions was easy. Counting areas was easy. Counting fences was okay. Counting sides caused me a lot of frustration as I tried and rejected a number of approaches, eventually arriving at a reasonably simple corner-counting approach. None of this was helped by all the examples lacking at least two important layouts, causing today to be the first day that I ran out of hints for wrong answers :-(.
(
cornersis where the magic happens)::: spoiler 70 or so lines, half a second to run, so that's fine for today.
:::
janAkali@lemmy.one · 3 pts · 1y
Nim
Runtime:
7ms3.18 msPart 1: I use flood fill to count all grouped plants and keep track of each border I see.
Part 2: I use an algorithm
similar to "merge overlapping ranges"to count spans of borders (border orientation matters) in each row and column, for each group. Resulting code (hidden under spoiler) is a little messy and not very DRY (it's completely soaked).Edit: refactored solution, removed some very stupid code.
::: spoiler
proc groupSpans():::
Codeberg repo
cabhan@discuss.tchncs.de · 3 pts · 1y
Rust
I essentially used flood fill to collect each region. Part 1 was then relatively easy: for each point, check how many neighbors are outside of the region.
Part 2 took me forever, and I ended up looking for hints online, where I discovered that an easy way to count the number of sides is to instead count the number of corners. Doing this for "normal" corners (e.g. in a square) was relatively easy, but "reverse corners" took me a long time. Corners like here what we see in the NE corner of the first
Cin the third row here:I'm more or less happy with my solution, but my brain is now totally fried.
https://gitlab.com/bricka/advent-of-code-2024-rust/-/blob/main/src/days/day12.rs?ref_type=heads
Quant@programming.dev · 1 pts · 1y
Counting the number of corners was a very useful hint for part 2. I had the most trouble with detecting the double corners, i.e. like in the example where the two B fields touch diagonally:
Still, I would've taken a lot longer and probably made really-bad-performance-code without reading this :D
hades@lemm.ee · 3 pts · 1y
lwhjp@lemmy.sdf.org · 2 pts · 1y
Haskell
This was a bit of a fiddly one. There's probably scope for golfing it down some more, but I've had enough for today :3
::: spoiler Solution
:::
VegOwOtenks@lemmy.world · 2 pts · 1y
Thank you for showing the floodfill-algorithm using explored/open sets, mine was hellish inefficiently, reminds me of A*.
VegOwOtenks@lemmy.world · 2 pts · 1y
Haskell
Detecting regions is a floodfill. For Part 2, I select all adjacent tiles that are not part of a region and group them by the direction relative to the closest region tile, then group adjacent tiles with the same direction again and count.
Edit:
Takes 0.06s
::: spoiler Reveal Code
:::
ace@lemmy.ananace.dev · 2 pts · 1y
Ended up oversleeping somewhat, so I did the first part on the way to work using flood fills over a global visited set, and now that work's over I've sat down to expand that solution to do corner counting for part two as well.
::: spoiler C#
:::
Pyro@programming.dev · 2 pts · 1y
Python
Part 1: Simple DFS counting up the cells and exposed edges
Part 2: Still DFS, however I chose to keep track of all segments of the area, merging them if two segments connected. In the end, number of non-overlapping, non-intersecting segments is equal to number of sides. Not the most efficient solution, but it works.
SteveDinn@lemmy.ca · 2 pts · 1y
C#
There is probably a more efficient way of finding the sides, but this way was what came to me.
hades@lemm.ee · 1 pts · 1y
SteveDinn@lemmy.ca · 1 pts · 1y
It's a simple record type I use for (x,y) coordinate problems:
record struct Point(int X, int Y);It's defined in a separate project containing things I use in multiple problems.
Maybe I could have done it that way, but this was the first thing I thought of, and it worked :)
Gobbel2000@programming.dev · 2 pts · 1y
Rust
Areas are found by flooding, in the meantime whenever the adjacent plot would be outside the region (or out of bounds) the edge (inside plot, outside plot) is saved in a perimeter list. Part 1 takes just the size of that list, in part 2 we remove fence parts and all entries directly next to it on both sides.
::: spoiler Solution
:::
Also on github
iAvicenna@lemmy.world · 2 pts · 1y
Python
Had to rely on an external polygon library for this one. Part 1 could have been easily done without it but part 2 would be diffucult (you can even use the simplify function to count the number of straight edges in internal and external boundaries modulo checking the collinearity of the start and end of the boundary)
RagingHungryPanda@lemm.ee · 2 pts · 1y
I know I'm late, but it's still fun and I'm sure no-one will see this.
Part 2 took me way too long to get right. I was initially only returning the relative point to which a plot needed a fence. I ran into issues of knowing if it was a valid fence or not by my method of counting (later). I eventually went with returning a tuple of the plot and an enum flag of the sides that it has fences on.
For counting I grouped the points by one axis then sorted on the other and counted the number of times the transition between two wasn't contiguous.
It could by done in parallel, but the original traversal would need de-duping, which I didn't feel like doing. After that things are done on a region basis, which could be parallel.
I also can't help but notice mine is by far the longest ( > . < )
F#
::: spoiler Tap for spoiler
:::
CameronDev@programming.dev · 2 pts · 1y
I saw it :)
If I understand your approach for pt2, you are getting all the fences and then grouping the connected ones? That definitely seems like a harder approach. I went with the counting corner method, which was also hard, but less iterating required.
Keep the solutions coming, even as the sub wanes in activity, I still appreciate them :)
RagingHungryPanda@lemm.ee · 2 pts · 1y
hey thanks!
I didn't check any other solutions before finishing (currently wondering way day 13 is too low), but I thought that trying to traverse fences would be a pain and since I have everything separated by regions and not traversing the array, counting corners never came to mind.
But the thought that I had was that for each region, all points will be a straight line in the V or H orientations, so if I can go up and down and count when
last != next - 1, then that'll tell me that that is a contiguous piece of fence.The idea isn't too hard, for tracking the XAxis it's
Except that I used a different splitting method and that came to me later.
Quant@programming.dev · 2 pts · 1y
Uiua
I spent a while thinking about how to best do a flood fill in Uiua when I saw that
β(partition) works beautifully with multidimensional markers: "Groups are formed from markers that are adjacent along any axis.", meaning I just had to convert all letters into numbers and I'd get all indices belonging to a field into an array.For part 2, I cheated a bit by coming here and reading that you only need to count the edges. To my surprise, the second part is actually a bit faster than part 1. Takes less than 0.2 seconds each though :D
Run with example input here