<Advent of Code 2022 day 3> :thread:
# advent-of-code
a
m
Somehow the description for part 2 made it seem to me like it's more complex, where it was in fact simpler than part 1.
m
Copy code
object Day3 : Challenge() {
    val parsed = input.lines()
    private fun valueExtract(char: Char) = if(char.isLowerCase()) char - 'a' + 1 else char - 'A' + 27
    override fun part1() = parsed
        .map { it.chunked(it.length / 2) }
        .map { (a,b) -> a.first(b::contains) }
        .sumOf(::valueExtract)
    override fun part2() = parsed.chunked(3)
        .map { (a,b,c) -> a.first { it in b && it in c } }
        .sumOf(::valueExtract)
}
wasted time because I did
Copy code
(a,b) -> a.first{ b.contains(a) }
and I just couldn't find what I did wrong, xd
k
how 👀
c
.toSortedSet & .intersect to the rescue
k
toSet()
also exists
m
My solutions:
m
@Kroppeb mhhh yeah that seems sus
c
Damn I forgot about chunked
w
i didn't remember how to do the char math for getting the priority, so i brute forced it :/
j
Aaaaaa I forgot the name of
a.intersect(b)
function, even if I knew it existed (and wasted half a minute scrolling the
Set
member methods), so i just went with
a.filter { it in b }
🙂
w
i think i can clean mine up significantly lol
v
Copy code
val ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

fun part1(input: List<String>): Int {
    return input
        .map { s1 ->
            val mid: Int = s1.length / 2
            s1.substring(0, mid) to s1.substring(mid)
        }
        .sumOf { (s1, s2) ->
            val union = s1.toSet().intersect(s2.toSet())
            union.sumOf { ALPHABET.indexOf(it).inc() }
        }
}

fun part2(input: List<String>): Int {
    val windowSize = 3
    return input
        .windowed(windowSize, windowSize)
        .sumOf { rucksacks ->
            val unique = rucksacks
                .map { it.toSet() }
                .reduce { init, item -> init.intersect(item) }
            unique.sumOf { ALPHABET.indexOf(it).inc() }
        }
}
When you forget how to calculate index of letter with charcode, .indefOf() hardcoded alphabet comes to the resque)
k
@Michael de Kaste people seem to think they used a GPT model. 10 second is not enough for anyone to find the needed information in the text today for a human
m
That would be one impressive model.
j
today’s the part when kotlin’s stdlib shines
m
I also heard that copilot has been good enough to solve puzzles in the early days
d
Y'all are amazing with your
map
reduce
and
filter
. Old fashioned way:
j
My part 2 looks like
m
@Jakub Gwóźdź Instead of hardcoding the string, you could use
('a'..'z') + ('A'..'Z')
to generate it
c
I think this is the most idiomatic Kotlin form I can get today into. https://github.com/CognitiveGear/AdventOfCode-Kotlin/blob/main/src/day03/day03.kt
j
@Marcin Wisniowski ah, indeed, true true
d
I bet the 10 second person had a ready-made function for this. Because it took them 2 more minutes for part 2.
k
@Jan Durovec instead of
windowed(x, x)
you can also use
chunked(x)
@David Whittaker why would anyone have a premade thing for this. 10 seconds is even too fast to realize that the problem matches a premade solution you have (somehow) somewhere and get it and run it on your input
c
Someone having GPT-3 keys and setting up an entirely automated chain is the only one that makes sense.
k
like even 30 and 37 seconds seems awefully fast
yep second place is confirmed to be ai generated: https://twitter.com/max_sixty/status/1598911166465835010
d
I thought about using a
Long
to represent a set of small integers, since it should be faster than
Set<Int>
but didn't bother. I might still do that later, though
c
Using chunked in part1 is inspired, I like that a lot
Having all these functional-style list comprehensions available is so nice!
p
Learned something from the video of Sebi yesterday that I could use today. Mapping char to int according to their order in alphabet. Now it's time to improve the first version which I drafted to solve the quiz to an showable version. But first have breakfast
j
I like the readability of Kotlin K
p
Nothing can stop me! Not even Family meetings where I thought that I didn't need a computer 😁
j
@Paul Woitaschek 👏 I did the whole Kotlin Basic Track on JetBrains Acadamy on my Phone a couple month ago. 😅 https://hyperskill.org/tracks/18
n
Almost identical to @Michael de Kaste's code, with the exception of taking me infinitely longer to get there! https://github.com/nbanman/Play2022/blob/master/src/main/kotlin/org/gristle/adventOfCode/y2022/d3/Y2022D3.kt
v
A beginner's work 🥰
r
☝️ I ended up doing the same,
chuncked
,
sumOf
and
intersect
k
just an fyi, you can destruct lists.
r
something like this should do
Copy code
val (first, second) = item.chunked(item.length / 2)
j
Haven't done it myself, but now I think it's best to just combine chunked, map/toSet and reduce/intersect
r
Looks very similar to what posted above.
v
First in converted to char array and second is converted into set... But you can convert both into set right?
e
you only need to build a set once (per compartment/rucksack)
r
yeah, not required. thanks.
toCharArray
can be removed.
e
I just replaced my sets with a single
Long
acting as a bitset, for an massive speedup
p
Lunatic solution part 2 😁
j
@Paul Woitaschek switching to light mode at 8:30? 😛
p
Automatic based on daytime. Solved part 2 right as the kids got up :)
w
I used typealias to give names to Rucksack (String) , Item (Char), and Group (List<String>). Kotlin typealis is so mice, you document usage without creating unnecessary class. Standard library is so awsome: chunked, intersect, single, sumof, and the efficiency using Sequence. https://github.com/wellithy/aoc2022/blob/main/src/Day03.kt
r
I cleaned it up a bit 😄 much better!
g
I wonder when solutions here will start to differ, cause for now problems are so easy, that almost all looks like homework, that you copy 5 minutes before lecture from your friend, and he tells you to change it a little bit, so teacher does not find out 😄 https://github.com/GrzegorzBaczek93/advent-of-code-kotlin-2022/blob/main/src/day03/Day03.kt
r
Some fun usage of windowed. Always a fun highlight, it doesn't come in quite as often as I would hope in daily work: https://github.com/renatomrcosta/adventofcode/blob/main/src/main/kotlin/aoc2022/day3/day3.kt
k
@renatomrcosta you used chunked in part 1 but then switched to windowed in part 2?
r
@Kroppeb just banged the fastest, least thinking answer, then went back and refactored it a little bit. Originally that part with the chunked was super dirty and ugly:
Copy code
this.splitOnLineBreaks()
    .map {
        val size = it.length - 1
        it.toCharArray(0, (size / 2) + 1) to it.toCharArray((size / 2) + 1, size + 1)
    }
    .map { (c1, c2) -> c1.toList() to c2.toList() }
So before putting it in public, I wanted to quickly clean it up haha. Ended up not looking back at pt2 tho (ah, also, the chunked in pt1 is to separate the compartments only. pt2 didn't really call for that, just the windowed worked out ok for the full strings)
p
Now that the kids let me time for optimization, here my solution. https://github.com/PoisonedYouth/advent-of-code-kotlin-2022/blob/main/src/day03/Day03.kt
w
I probably should have figured that the priorities were the order of the alphabet 😅
k
@renatomrcosta
windowed(n,n)
is the same as
chunked(n)
If the input length is a multiple of n at least
I think windowed will skip a final partial by default while chunked by default doesn't
e
@Wilerson Oliveira
itemTypes.zip(priorities).toMap()
does the same
p
Quite happy with this as my first solution
e
you could just skip
priorities
if you wanted,
Copy code
itemTypes.withIndex().associateBy(keySelector = { it.value }, valueTransform = { it.index + 1 })
j
message has been deleted
And a bunch of extension functions
Can I do this destructuring on one line? ElfTrio is just a Triple<String,..>
e
you can absolutely do
Copy code
.map { (a, b, c) ->
j
👌 thanks!
c
Which of these is preferred? They’re the same functionality wise:
Copy code
input
        .map { it.splitIntoHalfSets() }
        .map { it.first.intersect(it.second).first() }
        .sumOf { it.toPriority() }
vs
Copy code
input.sumOf {
        val (firstHalf, secondHalf) = it.splitIntoHalfSets()
        val commonItem = firstHalf.intersect(secondHalf).first()
        commonItem.toPriority()
    }
Is there any performance difference? Is one more readable than the other?
p
If
input
is a List, each
map
returns a new list with the transformed elements. As such the
sumOf
alone would have better performance. I'd normally use separate mapping steps if they make sense to reason over them separately, and use
asSequence
to make it lazy and mitigate some of the performance impact.
c
Cheers. Gone with the single
sumOf
approach but made various functions in a style making it easy to read. Think my solution reads well in the end: https://github.com/CfGit12/aoc-2022/blob/master/src/main/kotlin/aoc2022/3.kt
x
Thank god ktstdlib for
.toSet
and `intersect`s
Copy code
fun Sequence<Set<Char>>.scored() = this
  .map { it.map { char -> if (char.isLowerCase()) char - 'a' + 1 else char - 'A' + 27  } }
  .flatten()
  .sum()

fun part1(input: List<String>) = input.asSequence()
  .map { rucksack -> rucksack.chunked(rucksack.length / 2) }
  .map { (first, second) -> first.toSet() intersect second.toSet()  }
  .scored()

fun part2(input: List<String>) = input.asSequence()
  .chunked(3)
  .map { (first, second, third) -> first.toSet() intersect second.toSet() intersect third.toSet()}
  .scored()
j
Day 3 is all about how far should you get with going idiomatic. e.g. yes you could go with `map`/`reduce`/`intersect`, and then you can have part 1 and two that differs in only one line (grouping):
But maybe it’s enough to be less idiomatic and more readable?
f
I tend to start the tasks with writing a small class/enum to represent the entities in the task. Usually I find it gives readable/comprehensive code, but today it might have been unnecessary:
p
Who needs sets anyway?
j
@phldavies two reasons to have sets here: 1. chars repeat in one bag, no need to check them multiple times 2. hashsets help a lot with
it in b
and similar comparisons
but of course with this small set of data you’re good with simply strings
p
Oh I certainly agree sets are usually better - but the overhead of calculating and inserting into sets for these cases is more than a few extra iterations through the strings.
o
Bit flags gang anyone? 😅
j
@Oleksandr Balan creative!
k
A bit late to the party, I've also used sets K , also thought about lists, but I liked how the
intersect
made things more clear (code):
k
Well, I just learned earlier today that kotlin`s intersect is an extension function on iterables, and not sets
k
Yeah, but it internally converts the
Iterable
to a set, so in the case you were already using a set I'd like to believe that this step is just an identity, right?
k
message has been deleted
k
I don't think it is an identity
k
Why would they lie us that way? 🥹
f
I'd like to believe that this step is just an identity
-> I don't think so... Set.intersect =
```public infix fun <T> Iterable<T>.intersect(other: Iterable<T>): Set<T> {
val set = this.toMutableSet()
set.retainAll(other)
return set
}```
Set.toMutableSet =
```public fun <T> Iterable<T>.toMutableSet(): MutableSet<T> {
return when (this) {
is Collection<T> -> LinkedHashSet(this)
else -> toCollection(LinkedHashSet<T>())
}
}```
but then again I have some trouble navigating to stdlib these days: https://kotlinlang.slack.com/archives/C0B8H786P/p1670066861716219 😢 so I might be wrong
k
Yeah, but in the case where the conversion of
Iterable
set to a set is not an identity, then it's better to just call
intersect
directly onto the strings, right? 🤔
p
The reason
Iterable
is converted to `Set`(even when operating on a set) is because you want a new
Set
to be returned. The optimization would be to iterate through
other
and only add to the new set based on
it in this
(assuming
this
has optimal
contains
performance)
k
Yeah
Wait how is retainAll implemented on a linkedHashSet
k
That about the new
Set
makes a lot of sense
Oh okay, so you still gain performance from
intersect
if
other
is a
Set
(as @phldavies mentioned, I guess in this case it depends on the
contains
performance)
a
Copy code
val priorities = ('a'..'z').associateWith { it.code - 96 } + ('A'..'Z').associateWith { it.code - 38 }
I've seen some other ways to do the priority calc, but I haven't seen this yet. Sharing my char ranges map. It isn't the most clever, but it is another way.
e
I didn't do this for my solution, but if that's the part we're golfing, this works:
Copy code
fun Char.priority(): Int = (code - 38) % 58
e
s
Late but happy with my solution. I'm new to Kotlin and addicted to extension functions
b
message has been deleted
p
@Oleksandr Balan bitsets do work nicely 🙂 (although simply sticking to strings and not using sets is still faster on my basic timing - I should check out kotlinx-benchmark)
k
First attempt, could probably be done simpler. Liking these tasks so far :)
t
I had a busy morning so today's solution is a bit late. • BlogCode
d
A little late to the party due to weekend, but this one seemed easier than day 2 to me (also less code):
Copy code
object Day3 {

    fun getResultPart1() = getInputAsSequence()
        .map { line -> line.chunked(line.length / 2) }
        .map { compartments ->
            compartments[0].first { c -> compartments[1].contains(c) }
        }.sumOf { c -> getPriority(c) }

    fun getResultPart2() = getInputAsSequence()
        .chunked(3)
        .map { rucksacks ->
            rucksacks[0].first { c -> rucksacks[1].contains(c) && rucksacks[2].contains(c) }
        }.sumOf { c -> getPriority(c) }

    private fun getPriority(c: Char): Int = (c.lowercaseChar().code - '`'.code) + if (c.isUpperCase()) 26 else 0
}
Here I used the fact that the backtick character is the character preceding
'a'
in the ASCII / UTF table
And the
toUppercase
part is just to calculate the extra offset for uppercase letters which have higher priority