Advent of Code 2023 day 6
12/06/2023, 5:00 AMMichael de Kaste
12/06/2023, 5:08 AMMarcin Wisniowski
12/06/2023, 5:16 AMMarcin Wisniowski
12/06/2023, 5:18 AMbabel
12/06/2023, 5:18 AMbabel
12/06/2023, 5:18 AMPlease make sure you let go of the button so the boat gets to move.
Marcin Wisniowski
12/06/2023, 5:18 AMMarcin Wisniowski
12/06/2023, 5:18 AMbabel
12/06/2023, 5:19 AM..<time
😄Marcin Wisniowski
12/06/2023, 5:19 AMNorbert Kiesel
12/06/2023, 5:27 AMMichael de Kaste
12/06/2023, 5:33 AMNorbert Kiesel
12/06/2023, 5:34 AMMichael de Kaste
12/06/2023, 5:35 AMobject Day6 : Challenge() {
override fun part1() = input.lines()
.map { it.split(" ").mapNotNull { it.toIntOrNull() } }
.let { (a, b) -> a.zip(b) }
.fold(1L) { acc, (time, distance) -> acc * (0..time).count { i -> (time - i) * i > distance } }
override fun part2(): Any? {
val (time, distance) = input.lines().map { it.substringAfter(":").replace(" ", "").toLong() }
return (0..time).count { i -> (time - i) * i > distance }
}
}
Tomasz Linkowski
12/06/2023, 5:36 AMNorbert Kiesel
12/06/2023, 5:37 AMclass Race(private val time: Long, private val distance: Long) {
fun better() = (1..<time).count { (time - it) * it > distance }
}
Dan Fingal-Surma
12/06/2023, 5:40 AMDan Fingal-Surma
12/06/2023, 5:46 AMEl Anthony
12/06/2023, 5:46 AMDan Fingal-Surma
12/06/2023, 5:46 AMJonathan Kolberg
12/06/2023, 5:48 AMDan Fingal-Surma
12/06/2023, 5:52 AMDan Fingal-Surma
12/06/2023, 5:53 AMDan Fingal-Surma
12/06/2023, 5:53 AMJakub Gwóźdź
12/06/2023, 5:54 AMaoc2023.day6.part2() took 5m 1.039336083s and resulted with: 33875953
I have no shame 😆
(now that I have time, I’ll do it properly)Dan Fingal-Surma
12/06/2023, 5:54 AMDan Fingal-Surma
12/06/2023, 5:54 AMDan Fingal-Surma
12/06/2023, 5:55 AM(1..<time).count { velocity -> distance < velocity * (time - velocity) }
Jakub Gwóźdź
12/06/2023, 5:56 AMaoc2023.day6.part2() took 35.238708ms and resulted with: 33875953
El Anthony
12/06/2023, 5:56 AMJakub Gwóźdź
12/06/2023, 5:56 AMcount
on LongRangeDan Fingal-Surma
12/06/2023, 5:57 AMDan Fingal-Surma
12/06/2023, 5:57 AMfun countWinners(time: Long, distance: Long) =
(1..time).count { velocity -> distance < velocity * (time - velocity) }.toLong()
Johannes Gätjen
12/06/2023, 5:57 AMDan Fingal-Surma
12/06/2023, 5:57 AMJohannes Gätjen
12/06/2023, 5:58 AMJakub Gwóźdź
12/06/2023, 6:00 AMDan Fingal-Surma
12/06/2023, 6:01 AMDan Fingal-Surma
12/06/2023, 6:02 AMJacob
12/06/2023, 6:02 AMLongRange.count{}
return a long?Dan Fingal-Surma
12/06/2023, 6:02 AMIterable<T>.count(): Long
Jakub Gwóźdź
12/06/2023, 6:02 AMJakub Gwóźdź
12/06/2023, 6:02 AMpublic inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
if (this is Collection && isEmpty()) return 0
var count = 0
for (element in this) if (predicate(element)) checkCountOverflow(++count)
return count
}
Jakub Gwóźdź
12/06/2023, 6:03 AMDan Fingal-Surma
12/06/2023, 6:03 AMcheckCountOverflow
, which is niceJakub Gwóźdź
12/06/2023, 6:03 AMMichael de Kaste
12/06/2023, 6:04 AMobject Day6 : Challenge() {
override fun part1() = input.lines()
.map { it.split(" ").mapNotNull { it.toLongOrNull() } }
.let { (a, b) -> a.zip(b) }
.fold(1L) { acc, (time, distance) -> acc * solve(time, distance) }
override fun part2(): Any? {
val (time, distance) = input.lines().map { it.substringAfter(":").replace(" ", "").toLong() }
return solve(time, distance)
}
private fun solve(time: Long, distance: Long): Long {
fun solveQuadratic(a: Double, b: Double, c: Double): Long {
val determinant = b * b - 4.0 * a * c
val root1 = (-b + sqrt(determinant)) / (2 * a)
val root2 = (-b - sqrt(determinant)) / (2 * a)
return ceil(root2).toLong() - ceil(root1).toLong()
}
return solveQuadratic(-1.0, time.toDouble(), -distance.toDouble())
}
}
y9san9
12/06/2023, 6:05 AMMichael de Kaste
12/06/2023, 6:06 AMSergei Petunin
12/06/2023, 6:08 AMJohannes Gätjen
12/06/2023, 6:08 AMJakub Gwóźdź
12/06/2023, 6:09 AMMichael de Kaste
12/06/2023, 6:10 AMPaul Woitaschek
12/06/2023, 6:11 AMJerry hanks
12/06/2023, 6:13 AMy9san9
12/06/2023, 6:15 AMEl Anthony
12/06/2023, 6:17 AMy9san9
12/06/2023, 6:17 AMy9san9
12/06/2023, 6:17 AMJerry hanks
12/06/2023, 6:27 AMy9san9
12/06/2023, 6:30 AMMichael de Kaste
12/06/2023, 6:31 AMJakub Gwóźdź
12/06/2023, 6:32 AMfun calc(t: Long, d: Long): Long {
var i = 0L
while ((t - i) * i < d) i++
return t - i - i + 1
}
resulted in
Input(time=47 70 75 66, distance=282 1079 1147 1062)
aoc2023.day6.part1() took 1.270708ms and resulted with: 281600
aoc2023.day6.part2() took 5.151250ms and resulted with: 33875953
But I have this itch I can’t scratch: I know it’s not the PROPER solution, even if blazingly fast 🙂Riccardo Lippolis
12/06/2023, 6:47 AMfun countWaysToWin(t: Long, d: Long) =
(d / t..t - (d / t)).count { x -> (t - x) * x > d }
of course far from optimal like the quadratic solution, but at least I somewhat narrow the search space compared to iterating from 0
to t
😅Anirudh
12/06/2023, 6:52 AMephemient
12/06/2023, 6:59 AMJakub Gwóźdź
12/06/2023, 7:45 AMphldavies
12/06/2023, 7:47 AMWerner Altewischer
12/06/2023, 7:51 AMphldavies
12/06/2023, 7:51 AMWerner Altewischer
12/06/2023, 7:52 AMWerner Altewischer
12/06/2023, 7:54 AMWerner Altewischer
12/06/2023, 7:57 AM// Part 2
val totalTime = timeValues.joinToString(separator = "").toLong()
val totalDistance = distValues.joinToString(separator = "").toLong()
val initialEstimate = totalTime / 2
require(distTravelled(totalTime, initialEstimate) > totalDistance)
val lowerBound = binarySearch(0, initialEstimate, inverted = true) {
distTravelled(totalTime, it) > totalDistance
} ?: error("No value found")
val upperBound = binarySearch(initialEstimate, totalTime, inverted = false) {
distTravelled(totalTime, it) > totalDistance
} ?: error("No value found")
// Answer
println(upperBound - lowerBound + 1)
Anirudh
12/06/2023, 7:59 AM.count { }
?
> fortunately, my part 1 method works for part 2, not iterative. find shortest & longest only, then get the diff + 1
still slightly brute-forcey for the edge-cases but not over the whole range:
private fun isPressTimeWin(pressTime: Long, time: Long, distance: Long): Boolean =
(time - pressTime) * pressTime > distance
private fun numberOfWins(time: Long, distance: Long): Long {
val shortest = (1L..<time).first { isPressTimeWin(it, time, distance) }
val longest = (time - 1L downTo 1).first { isPressTimeWin(it, time, distance) }
return longest - shortest + 1
}
the only potential gotcha is the > distance
vs >= distance
check.
since a tie for first place is also a win - but not in AoC puzzles 😱PoisonedYouth
12/06/2023, 8:04 AMJakub Gwóźdź
12/06/2023, 8:08 AMDavio
12/06/2023, 8:08 AMDavio
12/06/2023, 8:17 AMDavio
12/06/2023, 8:31 AMy = tx - x2
Rearranged that gives us
y = -x2 + tx
And remember, t is just a constant
If we take the first example of part 1 and solve for y = 9, the distance we need to beat, we use the quadratic formula (https://en.wikipedia.org/wiki/Quadratic_formula) to get x1 and x2 of this range.
In this case we want to solve ax2 + bx + c= 0
for a = -1, b = t (total time) and c = - distance
This gives us x1 ~ 1.7 and x2 ~ 5.3
Obviously we can't hold the button fractions of a millisecond so we have to round up at the lower end and down at the top end, so we get 2 and 5. This is our range and there are 4 numbers in that range, 2, 3, 4 and 5 and this gives us the total number of winsWerner Altewischer
12/06/2023, 8:32 AM// Part 2
val totalTime = timeValues.joinToString(separator = "").toLong()
val totalDistance = distValues.joinToString(separator = "").toLong()
// x * (totalTime - x) - totalDistance = 0
// -x^2 - totalTime * x - totalDistance = 0
// a = -1, b = -totalTime, c = -totalDistance
val (root1, root2) = solveEquation(-1, -totalTime, -totalDistance)
val upperBound = floor(max(root1, root2)).toLong()
val lowerBound = ceil(min(root1, root2)).toLong()
// Part 2
println(upperBound - lowerBound + 1)
private fun solveEquation(a: Long, b: Long, c: Long): List<Double> {
val d = b * b - 4 * a * c
return if (d >= 0L) {
val denominator = 2.0 * a
if (d == 0L) {
listOf(-b / denominator)
} else {
listOf((-b + sqrt(d.toDouble())) / denominator, (-b - sqrt(d.toDouble())) / denominator)
}
} else {
emptyList()
}
}
Werner Altewischer
12/06/2023, 8:33 AMDavio
12/06/2023, 8:40 AMb = - (distance + 1)
or another way to make sure you solve for the distance + 1 mm to make sure you beat itDavio
12/06/2023, 8:45 AMprivate fun getNumberOfWins(timeInMs: Int, distanceInMm: Long): Int {
val c = -(distanceInMm + 1)
val d = (timeInMs.toLong() * timeInMs - 4 * -1 * c).toDouble()
val root = sqrt(d)
val x1 = ceil((-timeInMs + root) / -2).toLong()
val x2 = ((-timeInMs - root) / -2).toLong()
return (x2 - x1).toInt() + 1
}
So I inlined -1 as a
, timeInMs as b
and - (distanceInMm + 1)
as c
Davio
12/06/2023, 8:47 AMDavio
12/06/2023, 8:52 AMKai Yuan
12/06/2023, 9:27 AMval min = (1..time).first { (time - it) * it > distance }
val max = time - (2..time).first { ((distance) / it) < (time - it) }
max - min + 1 //<-- answer of each race
Davio
12/06/2023, 9:56 AM(0, 0)
it means that the max
value is always the same as time - min
, so let's look at the first example: the min is at x = 2, so the max is at x = 7 - 2 = 5.
This means that we only have to calculate the first value, x1
or the min
or what you want to call it, and simply do x2 = time - x1
Kai Yuan
12/06/2023, 10:00 AMDavio
12/06/2023, 10:38 AMsquared
, sqrt
and roundUp
, it now looks like this:
private fun getNumberOfWins(timeInMs: Long, distanceInMm: Long): Long {
val discriminant = timeInMs.squared + 4 * (-distanceInMm - 1)
val min = ((-timeInMs + discriminant.sqrt) / -2).roundUp()
return (timeInMs - 2 * min) + 1
}
I combined some of the calculation parts which makes it less readable, or less explicit, but at the core it's the same thingkqr
12/06/2023, 10:42 AMRafs
12/06/2023, 11:24 AMzip
todayephemient
12/06/2023, 11:31 AMaList.zip(bList) { f(a, b) }
instead of
aList.zip(bList).map { (a, b) -> f(a, b) }
Jakub Gwóźdź
12/06/2023, 11:37 AMaList.zip(bList) { a, b -> f(a, b) }
?
or
aList.zip(bList, ::f)
?Davio
12/06/2023, 12:26 PMreturn times.zip(distances).multiplicationOf { (t, d) -> getNumberOfWins(t, d) }
It's like sumOf
, but for multiplicationephemient
12/06/2023, 1:01 PMtimes.zip(distances, ::wins).fold(1, Long::times)
phldavies
12/06/2023, 1:02 PM.fold(1, Long::times)
or similar in lieu of .product()
or .productOf {...}
phldavies
12/06/2023, 1:03 PMJakub Gwóźdź
12/06/2023, 1:05 PM.reduce(Long::times)
🙂phldavies
12/06/2023, 1:05 PMDavio
12/06/2023, 1:06 PMJakub Gwóźdź
12/06/2023, 1:55 PMdata class Input(val time: String, val distance: String)
fun String.longs() = split(" ").filterNot(String::isBlank).map(String::toLong)
fun Input.zipLongs() = time.longs().zip(distance.longs(), ::calc)
fun calc(t: Long, d: Long): Long = t - 2 * lastNotBetter(t, d) - 1
fun lastNotBetter(t: Long, d: Long) = (t - (t * t - 4 * d).isqrt(ceil = true)) / 2
fun part1(input: Input) = input
.zipLongs()
.reduce(Long::times)
fun Input.fixForPart2() = Input(time.filterNot(Char::isWhitespace), distance.filterNot(Char::isWhitespace))
fun part2(input: Input) = input
.fixForPart2()
.zipLongs()
.reduce(Long::times)
There is so much you can do with references in kotlin!ephemient
12/06/2023, 1:57 PMfoo.let(fun(foo: Foo) = bar)
Charles Flynn
12/06/2023, 2:00 PMprivate val input = listOf(55 to 401, 99 to 1485, 97 to 2274, 93 to 1405)
private val input2 = 55999793L to 401148522741405
fun main() {
println(part1())
println(part2())
}
private fun part1() = input
.map {
(1..<it.first).count { holdTime -> holdTime * (it.first - holdTime) > it.second }
}.product()
private fun part2() = input2
.let {
(1..<it.first).count { holdTime -> holdTime * (it.first - holdTime) > it.second }
}
Jacob
12/06/2023, 2:03 PMNorbert Kiesel
12/06/2023, 6:08 PMJacob
12/06/2023, 6:18 PMNorbert Kiesel
12/06/2023, 6:21 PMDan Fingal-Surma
12/06/2023, 7:35 PMDan Fingal-Surma
12/06/2023, 7:36 PMJakub Gwóźdź
12/06/2023, 7:36 PMDan Fingal-Surma
12/06/2023, 7:41 PMNeil Banman
12/06/2023, 9:00 PMkqr
12/06/2023, 9:02 PMEgbor Raphael
12/07/2023, 4:08 AMTim R.
12/08/2023, 3:32 AMTim R.
12/08/2023, 3:39 AMKiet
12/11/2023, 6:00 AM