Skip to content

Commit

Permalink
Refactor 2023 day 21 part 2 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Dec 21, 2023
1 parent 18126db commit 48db598
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 86 deletions.
100 changes: 47 additions & 53 deletions src/main/scala/eu/sim642/adventofcode2023/Day21.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,76 +29,70 @@ object Day21 {
SimultaneousBFS.search(graphSearch).distances.count(_._2 % 2 == steps % 2)
}

// copied from 2021 day 25
extension (pos: Pos) {
def %+(other: Pos): Pos = Pos(pos.x %+ other.x, pos.y %+ other.y)
trait Part2Solution {
def countReachableExactlyInfinite(grid: Grid[Char], steps: Int = 26501365): Long
}

def countReachableExactlyInfinite(grid: Grid[Char], steps: Int = 26501365): Int = {
val gridSize = Pos(grid(0).size, grid.size)
object NaivePart2Solution extends Part2Solution {

val graphSearch = new GraphSearch[Pos] with UnitNeighbors[Pos] {
override val startNode: Pos = grid.posOf('S')
// copied from 2021 day 25
extension (pos: Pos) {
def %+(other: Pos): Pos = Pos(pos.x %+ other.x, pos.y %+ other.y)
}

override def unitNeighbors(pos: Pos): IterableOnce[Pos] = {
for {
offset <- Pos.axisOffsets
newPos = pos + offset
if grid(newPos %+ gridSize) != '#'
} yield newPos
override def countReachableExactlyInfinite(grid: Grid[Char], steps: Int = 26501365): Long = {
val gridSize = Pos(grid(0).size, grid.size)

val graphSearch = new GraphSearch[Pos] with UnitNeighbors[Pos] {
override val startNode: Pos = grid.posOf('S')

override def unitNeighbors(pos: Pos): IterableOnce[Pos] = {
for {
offset <- Pos.axisOffsets
newPos = pos + offset
if grid(newPos %+ gridSize) != '#'
} yield newPos
}

override def isTargetNode(pos: Pos, dist: Int): Boolean = dist == steps
}

override def isTargetNode(pos: Pos, dist: Int): Boolean = dist == steps
SimultaneousBFS.search(graphSearch).distances.count(_._2 % 2 == steps % 2)
}

SimultaneousBFS.search(graphSearch).distances.count(_._2 % 2 == steps % 2)
}

def countReachableExactlyInfinite2(grid: Grid[Char], steps: Int = 26501365): Long = {
val (q, r) = steps /% 131
//println((q, r))

val x1 = 0
val y1 = countReachableExactlyInfinite(grid, r)
val x2 = 1
val y2 = countReachableExactlyInfinite(grid, r + 131)
val x3 = 2
val y3 = countReachableExactlyInfinite(grid, r + 2 * 131)

def f(x: Long): Long = {
y1 * (x - x2) * (x - x3) / (x1 - x2) / (x1 - x3) +
y2 * (x - x1) * (x - x3) / (x2 - x1) / (x2 - x3) +
y3 * (x - x1) * (x - x2) / (x3 - x1) / (x3 - x2)
}
object QuadraticPart2Solution extends Part2Solution {

override def countReachableExactlyInfinite(grid: Grid[Char], steps: Int = 26501365): Long = {
val (q, r) = steps /% 131
//println((q, r))

val x1 = 0
val y1 = NaivePart2Solution.countReachableExactlyInfinite(grid, r)
val x2 = 1
val y2 = NaivePart2Solution.countReachableExactlyInfinite(grid, r + 131)
val x3 = 2
val y3 = NaivePart2Solution.countReachableExactlyInfinite(grid, r + 2 * 131)

f(q)

//val gridSize = Pos(grid(0).size, grid.size)
//print(gridSize)
//
//val graphSearch = new GraphSearch[Pos] with UnitNeighbors[Pos] {
// override val startNode: Pos = grid.posOf('S')
//
// override def unitNeighbors(pos: Pos): IterableOnce[Pos] = {
// for {
// offset <- Pos.axisOffsets
// newPos = pos + offset
// if grid(newPos %+ gridSize) != '#'
// } yield newPos
// }
//
// override def isTargetNode(pos: Pos, dist: Int): Boolean = dist == steps
//}
//
//SimultaneousBFS.search(graphSearch).distances.count(_._2 % 2 == steps % 2)
def f(x: Long): Long = {
y1 * (x - x2) * (x - x3) / (x1 - x2) / (x1 - x3) +
y2 * (x - x1) * (x - x3) / (x2 - x1) / (x2 - x3) +
y3 * (x - x1) * (x - x2) / (x3 - x1) / (x3 - x2)
}

f(q)
}
}


def parseGrid(input: String): Grid[Char] = input.linesIterator.map(_.toVector).toVector

lazy val input: String = scala.io.Source.fromInputStream(getClass.getResourceAsStream("day21.txt")).mkString.trim

def main(args: Array[String]): Unit = {
import QuadraticPart2Solution._

println(countReachableExactly(parseGrid(input)))
println(countReachableExactlyInfinite2(parseGrid(input)))
println(countReachableExactlyInfinite(parseGrid(input)))
}
}
95 changes: 62 additions & 33 deletions src/test/scala/eu/sim642/adventofcode2023/Day21Test.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package eu.sim642.adventofcode2023

import Day21._
import Day21.*
import Day21Test.*
import org.scalatest.Suites
import org.scalatest.funsuite.AnyFunSuite
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

class Day21Test extends AnyFunSuite {
class Day21Test extends Suites(
new Part1Test,
new NaivePart2SolutionTest,
new QuadraticPart2SolutionTest,
)

object Day21Test {

private val exampleInput =
"""...........
Expand All @@ -18,45 +27,65 @@ class Day21Test extends AnyFunSuite {
|.##..##.##.
|...........""".stripMargin

test("Part 1 examples") {
assert(countReachableExactly(parseGrid(exampleInput), 6) == 16)
}
class Part1Test extends AnyFunSuite {

test("Part 1 examples") {
assert(countReachableExactly(parseGrid(exampleInput), 6) == 16)
}

test("Part 1 input answer") {
assert(countReachableExactly(parseGrid(input)) == 3687)
test("Part 1 input answer") {
assert(countReachableExactly(parseGrid(input)) == 3687)
}
}

test("Part 2 examples") {
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 6) == 16)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 10) == 50)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 50) == 1594)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 100) == 6536)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 500) == 167004)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 1000) == 668697)
//assert(countReachableExactlyInfinite(parseGrid(exampleInput), 5000) == 16733044)
class NaivePart2SolutionTest extends AnyFunSuite with ScalaCheckPropertyChecks {

test("Part 2 examples") {
val stepsExpectedReachable = Table(
("steps", "expectedReachable"),
(6, 16),
(10, 50),
(50, 1594),
(100, 6536),
// TODO: optimize
//(500, 167004),
//(1000, 668697),
//(5000, 16733044),
)

forAll(stepsExpectedReachable) { (steps, expectedReachable) =>
assert(NaivePart2Solution.countReachableExactlyInfinite(parseGrid(exampleInput), steps) == expectedReachable)
}
}
}

test("Part 2 input answer") {
//assert(countReachableExactlyInfinite(parseGrid(input), 64) == 3687)
//assert(countReachableExactlyInfinite(parseGrid(input), 128) == 14452)
//assert(countReachableExactlyInfinite(parseGrid(input), 256) == 57375)
//assert(countReachableExactlyInfinite(parseGrid(input), 512) == 228690)
//assert(countReachableExactlyInfinite(parseGrid(input), 1024) == 912913)
//println(Day9.Part1.extrapolate(Seq(15130, 60085, 134866, 239473)))
abstract class Part2SolutionTest(part2Solution: Part2Solution) extends AnyFunSuite with ScalaCheckPropertyChecks {

//assert(countReachableExactlyInfinite(parseGrid(input), 131) == 15130)
//assert(countReachableExactlyInfinite(parseGrid(input), 2 * 131) == 60085)
//assert(countReachableExactlyInfinite(parseGrid(input), 3 * 131) == 134866)
//assert(countReachableExactlyInfinite(parseGrid(input), 4 * 131) == 239473)
//assert(countReachableExactlyInfinite(parseGrid(input), 5 * 131) == 373906)
test("Part 2 input answer") {
// computed by NaivePart2Solution
val stepsExpectedReachable = Table(
("steps", "expectedReachable"),
// TODO: optimize
//(64, 3687),
//(128, 14452),
//(256, 57375),
//(512, 228690),
//(1024, 912913),

(131, 15130),
//(2 * 131, 60085),
//(3 * 131, 134866),
//(4 * 131, 239473),
//(5 * 131, 373906),
)

//assert(countReachableExactlyInfinite2(parseGrid(input), 64) == 3687)
//assert(countReachableExactlyInfinite2(parseGrid(input), 128) == 14452)
//assert(countReachableExactlyInfinite2(parseGrid(input), 256) == 57375)
//assert(countReachableExactlyInfinite2(parseGrid(input), 512) == 228690)
//assert(countReachableExactlyInfinite2(parseGrid(input), 1024) == 912913)
forAll(stepsExpectedReachable) { (steps, expectedReachable) =>
assert(part2Solution.countReachableExactlyInfinite(parseGrid(input), steps) == expectedReachable)
}

assert(countReachableExactlyInfinite2(parseGrid(input)) == 610321885082978L)
assert(part2Solution.countReachableExactlyInfinite(parseGrid(input)) == 610321885082978L)
}
}

class QuadraticPart2SolutionTest extends Part2SolutionTest(QuadraticPart2Solution)
}

0 comments on commit 48db598

Please sign in to comment.