<Advent of Code 2022 day 23> :thread:
# advent-of-code
a
s
m
Lots of value today out of my premade utils: Point class, Direction class, Point.getAdjacent() method and Point.move(Direction) method.
j
@Sergei Petunin yeah we deserved it.
j
I must be doing something horribly wrong today. Simple task, simple code, but for some mysterious reason my code works OK on sample inputs but yields incorrect answer for the real one... 😭
j
My solution: https://github.com/bulldog98/advent-of-code-2022/blob/main/advent2022/src/main/kotlin/Day23.kt did part2 naivly and missed that I have to tell the simulation in which real round it is 😅
r
I also submitted a wrong answer, giving the number of empty cells after it settles, rather than how many rounds it takes to settle. I'm very glad the naïve solution works reasonably fast today.
j
@rkechols yeah answer for part2 was in the 10^3 order of magnitude, which is not to bad, since each round is simulated fast
c
cannot get the 2nd round to move the same way as the larger sample. I believe the top elf should move South and according to the larger sample, the top elf doesn't move. Do I misunderstand something? ```== End of Round 1 == .............. .......#...... .....#...#.... ...#..#.#..... .......#..#... ....#.#.##.... ..#..#.#...... ..#.#.#.##.... .............. ....#..#...... .............. .............. == End of Round 2 == .............. .......#...... ....#.....#... ...#..#.#..... .......#...#.. ...#..#.#..... .#...#.#.#.... .............. ..#.#.#.##.... ....#..#...... .............. .............. My grid after round 2 == End of Round 2 == .............. .............. ....#..#..#... ......#.#..... ...#...#...#.. ...#..#.#..... ...#.#.#.#.... .............. ..#.#.#.##.... .............. ....#..#...... ..............
j
I found a bug after almost 3 hours. I forgot to reset directions cycle after running tests 🤦
v
@corneil I've got into the same problem - it was because i missed next phrase in task description:
If no other Elves are in one of those eight positions, the Elf does not do anything during this round.
j
@Jan Durovec if you test state less stuff that does not happen, sounds like you have to much state 🙂
I missed many minor details with this one
o
Compared to yesterday's great puzzle, this one was rather easy and straight forward, no twists
o
Watch out for tomorrow
r
With this logic - ending day - 25 should be easy 😄
f
anyone optimised anything to get part2 to run fast? (naive runs in about 700ms here).
c
The question would be how do you determine which to exclude? If I look at the numbers moving each round it goes down and then picks up a little before going down to 0.
f
nod
j
@Fredrik Rødland I just came here to ask the exact same question 🙂 I’m trying to figure out if the part 2 can be somehow optimized. Anything to get busy and not do the important thing today (which is to finally solve effin robots in day 19 part 2…)
p
https://github.com/tke/aoc-22/blob/main/kt/src/main/kotlin/year2022/Day23.kt a pleasant change to need only a minor refactor for part2. Not the fastest run for part2 but ~1s isn’t terrible.
@ephemient nice to see the same
groupingBy/aggregate
approach for the rounds 👍
m
For these types of puzzles I usually spend the additional line to define my own data class instead of just using Pair, to get rid of the semantically void first and second.
e
I did define my own
data class IntPair(val first: Int, val second: Int)
just to get rid of the boxing that
Pair<Int, Int>
would do
but yeah, I do have a bad habit of re-using generic data types during AOC. lots of my other solutions use
IndexedValue
when I need to attach an
Int
to something, even though it's not really "indexed" by it
BTW, since this has come up a few times in AOC… there isn't any elegant way to compute min+max of multiple projections at once, is there?
e.g. in Haskell I can write
Copy code
let ((Min minX, Max maxX), (Min minY, Max maxY)) = foldMap ((Min &&& Max) *** (Min &&& Max)) points
but in Kotlin I'd either have to do it in multiple passes
Copy code
val minX = points.minOf { (x, _) -> x }
val maxX = points.maxOf { (x, _) -> x }
val minY = points.minOf { (_, y) -> y }
val maxY = points.maxOf { (_, y) -> y }
or with imperative code
Copy code
var minX = Int.MAX_VALUE
var maxX = Int.MIN_VALUE
var minY = Int.MAX_VALUE
var maxY = Int.MIN_VALUE
for ((x, y) in points) {
    minX = minOf(minX, x)
    maxX = maxOf(maxX, x)
    minY = minOf(minY, y)
    maxY = maxOf(maxY, y)
}
both of which I've written with typo'ed errors multiple times already
j
@ephemient if you can write an extension function on pair or what ever type you use to get both at the same time, but yes it's a bit sad that you cannot do that. On the other hand in typical industry scenarios I at least have not seen a case where this was needed. (Maybe I'm working to much with simple crud + db software)
c
It will probably be easy to create an Iterator extension function that maps a value to a
data class<T>MinMax(val min: T, val max: T)
The add a similar function for a Pair.
p
At one point I had a fold over IntRange to get first/last.
t
That was fun, eventually, after debugging what ended up being a typo for a little while. 🙂BlogCode
j
@todd.ginsberg you have a typo in the Blogpost
nextTurnOFfsets
t
Thanks @Jonathan Kolberg - fixed (publishing)
e
I experimented with generateSequence but didn’t end up liking how it looked
any reason? I think
Copy code
.zipWithNext().indexOfFirst { (prev, cur) -> prev == cur } + 1
is pretty good
t
Yeah, that is really nice. I’ll go back through my IDE history and see why I didn’t like it.
p
I used an early return from
sequence{}
in an
.ifEmpty { return@sequence }
to end the sequence letting me just
.count()
.
e
I used a
scan
(aka
runningFold
, but I'm more accustomed to the
scan
name as it's more common in other langauges) over a sequence of directions, so part moving the elves doesn't have to keep track of that
p
I was going to use a running fold but didn't like carrying the round in a pair, so unwrapped with sequence{}
e
I don't carry the round though, why would that be needed?
t
I guess I could have rotated the list of offsets and not done that.
p
Ah you scan over the ordered directions, nice.
e
my Python solution had a
cycle(("NSWE", "SWEN", "WENS", "ENSW"))
which I could have ported back to Kotlin, if I wanted to be type-unsafe :D
t
I need to learn python. I’m seriously impressed you can solve this 4x per day when I’m over here struggling with one language I already know. I still don’t have yesterday Part 2 figured out, I’ve written 90% of three solutions already.
e
it's been a bit less interesting this year, honestly. this time, the obvious solution or the optimal solution is structurally similar across them all
r
I solve in Python first since I'm a million times more familiar with it, then most days I re-write it into Kotlin for the Kotlin practice. Of course, there are some python things that you can't do in Kotlin (or I don't know how to do in Kotlin), but it's still good practice figuring out how to make those things work. It also means my Kotlin solutions don't use all the cool things Kotlin had to offer, but I think that's happen with me anyway since I just don't know them well yet.
n
this "find min and max" of a value does come up regularly in AoC, but I have not really seen it to often in other code. A
Collection<Int>.toRange(): IntRange
might do the trick.
e
@nkiesel This is an interesting idea. I've recorded it here: KT-55636 Add range/rangeOf family functions to compute both min and max value of a collection
r
Lesson learnt from this aoc - since this is first time, i am doing it seriously. Avoid using grids unless required -
Set<Position>
works, i had a hard time, when i was using grids, switching to sets of positions tracking elves made it much simpler.
o
yea, especially when there could be potentially negative coodinates.
e
grids are fine - just pad them or use `.getOrNull()`/`.getOrElse()` when indexing
p
A few times I've played with zigzag encoding signed integers in a bitset, but generally hidden it behind a
get(x, y)
operator.
e
although I did use
Set<IntPair>
on 23 because it's less work, but if I go back to speed it up, I'll probably switch to some packed bit representation so that I can scan over it with a LUT