Advent of Code 2021 day 4
12/04/2022, 5:00 AMMichael de Kaste
12/04/2022, 5:13 AMobject Day4 : Challenge() {
val parsed = input.lines().map { line ->
line.split(",").map {
it.split("-").map(String::toInt).let { (a, b) -> (a..b).toSet() } }
}
override fun part1() = parsed.count { (a, b) -> a.containsAll(b) || b.containsAll(a) }
override fun part2() = parsed.count { (a, b) -> a.intersect(b).isNotEmpty() }
}
Thanks Kotlin for the ..
:)Jakub Gwóźdź
12/04/2022, 5:16 AM.count { (s1, s2) -> s1.all { it in s2 } || s2.all { it in s1 } }
and
.count { (s1, s2) -> s1.any { it in s2 } || s2.any { it in s1 } }
ritesh
12/04/2022, 5:18 AMJakub Gwóźdź
12/04/2022, 5:18 AMMichael de Kaste
12/04/2022, 5:19 AMritesh
12/04/2022, 5:19 AMMichael de Kaste
12/04/2022, 5:20 AMMichael de Kaste
12/04/2022, 5:20 AMritesh
12/04/2022, 5:21 AMIn some languages, range overlaps might be more excitingIn second part i just had to change the condition from
&&
to ||
😄Michael de Kaste
12/04/2022, 5:22 AM(a.start <= b.start && b.end >= a.end) || (b.start <= a.start && b.end >= a.end)
which is probably optimizedritesh
12/04/2022, 5:23 AMinput.forEach {
val (first,second) = it.split(",").map { it.trim() }
val (firstSecF, firstSecS) = first.split("-").map { it.trim() }.map { it.toInt() }
val (secondSecF, secondSecS) = second.split("-").map { it.trim() }.map { it.toInt() }
if ((firstSecF in secondSecF..secondSecS) && (firstSecS in secondSecF..secondSecS))
sum++
else if ((secondSecF in firstSecF..firstSecS) && (secondSecS in firstSecF..firstSecS)){
sum++
}
}
second
input.forEach {
val (first,second) = it.split(",").map { it.trim() }
val (firstSecF, firstSecS) = first.split("-").map { it.trim() }.map { it.toInt() }
val (secondSecF, secondSecS) = second.split("-").map { it.trim() }.map { it.toInt() }
if ((firstSecF in secondSecF..secondSecS) || (firstSecS in secondSecF..secondSecS))
sum++
else if ((secondSecF in firstSecF..firstSecS) || (secondSecS in firstSecF..firstSecS)){
sum++
}
}
xxfast
12/04/2022, 5:24 AMfun part1(input: List<String>) = input
.map { line -> line.split(",").map { it.split("-").let { (start, end) -> start.toInt()..end.toInt() } } }
.count { (first, second) -> (first - second).isEmpty() || (second - first).isEmpty() }
fun part2(input: List<String>): Int = input
.map { line -> line.split(",").map { it.split("-").let { (start, end) -> start.toInt()..end.toInt() } } }
.count { (first, second) -> first.intersect(second).isNotEmpty() || second.intersect(first).isNotEmpty() }
Neil Banman
12/04/2022, 5:24 AMDavid Whittaker
12/04/2022, 5:29 AMintersect
today - thanks everyone for the learnings yesterday! 🙂Marcin Wisniowski
12/04/2022, 5:29 AMMarcin Wisniowski
12/04/2022, 5:29 AMMarcin Wisniowski
12/04/2022, 5:30 AMJan Durovec
12/04/2022, 5:35 AM(a..b).toSet()
lines above we're lucky it's still just day 4 because I can imagine that in 2-digit days Eric would have include some massive interval 😉ritesh
12/04/2022, 5:37 AMCognitive Gear
12/04/2022, 5:43 AMJakub Gwóźdź
12/04/2022, 5:48 AM<=
and >=
if you want to be more kotlinish, you can go with
s1.first in s2 && s1.last in s2 || s2.first in s1 && s2.last in s1
but that’s four unnecessary comparisons under the hood. But you’re in trouble if you go (as I did initially) with .any
, .all
since they check for every number in the range.
Even worse, intersect
, is actually O(n^2) (or n log n ?) in cpu and O(n) memory usage. so for input data 1-100000000,2-99999999
the intersect might say “you know what, count it yourself”. 🙂Christian Ricardo
12/04/2022, 5:55 AMDirk Groot
12/04/2022, 5:57 AMsolutionX
functions easier to read without making it overly verbose.Cognitive Gear
12/04/2022, 6:05 AMJan Durovec
12/04/2022, 6:09 AMChristian Ricardo
12/04/2022, 6:11 AMMarcin Wisniowski
12/04/2022, 6:11 AMMichael Böiers
12/04/2022, 6:12 AMpackage aoc2022
fun main() = day04(String(System.`in`.readAllBytes())).forEach(::println)
fun day04(input: String) = input.lines()
.map { line ->
line.split(",").map { elf ->
elf.split("-").map(String::toInt).let { it[0]..it[1] }
}
}
.run {
listOf(Iterable<Int>::all, Iterable<Int>::any).map { op ->
count { (a, b) -> op(a) { it in b } || op(b) { it in a } }
}
}
Jakub Gwóźdź
12/04/2022, 6:15 AMMarcin Wisniowski
12/04/2022, 6:15 AMcontains
instead of isInside
, and then you can call it with the built-in in
operator.Jakub Gwóźdź
12/04/2022, 6:25 AMthis
and other
operands and it looks meh 🙂
private operator fun IntRange.contains(other: IntRange) = other.first in this && other.last in this
Wael Ellithy
12/04/2022, 6:33 AMChristian Ricardo
12/04/2022, 6:36 AMephemient
12/04/2022, 6:42 AMephemient
12/04/2022, 6:42 AMIntRange
as the representation, it's pretty naturalephemient
12/04/2022, 6:43 AMDan Fingal-Surma
12/04/2022, 6:57 AMDan Fingal-Surma
12/04/2022, 7:06 AMDan Fingal-Surma
12/04/2022, 7:18 AMandrew
12/04/2022, 7:24 AMVenkataramanan Parameswaran
12/04/2022, 7:54 AMVenkataramanan Parameswaran
12/04/2022, 7:58 AMVenkataramanan Parameswaran
12/04/2022, 8:21 AMcontainsAll
, in
, intersect
, set
are seems to be more kotlin idiomatic... they are less performant than the solutions involve simple math.... your opinionritesh
12/04/2022, 8:21 AMphldavies
12/04/2022, 8:39 AMPoisonedYouth
12/04/2022, 9:00 AMfun splitToRange(input: String): IntRange {
val (start, end) = input.split("-")
return IntRange(
start = start.toInt(),
endInclusive = end.toInt()
)
}
fun IntRange.fullyContains(other: IntRange): Boolean {
return this.all { other.contains(it) }
}
fun IntRange.overlap(other: IntRange): Boolean {
return this.any { other.contains(it) }
}
fun part1(input: List<String>): Int {
return input.count { line ->
val (elv1, elv2) = line.split(",")
val elv1Range = splitToRange(elv1)
val elv2Range = splitToRange(elv2)
elv1Range.fullyContains(elv2Range) || elv2Range.fullyContains(elv1Range)
}
}
fun part2(input: List<String>): Int {
return input.count { line ->
val (elv1, elv2) = line.split(",")
val elv1Range = splitToRange(elv1)
val elv2Range = splitToRange(elv2)
elv1Range.overlap(elv2Range)
}
}
Abdul Khaliq
12/04/2022, 9:23 AMCharles Flynn
12/04/2022, 9:34 AMall
to any
🙂
https://github.com/CfGit12/aoc-2022/blob/master/src/main/kotlin/aoc2022/4.ktAbdul Khaliq
12/04/2022, 10:28 AMDavio
12/04/2022, 10:41 AMfun getResultPart1(): Int {
fun IntRange.contains(other: IntRange) = this.first <= other.first && this.last >= other.last
return getInputAsSequence()
.map { line -> line.split(",") }
.map { lineParts -> lineParts[0].toIntRange() to lineParts[1].toIntRange() }
.count { ranges -> ranges.first.contains(ranges.second) || ranges.second.contains(ranges.first) }
}
fun getResultPart2(): Int {
fun IntRange.overlaps(other: IntRange) = this.any { other.contains(it) }
return getInputAsSequence()
.map { line -> line.split(",") }
.map { lineParts -> lineParts[0].toIntRange() to lineParts[1].toIntRange() }
.count { ranges -> ranges.first.overlaps(ranges.second) }
}
private fun String.toIntRange(): IntRange {
val parts =this.split("-")
return parts[0].toInt()..parts[1].toInt()
}
Davio
12/04/2022, 10:41 AMMichael Böiers
12/04/2022, 10:53 AMMarc Javier
12/04/2022, 10:56 AMVenkataramanan Parameswaran
12/04/2022, 10:58 AMDavio
12/04/2022, 11:01 AMFredrik Rødland
12/04/2022, 12:15 PMkqr
12/04/2022, 12:19 PMSergei Petunin
12/04/2022, 1:21 PMfun part1(input: List<String>) = input.map {
it.split(",", "-").map(String::toInt)
}.count { (from1, to1, from2, to2) ->
from1 <= from2 && to1 >= to2 || from1 >= from2 && to1 <= to2
}
fun part2(input: List<String>) = input.map {
it.split(",", "-").map(String::toInt)
}.count { (from1, to1, from2, to2) ->
(from1..to1).intersect((from2..to2)).isNotEmpty()
}
Kevin Del Castillo
12/04/2022, 1:44 PMSets
for this one, just plain range comparisons (code)todd.ginsberg
12/04/2022, 1:45 PMIntRange
because I thought Pair<Int,Int>
wasn''t ideal looking and I'd have to write extension functions on either one of them to test for overlaps.Kevin Del Castillo
12/04/2022, 1:47 PMIntRange
feels pretty natural and easy to do, you can just grab your limits and check if on is contained inside another, and for the overlap you can use the already defined contains
function for single elements, which implementation also uses range comparison, which you don't have to rewrite againDavio
12/04/2022, 1:48 PMintersect(other).isNotEmpty()
ritesh
12/04/2022, 1:48 PMvarargs
and limit the receiving part before today. It came handy.
I learnt about it from this thread 🙂Kevin Del Castillo
12/04/2022, 1:49 PMintersect
will create a Set
😬 which is not needed at all for this case if you can use simple logicMichael Böiers
12/04/2022, 1:53 PMpackage aoc2022
fun main() = day04(String(System.`in`.readAllBytes())).forEach(::println)
fun day04(input: String) = input.lines()
.map { it.split(",", "-").map(String::toInt).let { (a, b, c, d) -> a..b to c..d } }
.run {
listOf(
count { (a, b) -> a.includes(b) || b.includes(a) },
count { (a, b) -> a.overlapsWith(b) || b.overlapsWith(a) },
)
}
fun IntRange.overlapsWith(o: IntRange) = first in o || last in o || includes(o)
fun IntRange.includes(o: IntRange) = o.first in this && o.last in this
Davio
12/04/2022, 2:06 PMGrzegorz Aniol
12/04/2022, 3:07 PMfun main() {
fun List<String>.splitToRanges(): List<Pair<IntRange, IntRange>> =
map { text ->
val numbers = text.split(",").flatMap { it.split("-") }.map { it.toInt() }
check (numbers.size == 4)
Pair(numbers[0]..numbers[1], numbers[2]..numbers[3])
}
fun IntRange.contains(other: IntRange) =
other.first in this && other.last in this
fun fullyContains(ranges: Pair<IntRange, IntRange>) =
ranges.first.contains(ranges.second) || ranges.second.contains(ranges.first)
fun overlaps(pairOfRanges: Pair<IntRange, IntRange>) =
pairOfRanges.first.intersect(pairOfRanges.second).isNotEmpty()
fun part1(input: List<String>): Int =
input.splitToRanges().count(::fullyContains)
fun part2(input: List<String>): Int =
input.splitToRanges().count(::overlaps)
val testInput = readInput("Day04_test")
check(part1(testInput) == 2) { "Part1 Test - expected value doesn't match" }
check(part2(testInput) == 4) { "Part2 Test - expected value doesn't match" }
val input = readInput("Day04")
println(part1(input))
println(part2(input))
}
Joris PZ
12/04/2022, 4:23 PMexpect/actual
mechanism.
My day 4 in common code:
val p04 = suspend {
// List of Lists, each containing two IntRanges
val input = readInput("04.txt").split("\n").map {
it.split(",").map { it.split("-").map { it.toInt() }.let { it[0]..it[1] } }
}
input.count {
it[0].containsFully(it[1]) || it[1].containsFully(it[0])
}.print()
input.count {
it[0].overlapsWith(it[1])
}.print()
}
Using some utility functions:
fun IntRange.permute() = this.toList().permute()
fun IntRange.containsFully(other: IntRange) = this.contains(other.first) && this.contains(other.last)
fun IntRange.overlapsWith(other: IntRange) =
this.contains(other.first) || this.contains(other.last) || other.contains(this.first) || other.contains(this.last)
Execution times per platform in microseconds (15 repetitions):
JVM : 13017, 2816, 2513, 2390, 2315, 2297, 1983, 1523, 991, 901, 736, 767, 751, 747, 729
JS : 47697, 13468, 5338, 7299, 4328, 2491, 1374, 1297, 1638, 2772, 1361, 1236, 1165, 1470, 1304
Native : 8143, 7496, 7079, 7967, 7424, 7372, 8403, 6711, 7832, 7634, 8017, 7093, 7932, 7437, 7825
This is a pattern I have seen many times over the past AoC's. Native beating JVM and JS on the first run, but when the virtual machines get warm they rapidly start peforming better, with the JVM being fastest.Karloti
12/04/2022, 4:40 PMfun main() {
fun Sequence<String>.splitBounds() = map { it.split('-', ',').map(String::toInt) }
fun part1(input: Sequence<String>): Int =
input.splitBounds().count { (a, b, c, d) -> (a <= c) && (b >= d) || (a >= c) && (b >= d) }
fun part2(input: Sequence<String>): Int =
input.splitBounds().count { (a, b, c, d) -> a <= d && c <= b }
check(part1(readInputAsSequence("Day04_test")) == 2)
check(part2(readInputAsSequence("Day04_test")) == 4)
println(part1(readInputAsSequence("Day04")))
println(part2(readInputAsSequence("Day04")))
}
Baghaii
12/04/2022, 5:40 PMval ranges = line.split(",","-").map { it.toInt() }
Karloti
12/04/2022, 5:46 PMDavio
12/04/2022, 5:46 PMfun IntRange.overlaps(other: IntRange) =
this.first in other || this.last in other || other.first in this
// No other.last in this is needed here
Davio
12/04/2022, 5:51 PMDavio
12/04/2022, 5:56 PMother.last in this
or other.first in this
it has the same result. Now with short circuiting, there wouldn't be a performance hit specifying bothMichael Böiers
12/04/2022, 5:57 PMMichael Böiers
12/04/2022, 5:58 PMDavio
12/04/2022, 5:59 PMDavio
12/04/2022, 6:03 PMPaul Woitaschek
12/04/2022, 9:09 PMephemient
12/05/2022, 12:23 AMOzioma Ogbe
12/05/2022, 12:42 AMephemient
12/05/2022, 12:46 AM.map { ... }.count { it }
? .count { ... }
has the same result with less workDan Fingal-Surma
12/05/2022, 12:49 AMcount
also took a predicate (looked for a countBy
or countIf
method)Potatoboy
12/05/2022, 12:50 AMclass Day4 {
val sections = readInput(4).map {
val pair = it.split(",").let { it[0] to it[1] }
return@map pair.first.toRange() to pair.second.toRange()
}
fun String.toRange() = split("-").let { it[0].toInt()..it[1].toInt() }
fun IntRange.contains(other: IntRange) = other.all { this.contains(it) }
fun IntRange.overlaps(other: IntRange) = other.any { this.contains(it) }
fun part1() = sections.filter { it.first.contains(it.second) || it.second.contains(it.first) }.size
fun part2() = sections.filter { it.first.overlaps(it.second) }.size
}
fun main() {
val day = Day4()
println(day.part1())
println(day.part2())
}
Dan Fingal-Surma
12/05/2022, 12:52 AMephemient
12/05/2022, 12:52 AM.count { ... }
works better than .filter { ... }.size
Potatoboy
12/05/2022, 12:54 AMPotatoboy
12/05/2022, 12:57 AMOzioma Ogbe
12/05/2022, 1:01 AMOzioma Ogbe
12/05/2022, 1:01 AMMarc Javier
12/05/2022, 2:06 PMMichael de Kaste
12/05/2022, 2:08 PMMichael de Kaste
12/05/2022, 2:09 PMephemient
12/05/2022, 2:10 PMPaul Woitaschek
12/05/2022, 2:11 PMephemient
12/05/2022, 2:12 PMPaul Woitaschek
12/05/2022, 2:13 PM