diff --git a/src/day5/Puzzle2.scala b/src/day5/Puzzle2.scala index 7d5028b..f7a0143 100644 --- a/src/day5/Puzzle2.scala +++ b/src/day5/Puzzle2.scala @@ -14,10 +14,15 @@ object Puzzle2 { def backward(dst: Long): Long = startSrc + dst - startDst override def toString: String = s"[$startSrc;$endSrc[ -> ($dstTable) [$startDst;$endDst[" } + class Table(val id: Int) { var ranges: ArrayBuffer[Range] = new ArrayBuffer() def addRange(range: Range): Unit = ranges.addOne(range) + + /** + * Sorts the ranges in ascending order + */ def sortRanges(): Unit = { var changed: Boolean = false do { @@ -33,6 +38,9 @@ object Puzzle2 { } while (changed) } + /** + * Fills the gaps between ranges with identity ranges (i.e. which keep values unchanged) + */ def fillGaps(): Unit = { var r1: Range = Range(0, 0, 0, id + 1) var r2: Range = r1 @@ -49,12 +57,23 @@ object Puzzle2 { } } + /** + * Forwards a value through this table + * @param src The source to forward + * @return A pair containing the destination value and new table id + */ def forward(src: Long): (Long, Int) = { for (range: Range <- ranges) { if (range.containsSrc(src)) return (range.forward(src), range.dstTable) } return (src, id+1) } + + /** + * Forwards a value in reverse through this table + * @param dst The destination to "backward" + * @return A pair containing the source value and new table id + */ def backward(dst: Long): (Long, Int) = { for (range: Range <- ranges) { if (range.containsDst(dst)) return (range.backward(dst), id) @@ -67,6 +86,7 @@ object Puzzle2 { var forwardingTables: Array[Table] = Array.empty var seeds: Array[Long] = Array.empty + def loadInput(path: String): Unit = { val source: BufferedSource = Source.fromFile(path) @@ -89,6 +109,9 @@ object Puzzle2 { source.close() } + /** + * Reorganizes all the tables by sorting the ranges and filling the gaps between them + */ def sortTables(): Unit = { for (table: Table <- forwardingTables) { table.sortRanges() @@ -96,50 +119,24 @@ object Puzzle2 { } } - def simplifyTables(): Unit = { + /** + * Collapses all forwarding tables into a single one, mapping seeds to locations + */ + def collapseTables(): Unit = { for (i: Int <- forwardingTables.length - 1 until 0 by -1) { - //simplifyTable(i) - val newTable: Table = collapseTables(forwardingTables(i-1), forwardingTables(i)) + val newTable: Table = collapseTable(forwardingTables(i-1), forwardingTables(i)) forwardingTables(i-1) = newTable } } - def simplifyTable(tableI: Int): Unit = { - val table1: Table = forwardingTables(tableI) - val table2: Table = forwardingTables(tableI+1) - - val newTable1: Table = new Table(table1.id) - val limits: ArrayBuffer[Long] = new ArrayBuffer() - - for (r1: Range <- table1.ranges) { - val startFrom1 = r1.startSrc - val endFrom1 = startFrom1 + r1.length - val startTo1 = r1.startDst - val endTo1 = startTo1 + r1.length - - limits.addOne(startTo1) - limits.addOne(endTo1) - - for (r2: Range <- table2.ranges) { - val startFrom2 = r2.startSrc - val endFrom2 = startFrom2 + r2.length - val startTo2 = r2.startDst - val endTo2 = startTo2 + r2.length - - limits.addOne(startFrom2) - limits.addOne(endFrom2) - - // If overlap - if (!(endTo1 < startFrom2 || startTo1 > endFrom2)) { - - } - } - } - - forwardingTables(tableI) = newTable1 - } - - def collapseTables(table1: Table, table2: Table): Table = { + /** + * Collapses a forwarding table with the next tables + * @param table1 The table to collapse + * @param table2 The next table + * @return A new table equivalent to the fusion of both tables + */ + def collapseTable(table1: Table, table2: Table): Table = { + // Find all new range limits val limitsSet: mutable.Set[Long] = new mutable.HashSet() limitsSet.addOne(table1.ranges(0).startSrc) for (range: Range <- table1.ranges) { @@ -152,6 +149,8 @@ object Puzzle2 { val newTable: Table = new Table(table1.id) val limits: Array[Long] = limitsSet.toArray.sorted + + // For each limit, create the corresponding range by forwarding table 1 through table 2 for (i: Int <- 0 until limits.length - 1) { val src1: Long = limits(i) val dst1: (Long, Int) = table1.forward(src1) @@ -162,42 +161,40 @@ object Puzzle2 { return newTable } - def forward(value: Long, tableI: Int): Long = { - for (range: Range <- forwardingTables(tableI).ranges) { - if (value >= range.startSrc) { - if (value < range.startSrc + range.length) { - return value - range.startSrc + range.startDst - } - } else { - return value - } - } - return value - } def solve(path: String): Long = { println("Solving puzzle 2") loadInput(path) println("Loaded input") sortTables() - for (table: Table <- forwardingTables) { + /*for (table: Table <- forwardingTables) { println(table) - } + }*/ println("Sorted forwarding tables") - simplifyTables() + collapseTables() println("Simplified forwarding tables") val table: Table = forwardingTables(0) - println(table) + //println(table) var smallest: Long = -1 for (i: Int <- seeds.indices by 2) { - println(s"Mapping range of seed [${seeds(i)},${seeds(i)+seeds(i+1)}]") - for (seed: Long <- seeds(i) until seeds(i)+seeds(i+1)) { - val location: Long = table.forward(seed)._1 - if (smallest == -1 || location < smallest) { - smallest = location - } + val seedStart: Long = seeds(i) + val seedEnd: Long = seedStart + seeds(i+1) - 1 + + // Find range limits lying inside seed range + val limitsSet: mutable.Set[Long] = new mutable.HashSet() + limitsSet.addOne(seedStart) + limitsSet.addOne(seedEnd) + for (r2: Range <- table.ranges) { + if (r2.startSrc > seedStart && r2.startSrc < seedEnd) limitsSet.add(r2.startSrc) + if (r2.endSrc > seedStart && r2.endSrc < seedEnd) limitsSet.add(r2.startSrc) + } + val limits: Array[Long] = limitsSet.toArray.sorted + + // For each limit, forward its value and compare with current smallest + for (i: Int <- 0 until limits.length - 1) { + val value: Long = table.forward(limits(i))._1 + if (smallest == -1 || value < smallest) smallest = value } - println(s"New smallest location: $smallest") } return smallest }