-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import java.math.BigDecimal | ||
|
||
fun main() { | ||
val input = readInput("Day24") | ||
|
||
data class V3(val x: Long, val y: Long, val z: Long) { | ||
override fun toString(): String = "$x, $y, $z" | ||
} | ||
data class Stone(val p: V3, val v: V3) { | ||
override fun toString(): String = "$p @ $v" | ||
} | ||
|
||
fun String.toV3(): V3 = split(",") | ||
.map { it.trim().toLong() } | ||
.let { (x, y, z) -> V3(x, y, z) } | ||
|
||
val stones = input.map { s -> | ||
val (p, v) = s.split("@") | ||
Stone(p.toV3(), v.toV3()) | ||
} | ||
// println(stones.size) | ||
|
||
data class Line(val a: BigDecimal, val b: BigDecimal, val c: BigDecimal) | ||
|
||
fun Stone.toLine(): Line { | ||
val a = v.y.toBigDecimal() | ||
val b = (-v.x).toBigDecimal() | ||
val c = a * p.x.toBigDecimal() + b * p.y.toBigDecimal() | ||
return Line(a, b, c) | ||
} | ||
|
||
fun det(a: BigDecimal, b: BigDecimal, c: BigDecimal, d: BigDecimal): BigDecimal = a * d - b * c | ||
|
||
// val rMin = 7.toBigDecimal() | ||
// val rMax = 27.toBigDecimal() | ||
val rMin = 200000000000000.toBigDecimal() | ||
val rMax = 400000000000000.toBigDecimal() | ||
|
||
// n / d in rMin..rMax | ||
fun inRange(n: BigDecimal, d: BigDecimal): Boolean = when { | ||
d > BigDecimal.ZERO -> n in (rMin * d)..(rMax * d) | ||
d < BigDecimal.ZERO -> n in (rMax * d)..(rMin * d) | ||
else -> error("!!!") | ||
} | ||
|
||
fun isFuture(p: Long, v: Long, n: BigDecimal, d: BigDecimal): Boolean = when { | ||
v > 0 -> if (d > BigDecimal.ZERO) n > d * p.toBigDecimal() else n < d * p.toBigDecimal() | ||
v < 0 -> if (d > BigDecimal.ZERO) n < d * p.toBigDecimal() else n > d * p.toBigDecimal() | ||
else -> error("!!!") | ||
} | ||
|
||
var cnt = 0 | ||
for (i in stones.indices) for (j in i + 1..<stones.size) { | ||
val si = stones[i] | ||
val li = si.toLine() | ||
val sj = stones[j] | ||
val lj = sj.toLine() | ||
val d = det(li.a, li.b, lj.a, lj.b) | ||
if (d == BigDecimal.ZERO) { | ||
if (li.a * sj.p.x.toBigDecimal() + li.b * sj.p.y.toBigDecimal() == li.c) { | ||
error(" !!! same") | ||
} | ||
continue | ||
} | ||
val nx = det(li.c, li.b, lj.c, lj.b) // x = nx / d | ||
val ny = det(li.a, li.c, lj.a, lj.c) // y = ny / d | ||
if (!inRange(nx, d) || !inRange(ny, d)) continue | ||
if (!isFuture(si.p.x, si.v.x, nx, d) || !isFuture(si.p.y, si.v.y, ny, d)) continue | ||
if (!isFuture(sj.p.x, sj.v.x, nx, d) || !isFuture(sj.p.y, sj.v.y, ny, d)) continue | ||
// println("intersection: ${nx.toDouble() / d.toDouble()}, ${ny.toDouble() / d.toDouble()}") | ||
// println(" A: $si") | ||
// println(" B: $sj") | ||
cnt++ | ||
} | ||
println(cnt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
fun main() { | ||
val input = readInput("Day24") | ||
|
||
data class V3(val x: Long, val y: Long, val z: Long) { | ||
override fun toString(): String = "$x, $y, $z" | ||
} | ||
data class Stone(val p: V3, val v: V3) { | ||
override fun toString(): String = "$p @ $v" | ||
} | ||
|
||
fun String.toV3(): V3 = split(",") | ||
.map { it.trim().toLong() } | ||
.let { (x, y, z) -> V3(x, y, z) } | ||
|
||
val stones = input.map { s -> | ||
val (p, v) = s.split("@") | ||
Stone(p.toV3(), v.toV3()) | ||
} | ||
// println(stones.size) | ||
|
||
data class Line(val a: Long, val b: Long, val c: Long) | ||
|
||
fun Stone.toLine(): Line { | ||
val a = v.y | ||
val b = -v.x | ||
val c = Math.multiplyExact(a, p.x) + Math.multiplyExact(b, p.y) | ||
return Line(a, b, c) | ||
} | ||
|
||
fun det(a: Long, b: Long, c: Long, d: Long): Double = a.toDouble() * d - b.toDouble() * c.toDouble() | ||
|
||
// val rMin = 7 | ||
// val rMax = 27 | ||
val rMin = 200000000000000 | ||
val rMax = 400000000000000 | ||
|
||
// n / d in rMin..rMax | ||
fun inRange(t: Double): Boolean = t >= rMin && t <= rMax | ||
|
||
fun isFuture(p: Long, v: Long, t: Double): Boolean = when { | ||
v > 0 -> t >= p | ||
v < 0 -> t <= p | ||
else -> error("!!!") | ||
} | ||
|
||
var cnt = 0 | ||
for (i in stones.indices) for (j in i + 1..<stones.size) { | ||
val si = stones[i] | ||
val li = si.toLine() | ||
val sj = stones[j] | ||
val lj = sj.toLine() | ||
val d = det(li.a, li.b, lj.a, lj.b) | ||
if (d == 0.0) continue | ||
val x = det(li.c, li.b, lj.c, lj.b) / d | ||
val y = det(li.a, li.c, lj.a, lj.c) / d | ||
if (!inRange(x) || !inRange(y)) continue | ||
if (!isFuture(si.p.x, si.v.x, x) || !isFuture(si.p.y, si.v.y, y)) continue | ||
if (!isFuture(sj.p.x, sj.v.x, x) || !isFuture(sj.p.y, sj.v.y, y)) continue | ||
cnt++ | ||
} | ||
println(cnt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import java.math.BigInteger | ||
import kotlin.math.* | ||
|
||
fun main() { | ||
val input = readInput("Day24") | ||
|
||
data class V3(val x: Long, val y: Long, val z: Long) { | ||
override fun toString(): String = "$x, $y, $z" | ||
} | ||
data class Stone(val p: V3, val v: V3) { | ||
override fun toString(): String = "$p @ $v" | ||
} | ||
|
||
fun String.toV3(): V3 = split(",") | ||
.map { it.trim().toLong() } | ||
.let { (x, y, z) -> V3(x, y, z) } | ||
|
||
val stones = input.map { s -> | ||
val (p, v) = s.split("@") | ||
Stone(p.toV3(), v.toV3()) | ||
} | ||
|
||
fun Long.divSurely(that: Long): Long { | ||
check(this % that == 0L) | ||
return this / that | ||
} | ||
|
||
data class Range(val pi: LongRange, val v: Long) | ||
|
||
fun checkTimes(coord: String, ps: List<Long>, vs: List<Long>): Long { | ||
val n = ps.size | ||
check(vs.size == n) | ||
|
||
val minV = -1000L | ||
val maxV = 1000L | ||
val minP = 0L | ||
val maxP = 1_000_000_000_000_000L | ||
check(minV < vs.min() && maxV > vs.max()) | ||
check(minP < ps.min() && maxP > ps.max()) | ||
val vss = vs.zip(ps).groupBy { it.first }.mapValues { e -> e.value.map { it.second }.toSet() } | ||
val rs = ArrayList<Range>() | ||
|
||
tailrec fun gcd(x: BigInteger, y: BigInteger): BigInteger = if (y == BigInteger.ZERO) x else gcd(y, x % y) | ||
fun lcm(x: BigInteger, y: BigInteger) = x * y / gcd(x, y) | ||
fun BigInteger.floorDiv(d: BigInteger): BigInteger = | ||
if (this >= BigInteger.ZERO) divide(d) else -(-this + d - BigInteger.ONE).divide(d) | ||
|
||
fun Long.floorDiv(d: BigInteger): BigInteger = toBigInteger().floorDiv(d) | ||
fun Long.mod(d: BigInteger): BigInteger = toBigInteger().mod(d) | ||
fun modRoundUp(x: Long, m: BigInteger, r: BigInteger): BigInteger = | ||
(x.floorDiv(m) + if (x.mod(m) <= r) BigInteger.ZERO else BigInteger.ONE) * m + r | ||
|
||
fun modRoundDn(x: Long, m: BigInteger, r: BigInteger): BigInteger = | ||
(x.floorDiv(m) - if (x.mod(m) >= r) BigInteger.ZERO else BigInteger.ONE) * m + r | ||
|
||
vloop@ for (v in minV..maxV) { | ||
val p1 = vs.withIndex().filter { v < it.value }.maxOfOrNull { ps[it.index] } ?: minP | ||
val p2 = vs.withIndex().filter { v > it.value }.minOfOrNull { ps[it.index] } ?: maxP | ||
if (p1 > p2) continue | ||
var pmod = BigInteger.ONE | ||
var prem = BigInteger.ZERO | ||
var p1r = p1 | ||
var p2r = p2 | ||
for (i in 0..<n) { | ||
val pi = ps[i] | ||
val vi = vs[i] | ||
if (v == vi) { | ||
val p0 = vss[v]?.singleOrNull() ?: continue@vloop | ||
if (p0 !in p1r..p2r) continue@vloop | ||
p1r = p0 | ||
p2r = p0 | ||
continue | ||
} | ||
// t_meet = (p - pi) / (vi - v) | ||
val d = abs(vi - v).toBigInteger() | ||
val r = pi.mod(d) | ||
val pmod2 = lcm(pmod, d) | ||
var prem2 = prem | ||
while (prem2 < pmod2) { | ||
if (prem2.remainder(d) == r) break | ||
prem2 += pmod | ||
if (prem2 >= pmod2) continue@vloop | ||
if (prem2 > p2r.toBigInteger()) continue@vloop | ||
} | ||
pmod = pmod2 | ||
prem = prem2 | ||
val p1n = modRoundUp(p1r, pmod, prem) | ||
val p2n = modRoundDn(p2r, pmod, prem) | ||
if (p1n > p2n) continue@vloop | ||
check(p1n >= p1r.toBigInteger()) | ||
check(p2n <= p2r.toBigInteger()) | ||
p1r = p1n.toLong() | ||
p2r = p2n.toLong() | ||
} | ||
println("$coord -> $p1r .. $p2r, v = $v") | ||
rs += Range(p1r..p2r, v) | ||
} | ||
println("$coord: ${rs.size} ranges") | ||
return rs.single().pi.first | ||
} | ||
|
||
val x = checkTimes("x", stones.map { it.p.x }, stones.map { it.v.x }) | ||
val y = checkTimes("y", stones.map { it.p.y }, stones.map { it.v.y }) | ||
val z = checkTimes("z", stones.map { it.p.z }, stones.map { it.v.z }) | ||
|
||
println(x + y + z) | ||
} |