Compare commits
	
		
			29 Commits
		
	
	
		
			9f27d02b93
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c429c5bbb | |||
| 56b7f06e77 | |||
| abeeee237c | |||
| 78b3d77a29 | |||
| e6ef68d818 | |||
| 802404ac1f | |||
| 67c609e489 | |||
| d36d403402 | |||
| 6b134b55c5 | |||
| 0fb66d1bc7 | |||
| c693cd9259 | |||
| 470eeacbf2 | |||
| a234ea59b3 | |||
| dedcd1cc70 | |||
| 651418bea3 | |||
| 8d55a3c7b3 | |||
| ee3a02060b | |||
| 0f8f8c3f19 | |||
| c3c9a8a4e4 | |||
| 76427545d6 | |||
| 3f2f2fd741 | |||
| a170fb9b50 | |||
| 6493ca83f0 | |||
| 86ca2c95af | |||
| c8ea1636e1 | |||
| 4e1cd00e53 | |||
| 123d0db8b2 | |||
| 43f355cdd8 | |||
| 6d95994276 | 
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,11 +5,11 @@ This repo contains my attempt at this year's Advent of Code (2023) | |||||||
| I will try and do some problems (probably not all of them) in Scala | I will try and do some problems (probably not all of them) in Scala | ||||||
|  |  | ||||||
| ## Progress: | ## Progress: | ||||||
| #### Stars: 8 / 50 | #### Stars: 30 / 50 | ||||||
| |        Mon        | Tue | Wed | Thu |        Fri        |        Sat        |        Sun        | | |        Mon         |        Tue        |        Wed         |        Thu         |        Fri         |        Sat         |        Sun         | | ||||||
| |:-----------------:|:---:|:---:|:---:|:-----------------:|:-----------------:|:-----------------:| | |:------------------:|:-----------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:| | ||||||
| |                   |     |     |     | 1<br>:star::star: | 2<br>:star::star: | 3<br>:star::star: | | |                    |                   |                    |                    | 1<br>:star::star:  | 2<br>:star::star:  | 3<br>:star::star:  | | ||||||
| | 4<br>:star::star: |  5  |  6  |  7  |         8         |         9         |        10         | | | 4<br>:star::star:  | 5<br>:star::star: | 6<br>:star::star:  | 7<br>:star::star:  | 8<br>:star::star:  | 9<br>:star::star:  | 10<br>:star::star: | | ||||||
| |        11         | 12  | 13  | 14  |        15         |        16         |        17         | | | 11<br>:star::star: |        12         | 13<br>:star::star: | 14<br>:star::star: | 15<br>:star::star: | 16<br>:star::star: |         17         | | ||||||
| |        18         | 19  | 20  | 21  |        22         |        23         |        24         | | |         18         |        19         |         20         |         21         |         22         |         23         |         24         | | ||||||
| |        25         |     |     |     |                   |                   |                   | | |         25         |                   |                    |                    |                    |                    |                    | | ||||||
							
								
								
									
										77
									
								
								src/day10/Painter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/day10/Painter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  |  | ||||||
|  | class Painter(startX: Int, startY: Int, excludeX: Int, excludeY: Int) extends Walker(startX, startY, excludeX, excludeY) { | ||||||
|  |   def walk(grid: Array[Array[Byte]], zones: Array[Array[Int]], path: ArrayBuffer[(Int, Int)]): Unit = { | ||||||
|  |     val prevX: Int = lastX | ||||||
|  |     val prevY: Int = lastY | ||||||
|  |     val curX: Int = x | ||||||
|  |     val curY: Int = y | ||||||
|  |     val curTile: Byte = grid(y)(x) | ||||||
|  |     super.walk(grid) | ||||||
|  |     val newX: Int = x | ||||||
|  |     val newY: Int = y | ||||||
|  |     val dx: Int = newX - prevX | ||||||
|  |     val dy: Int = newY - prevY | ||||||
|  |  | ||||||
|  |     // West-East | ||||||
|  |     if (curTile == 5) { | ||||||
|  |       // Going east | ||||||
|  |       if (dx > 0) { | ||||||
|  |         setZone(path, zones, curX, curY - 1, 1) | ||||||
|  |         setZone(path, zones, curX, curY + 1, 2) | ||||||
|  |  | ||||||
|  |         // Going west | ||||||
|  |       } else { | ||||||
|  |         setZone(path, zones, curX, curY - 1, 2) | ||||||
|  |         setZone(path, zones, curX, curY + 1, 1) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     // North-South | ||||||
|  |     } else if (curTile == 10) { | ||||||
|  |       // Going south | ||||||
|  |       if (dy > 0) { | ||||||
|  |         setZone(path, zones, curX+1, curY, 1) | ||||||
|  |         setZone(path, zones, curX-1, curY, 2) | ||||||
|  |  | ||||||
|  |       // Going north | ||||||
|  |       } else { | ||||||
|  |         setZone(path, zones, curX+1, curY, 2) | ||||||
|  |         setZone(path, zones, curX-1, curY, 1) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     // Corner | ||||||
|  |     } else if (curTile != 15) { | ||||||
|  |       val east: Boolean = (curTile & 1) != 0 | ||||||
|  |       val south: Boolean = (curTile & 2) != 0 | ||||||
|  |  | ||||||
|  |       val toEast: Boolean = dx > 0 | ||||||
|  |       val toSouth: Boolean = dy > 0 | ||||||
|  |  | ||||||
|  |       val offsetBottom: Boolean = east ^ toEast ^ toSouth | ||||||
|  |       val zoneY: Boolean = !toSouth ^ east | ||||||
|  |  | ||||||
|  |       val offsetRight: Boolean = south ^ toEast ^ toSouth | ||||||
|  |       val zoneX: Boolean = toEast ^ south | ||||||
|  |  | ||||||
|  |       val offsetY: Int = if (offsetBottom) 1 else -1 | ||||||
|  |       val zoneYVal: Int = if (zoneY) 2 else 1 | ||||||
|  |       setZone(path, zones, curX, curY+offsetY, zoneYVal) | ||||||
|  |  | ||||||
|  |       val offsetX: Int = if (offsetRight) 1 else -1 | ||||||
|  |       val zoneXVal: Int = if (zoneX) 2 else 1 | ||||||
|  |       setZone(path, zones, curX+offsetX, curY, zoneXVal) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private def setZone(path: ArrayBuffer[(Int, Int)], zones: Array[Array[Int]], posX: Int, posY: Int, zone: Int): Unit = { | ||||||
|  |     val height: Int = zones.length | ||||||
|  |     val width: Int = if (height == 0) 0 else zones(0).length | ||||||
|  |     if (0 <= posX && posX < width && 0 <= posY && posY < height) { | ||||||
|  |       if (!path.contains((posX, posY))) { | ||||||
|  |         zones(posY)(posX) = zone | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								src/day10/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/day10/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | import scala.collection.immutable.HashMap | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var grid: Array[Array[Byte]] = Array.empty | ||||||
|  |  | ||||||
|  |   // bits: NWSE | ||||||
|  |   val TILES: Map[Char, Byte] = HashMap( | ||||||
|  |     '|' -> 10, | ||||||
|  |     '-' -> 5, | ||||||
|  |     'L' -> 9, | ||||||
|  |     'J' -> 12, | ||||||
|  |     '7' -> 6, | ||||||
|  |     'F' -> 3, | ||||||
|  |     '.' -> 0, | ||||||
|  |     'S' -> 15 | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   var height: Int = 0 | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var startX: Int = 0 | ||||||
|  |   var startY: Int = 0 | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     height = lines.length | ||||||
|  |     width = if (height == 0) 0 else lines(0).length | ||||||
|  |     grid = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         grid(y)(x) = TILES(c) | ||||||
|  |         if (c == 'S') { | ||||||
|  |           startX = x | ||||||
|  |           startY = y | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def calculateMaxDistance(): Int = { | ||||||
|  |     val walker1: Walker = new Walker(startX, startY) | ||||||
|  |     walker1.walk(grid) | ||||||
|  |     val walker2: Walker = new Walker(startX, startY, walker1.getX(), walker1.getY()) | ||||||
|  |  | ||||||
|  |     while (walker1.getPos() != walker2.getPos()) { | ||||||
|  |       walker2.walk(grid) | ||||||
|  |       if (walker1.getPos() == walker2.getPos()) { | ||||||
|  |         return walker2.getDistance() | ||||||
|  |       } | ||||||
|  |       walker1.walk(grid) | ||||||
|  |     } | ||||||
|  |     return walker1.getDistance() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     val solution: Int = calculateMaxDistance() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day10/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										195
									
								
								src/day10/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								src/day10/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,195 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | import util.Ansi | ||||||
|  |  | ||||||
|  | import scala.collection.immutable.HashMap | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var grid: Array[Array[Byte]] = Array.empty | ||||||
|  |   var zones: Array[Array[Int]] = Array.empty | ||||||
|  |  | ||||||
|  |   // bits: NWSE | ||||||
|  |   val TILES: Map[Char, Byte] = HashMap( | ||||||
|  |     '|' -> 10, | ||||||
|  |     '-' -> 5, | ||||||
|  |     'L' -> 9, | ||||||
|  |     'J' -> 12, | ||||||
|  |     '7' -> 6, | ||||||
|  |     'F' -> 3, | ||||||
|  |     '.' -> 0, | ||||||
|  |     'S' -> 15 | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   val TILE_CHARS: Map[Int, Char] = HashMap( | ||||||
|  |     10 -> '│', | ||||||
|  |     5 -> '─', | ||||||
|  |     9 -> '└', | ||||||
|  |     12 -> '┘', | ||||||
|  |     6 -> '┐', | ||||||
|  |     3 -> '┌', | ||||||
|  |     0 -> ' ', | ||||||
|  |     15 -> '█' | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   val TILE_CHARS_BOLD: Map[Int, Char] = HashMap( | ||||||
|  |     10 -> '┃', | ||||||
|  |     5 -> '━', | ||||||
|  |     9 -> '┗', | ||||||
|  |     12 -> '┛', | ||||||
|  |     6 -> '┓', | ||||||
|  |     3 -> '┏', | ||||||
|  |     0 -> ' ', | ||||||
|  |     15 -> '█' | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   var height: Int = 0 | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var startX: Int = 0 | ||||||
|  |   var startY: Int = 0 | ||||||
|  |   var path: ArrayBuffer[(Int, Int)] = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     height = lines.length | ||||||
|  |     width = if (height == 0) 0 else lines(0).length | ||||||
|  |     grid = Array.ofDim(height, width) | ||||||
|  |     zones = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         grid(y)(x) = TILES(c) | ||||||
|  |         if (c == 'S') { | ||||||
|  |           startX = x | ||||||
|  |           startY = y | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def display(): Unit = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         val tile: Byte = grid(y)(x) | ||||||
|  |         val zone: Int = zones(y)(x) | ||||||
|  |         if (zone == 1) { | ||||||
|  |           print(Ansi.BG_RGB(163, 61, 61)) | ||||||
|  |         } else if (zone == 2) { | ||||||
|  |           print(Ansi.BG_RGB(77, 163, 61)) | ||||||
|  |         } | ||||||
|  |         if (path.contains((x, y))) { | ||||||
|  |           print(Ansi.BOLD) | ||||||
|  |           print(TILE_CHARS_BOLD(tile)) | ||||||
|  |         } else { | ||||||
|  |           print(TILE_CHARS(tile)) | ||||||
|  |         } | ||||||
|  |         print(Ansi.CLEAR) | ||||||
|  |       } | ||||||
|  |       println() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def calculateArea(): Int = { | ||||||
|  |     path = new ArrayBuffer() | ||||||
|  |     path.addOne((startX, startY)) | ||||||
|  |  | ||||||
|  |     val walker: Walker = new Walker(startX, startY) | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       walker.walk(grid) | ||||||
|  |       path.addOne(walker.getPos()) | ||||||
|  |     } while (walker.getX() != startX || walker.getY() != startY) | ||||||
|  |     println(s"Found path (length = ${path.length})") | ||||||
|  |  | ||||||
|  |     val painter: Painter = new Painter(startX, startY, 2*startX-path(1)._1, 2*startY-path(1)._2) | ||||||
|  |     do { | ||||||
|  |       painter.walk(grid, zones, path) | ||||||
|  |     } while (painter.getX() != startX || painter.getY() != startY) | ||||||
|  |     println("Painted path neighbours") | ||||||
|  |  | ||||||
|  |     var newZones: Array[Array[Int]] = Array.ofDim(height, width) | ||||||
|  |     var changed: Boolean = true | ||||||
|  |     var exteriorZone: Int = 0 | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       changed = false | ||||||
|  |       newZones = copyZones() | ||||||
|  |       for (y: Int <- 0 until height) { | ||||||
|  |         for (x: Int <- 0 until width) { | ||||||
|  |           if (zones(y)(x) != 0) { | ||||||
|  |             if (floodTile(x, y, newZones, path)) { | ||||||
|  |               changed = true | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       zones = newZones | ||||||
|  |     } while (changed) | ||||||
|  |     println("Flooded zones") | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (x == 0 || x == width - 1 || y == 0 || y == height - 1) { | ||||||
|  |           if (zones(y)(x) != 0) { | ||||||
|  |             exteriorZone = zones(y)(x) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     println(s"Found exterior zone: $exteriorZone") | ||||||
|  |  | ||||||
|  |     var area: Int = 0 | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (zones(y)(x) != exteriorZone && zones(y)(x) != 0) area += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     println(s"Calculated area: $area") | ||||||
|  |  | ||||||
|  |     return area | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def floodTile(x: Int, y: Int, newZones: Array[Array[Int]], path: ArrayBuffer[(Int, Int)]): Boolean = { | ||||||
|  |     var changed: Boolean = false | ||||||
|  |     for ((dx: Int, dy: Int) <- Walker.OFFSETS) { | ||||||
|  |       val x2: Int = x + dx | ||||||
|  |       val y2: Int = y + dy | ||||||
|  |       if (0 <= x2 && x2 < width && 0 <= y2 && y2 < height) { | ||||||
|  |         if (zones(y2)(x2) == 0 && !path.contains((x2, y2))) { | ||||||
|  |           newZones(y2)(x2) = zones(y)(x) | ||||||
|  |           changed = true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return changed | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def copyZones(): Array[Array[Int]] = { | ||||||
|  |     val result: Array[Array[Int]] = Array.ofDim(height, width) | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         result(y)(x) = zones(y)(x) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     val solution: Int = calculateArea() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day10/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |     display() | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/day10/Walker.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/day10/Walker.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | class Walker { | ||||||
|  |   protected var x: Int = 0 | ||||||
|  |   protected var y: Int = 0 | ||||||
|  |   protected var lastX: Int = -1 | ||||||
|  |   protected var lastY: Int = -1 | ||||||
|  |   protected var distance: Int = 0 | ||||||
|  |  | ||||||
|  |   def this(startX: Int, startY: Int) = { | ||||||
|  |     this() | ||||||
|  |     x = startX | ||||||
|  |     y = startY | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def this(startX: Int, startY: Int, excludeX: Int, excludeY: Int) = { | ||||||
|  |     this(startX, startY) | ||||||
|  |     lastX = excludeX | ||||||
|  |     lastY = excludeY | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getX(): Int = x | ||||||
|  |   def getY(): Int = y | ||||||
|  |   def getPos(): (Int, Int) = (x, y) | ||||||
|  |   def getDistance(): Int = distance | ||||||
|  |  | ||||||
|  |   def walk(grid: Array[Array[Byte]]): Unit = { | ||||||
|  |     val height: Int = grid.length | ||||||
|  |     val width: Int = if (height == 0) 0 else grid(0).length | ||||||
|  |     val (x2: Int, y2: Int) = getNextPos(grid, width, height) | ||||||
|  |     lastX = x | ||||||
|  |     lastY = y | ||||||
|  |     x = x2 | ||||||
|  |     y = y2 | ||||||
|  |     distance += 1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private def getNextPos(grid: Array[Array[Byte]], width: Int, height: Int): (Int, Int) = { | ||||||
|  |     val curTile: Byte = grid(y)(x) | ||||||
|  |     for (((dx: Int, dy: Int), i: Int) <- Walker.OFFSETS.zipWithIndex) { | ||||||
|  |       val x2: Int = x + dx | ||||||
|  |       val y2: Int = y + dy | ||||||
|  |       if (x2 != lastX || y2 != lastY) { | ||||||
|  |         if (0 <= x2 && x2 < width && 0 <= y2 && y2 < height) { | ||||||
|  |           val bit: Byte = (1 << i).toByte | ||||||
|  |           val bit2: Byte = (1 << ((i + 2) % 4)).toByte | ||||||
|  |           if ((curTile & bit) != 0 && (grid(y2)(x2) & bit2) != 0) { | ||||||
|  |             return (x2, y2) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     throw new Exception("Dead-end path") | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object Walker { | ||||||
|  |   val OFFSETS: Array[(Int, Int)] = Array( | ||||||
|  |     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								src/day11/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/day11/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | package day11 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var ogUniverse: Array[Array[Boolean]] = Array.empty | ||||||
|  |   var ogHeight: Int = 0 | ||||||
|  |   var ogWidth: Int = 0 | ||||||
|  |   var colCounts: Array[Int] = Array.empty | ||||||
|  |   var rowCounts: Array[Int] = Array.empty | ||||||
|  |   var galaxies: ArrayBuffer[(Int, Int)] = new ArrayBuffer() | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     ogHeight = lines.length | ||||||
|  |     ogWidth = if (ogHeight == 0) 0 else lines(0).length | ||||||
|  |     ogUniverse = Array.ofDim(ogHeight, ogWidth) | ||||||
|  |  | ||||||
|  |     colCounts = new Array(ogWidth) | ||||||
|  |     rowCounts = new Array(ogHeight) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         if (c == '#') { | ||||||
|  |           ogUniverse(y)(x) = true | ||||||
|  |           colCounts(x) += 1 | ||||||
|  |           rowCounts(y) += 1 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def findGalaxiesRealPos(): Unit = { | ||||||
|  |     galaxies = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |     var realX: Int = 0 | ||||||
|  |     var realY: Int = 0 | ||||||
|  |  | ||||||
|  |     for (ogY: Int <- 0 until ogHeight) { | ||||||
|  |       realX = 0 | ||||||
|  |       for (ogX: Int <- 0 until ogWidth) { | ||||||
|  |         if (ogUniverse(ogY)(ogX)) { | ||||||
|  |           galaxies.addOne((realX, realY)) | ||||||
|  |         } | ||||||
|  |         if (colCounts(ogX) == 0) realX += 1 | ||||||
|  |         realX += 1 | ||||||
|  |       } | ||||||
|  |       if (rowCounts(ogY) == 0) realY += 1 | ||||||
|  |       realY += 1 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def distance(g1: (Int, Int), g2: (Int, Int)): Int = { | ||||||
|  |     return Math.abs(g2._1 - g1._1) + Math.abs(g2._2 - g1._2) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     findGalaxiesRealPos() | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (i: Int <- galaxies.indices) { | ||||||
|  |       val g1: (Int, Int) = galaxies(i) | ||||||
|  |       for (j: Int <- i+1 until galaxies.length) { | ||||||
|  |         val g2: (Int, Int) = galaxies(j) | ||||||
|  |         solution += distance(g1, g2) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day11/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								src/day11/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/day11/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | package day11 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var ogUniverse: Array[Array[Boolean]] = Array.empty | ||||||
|  |   var ogHeight: Int = 0 | ||||||
|  |   var ogWidth: Int = 0 | ||||||
|  |   var colCounts: Array[Int] = Array.empty | ||||||
|  |   var rowCounts: Array[Int] = Array.empty | ||||||
|  |   var galaxies: ArrayBuffer[(Long, Long)] = new ArrayBuffer() | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     ogHeight = lines.length | ||||||
|  |     ogWidth = if (ogHeight == 0) 0 else lines(0).length | ||||||
|  |     ogUniverse = Array.ofDim(ogHeight, ogWidth) | ||||||
|  |  | ||||||
|  |     colCounts = new Array(ogWidth) | ||||||
|  |     rowCounts = new Array(ogHeight) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         if (c == '#') { | ||||||
|  |           ogUniverse(y)(x) = true | ||||||
|  |           colCounts(x) += 1 | ||||||
|  |           rowCounts(y) += 1 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def findGalaxiesRealPos(age: Int): Unit = { | ||||||
|  |     galaxies = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |     var realX: Long = 0 | ||||||
|  |     var realY: Long = 0 | ||||||
|  |  | ||||||
|  |     for (ogY: Int <- 0 until ogHeight) { | ||||||
|  |       realX = 0 | ||||||
|  |       for (ogX: Int <- 0 until ogWidth) { | ||||||
|  |         if (ogUniverse(ogY)(ogX)) { | ||||||
|  |           galaxies.addOne((realX, realY)) | ||||||
|  |         } | ||||||
|  |         if (colCounts(ogX) == 0) realX += age-1 | ||||||
|  |         realX += 1 | ||||||
|  |       } | ||||||
|  |       if (rowCounts(ogY) == 0) realY += age-1 | ||||||
|  |       realY += 1 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def distance(g1: (Long, Long), g2: (Long, Long)): Long = { | ||||||
|  |     return Math.abs(g2._1 - g1._1) + Math.abs(g2._2 - g1._2) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String, age: Int=2): Long = { | ||||||
|  |     loadInput(path) | ||||||
|  |     findGalaxiesRealPos(age) | ||||||
|  |  | ||||||
|  |     var solution: Long = 0 | ||||||
|  |  | ||||||
|  |     for (i: Int <- galaxies.indices) { | ||||||
|  |       val g1: (Long, Long) = galaxies(i) | ||||||
|  |       for (j: Int <- i+1 until galaxies.length) { | ||||||
|  |         val g2: (Long, Long) = galaxies(j) | ||||||
|  |         solution += distance(g1, g2) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Long = solve("./res/day11/input1.txt", 1000000) | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										92
									
								
								src/day13/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/day13/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | package day13 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var maps: Array[Array[Array[Boolean]]] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: String = source.getLines().mkString("\n") | ||||||
|  |  | ||||||
|  |     val mapsStr: Array[String] = lines.split("\n\n") | ||||||
|  |     maps = new Array(mapsStr.length) | ||||||
|  |  | ||||||
|  |     for ((mapStr: String, i: Int) <- mapsStr.zipWithIndex) { | ||||||
|  |       val rows: Array[String] = mapStr.split("\n") | ||||||
|  |       val map: Array[Array[Boolean]] = new Array(rows.length) | ||||||
|  |       for ((row: String, j: Int) <- rows.zipWithIndex) { | ||||||
|  |         map(j) = row.map(_ == '#').toArray | ||||||
|  |       } | ||||||
|  |       maps(i) = map | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getReflectionValue(map: Array[Array[Boolean]]): Int = { | ||||||
|  |     val height: Int = map.length | ||||||
|  |     val width: Int = if (height == 0) 0 else map(0).length | ||||||
|  |     var value: Int = 0 | ||||||
|  |  | ||||||
|  |     for (x: Int <- 0 until width-1) { | ||||||
|  |       if (isReflection(map, x)) { | ||||||
|  |         value += x+1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val mapT: Array[Array[Boolean]] = transpose(map) | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height-1) { | ||||||
|  |       if (isReflection(mapT, y)) { | ||||||
|  |         value += 100*(y+1) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return value | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def isReflection(map: Array[Array[Boolean]], x: Int): Boolean = { | ||||||
|  |     for (row: Array[Boolean] <- map) { | ||||||
|  |       val a = row.dropRight(row.length - x - 1).reverse | ||||||
|  |       val b = row.drop(x+1) | ||||||
|  |       for (i: Int <- 0 until Math.min(a.length, b.length)) { | ||||||
|  |         if (a(i) != b(i)) { | ||||||
|  |           return false | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def transpose(map: Array[Array[Boolean]]): Array[Array[Boolean]] = { | ||||||
|  |     val height: Int = map.length | ||||||
|  |     val width: Int = if (height == 0) 0 else map(0).length | ||||||
|  |  | ||||||
|  |     val mapT: Array[Array[Boolean]] = Array.ofDim(width, height) | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         mapT(x)(y) = map(y)(x) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return mapT | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (map: Array[Array[Boolean]] <- maps) { | ||||||
|  |       solution += getReflectionValue(map) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day13/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								src/day13/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/day13/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | package day13 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var maps: Array[Array[Array[Boolean]]] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: String = source.getLines().mkString("\n") | ||||||
|  |  | ||||||
|  |     val mapsStr: Array[String] = lines.split("\n\n") | ||||||
|  |     maps = new Array(mapsStr.length) | ||||||
|  |  | ||||||
|  |     for ((mapStr: String, i: Int) <- mapsStr.zipWithIndex) { | ||||||
|  |       val rows: Array[String] = mapStr.split("\n") | ||||||
|  |       val map: Array[Array[Boolean]] = new Array(rows.length) | ||||||
|  |       for ((row: String, j: Int) <- rows.zipWithIndex) { | ||||||
|  |         map(j) = row.map(_ == '#').toArray | ||||||
|  |       } | ||||||
|  |       maps(i) = map | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getReflectionValue(map: Array[Array[Boolean]]): Int = { | ||||||
|  |     val height: Int = map.length | ||||||
|  |     val width: Int = if (height == 0) 0 else map(0).length | ||||||
|  |     var value: Int = 0 | ||||||
|  |  | ||||||
|  |     for (x: Int <- 0 until width-1) { | ||||||
|  |       if (isReflection(map, x)) { | ||||||
|  |         value += x+1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val mapT: Array[Array[Boolean]] = transpose(map) | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height-1) { | ||||||
|  |       if (isReflection(mapT, y)) { | ||||||
|  |         value += 100*(y+1) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return value | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def isReflection(map: Array[Array[Boolean]], x: Int): Boolean = { | ||||||
|  |     var diffs: Int = 0 | ||||||
|  |     for (row: Array[Boolean] <- map) { | ||||||
|  |       val a = row.dropRight(row.length - x - 1).reverse | ||||||
|  |       val b = row.drop(x+1) | ||||||
|  |       for (i: Int <- 0 until Math.min(a.length, b.length)) { | ||||||
|  |         if (a(i) != b(i)) { | ||||||
|  |           diffs += 1 | ||||||
|  |           if (diffs > 1) return false | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return diffs == 1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def transpose(map: Array[Array[Boolean]]): Array[Array[Boolean]] = { | ||||||
|  |     val height: Int = map.length | ||||||
|  |     val width: Int = if (height == 0) 0 else map(0).length | ||||||
|  |  | ||||||
|  |     val mapT: Array[Array[Boolean]] = Array.ofDim(width, height) | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         mapT(x)(y) = map(y)(x) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return mapT | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (map: Array[Array[Boolean]] <- maps) { | ||||||
|  |       solution += getReflectionValue(map) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day13/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								src/day14/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/day14/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | package day14 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   val CHARS: Array[Char] = Array('.', 'O', '#') | ||||||
|  |   var platform: Array[Array[Int]] = Array.empty | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var height: Int = 0 | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     platform = lines.map(_.map(CHARS.indexOf(_)).toArray) | ||||||
|  |     height = platform.length | ||||||
|  |     width = if (height == 0) 0 else platform(0).length | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def slideUp(): Unit = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (platform(y)(x) == 1) { | ||||||
|  |           slideRockUp(x, y) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def slideRockUp(x: Int, y: Int): Unit = { | ||||||
|  |     platform(y)(x) = 0 | ||||||
|  |     for (y2: Int <- y-1 to 0 by -1) { | ||||||
|  |       if (platform(y2)(x) != 0) { | ||||||
|  |         platform(y2+1)(x) = 1 | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     platform(0)(x) = 1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def computeLoad(): Int = { | ||||||
|  |     var load: Int = 0 | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (platform(y)(x) == 1) { | ||||||
|  |           load += height-y | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return load | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     slideUp() | ||||||
|  |  | ||||||
|  |     val solution: Int = computeLoad() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day14/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										153
									
								
								src/day14/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/day14/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | |||||||
|  | package day14 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   val CHARS: Array[Char] = Array('.', 'O', '#') | ||||||
|  |   var platform: Array[Array[Int]] = Array.empty | ||||||
|  |   var prevPlatform: Array[Array[Int]] = Array.empty | ||||||
|  |   var history: ArrayBuffer[Array[Array[Int]]] = new ArrayBuffer() | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var height: Int = 0 | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     platform = lines.map(_.map(CHARS.indexOf(_)).toArray) | ||||||
|  |     height = platform.length | ||||||
|  |     width = if (height == 0) 0 else platform(0).length | ||||||
|  |     prevPlatform = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def slideUp(): Unit = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (platform(y)(x) == 1) { | ||||||
|  |           slideRockUp(x, y) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def slideRockUp(x: Int, y: Int): Unit = { | ||||||
|  |     platform(y)(x) = 0 | ||||||
|  |     for (y2: Int <- y-1 to 0 by -1) { | ||||||
|  |       if (platform(y2)(x) != 0) { | ||||||
|  |         platform(y2+1)(x) = 1 | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     platform(0)(x) = 1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def rotate(): Unit = { | ||||||
|  |     val result: Array[Array[Int]] = Array.ofDim(width, height) | ||||||
|  |     width += height | ||||||
|  |     height = width - height | ||||||
|  |     width = width - height | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         result(y)(x) = platform(width-x-1)(y) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     platform = result | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def cycle(): Unit = { | ||||||
|  |     slideUp() | ||||||
|  |     rotate() | ||||||
|  |     slideUp() | ||||||
|  |     rotate() | ||||||
|  |     slideUp() | ||||||
|  |     rotate() | ||||||
|  |     slideUp() | ||||||
|  |     rotate() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def copyPlatform(): Array[Array[Int]] = { | ||||||
|  |     val result: Array[Array[Int]] = Array.ofDim(height, width) | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         result(y)(x) = platform(y)(x) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def computeLoad(): Int = { | ||||||
|  |     var load: Int = 0 | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (platform(y)(x) == 1) { | ||||||
|  |           load += height-y | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return load | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getLoopStart(): Int = { | ||||||
|  |     for ((prevPlatform: Array[Array[Int]], i: Int) <- history.zipWithIndex) { | ||||||
|  |       if (noChange(prevPlatform)) return i | ||||||
|  |     } | ||||||
|  |     return -1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def noChange(prevPlatform: Array[Array[Int]]): Boolean = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (prevPlatform(y)(x) != platform(y)(x)) { | ||||||
|  |           return false | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def hasLooped(): Boolean = getLoopStart() != -1 | ||||||
|  |  | ||||||
|  |   def repeatCycle(count: Int): Unit = { | ||||||
|  |     for (i: Int <- 0 until count) { | ||||||
|  |       history.prepend(copyPlatform()) | ||||||
|  |       cycle() | ||||||
|  |       //display() | ||||||
|  |       if (hasLooped()) { | ||||||
|  |         val loopStart: Int = getLoopStart() | ||||||
|  |         val loopOffset: Int = (count - i - 1) % (loopStart + 1) | ||||||
|  |         val finalI: Int = loopStart - loopOffset | ||||||
|  |  | ||||||
|  |         platform = history(finalI) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def display(): Unit = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         print(CHARS(platform(y)(x))) | ||||||
|  |       } | ||||||
|  |       println() | ||||||
|  |     } | ||||||
|  |     println() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String, cycles: Int): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     repeatCycle(cycles) | ||||||
|  |  | ||||||
|  |     val solution: Int = computeLoad() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day14/input1.txt", 1000000000) | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								src/day15/Box.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/day15/Box.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  |  | ||||||
|  | class Box(val id: Int) { | ||||||
|  |   var lenses: ArrayBuffer[Lens] = new ArrayBuffer() | ||||||
|  |   var lensesLabels: ArrayBuffer[String] = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |   def addLens(lens: Lens): Unit = { | ||||||
|  |     if (lensesLabels.contains(lens.label)) { | ||||||
|  |       val i: Int = lensesLabels.indexOf(lens.label) | ||||||
|  |       lenses(i) = lens | ||||||
|  |     } else { | ||||||
|  |       lenses.addOne(lens) | ||||||
|  |       lensesLabels.addOne(lens.label) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def removeLens(label: String): Unit = { | ||||||
|  |     if (lensesLabels.contains(label)) { | ||||||
|  |       val i: Int = lensesLabels.indexOf(label) | ||||||
|  |       lenses.remove(i) | ||||||
|  |       lensesLabels.remove(i) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   def getFocusingPower(): Long = { | ||||||
|  |     var power: Long = 0 | ||||||
|  |     for ((lens: Lens, i: Int) <- lenses.zipWithIndex) { | ||||||
|  |       power += (i+1) * lens.focalLength | ||||||
|  |     } | ||||||
|  |     return power | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/day15/Lens.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/day15/Lens.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | class Lens(val label: String, val focalLength: Int) { | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								src/day15/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/day15/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var values: Array[String] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val line: String = source.getLines().mkString | ||||||
|  |  | ||||||
|  |     values = line.split(",") | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def hash(str: String): Int = { | ||||||
|  |     var value: Int = 0 | ||||||
|  |     for (c: Char <- str) { | ||||||
|  |       value += c | ||||||
|  |       value *= 17 | ||||||
|  |       value %= 256 | ||||||
|  |     } | ||||||
|  |     return value | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (value: String <- values) { | ||||||
|  |       solution += hash(value) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day15/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								src/day15/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/day15/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  | import scala.util.matching.Regex | ||||||
|  | import scala.util.matching.Regex.Match | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var steps: Array[String] = Array.empty | ||||||
|  |   val boxes: Array[Box] = new Array(256) | ||||||
|  |   val regex: Regex = new Regex("([a-z]+)(-|=\\d)") | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val line: String = source.getLines().mkString | ||||||
|  |  | ||||||
|  |     for (i: Int <- 0 until 256) { | ||||||
|  |       boxes(i) = new Box(i) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     steps = line.split(",") | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def hash(str: String): Int = { | ||||||
|  |     var value: Int = 0 | ||||||
|  |     for (c: Char <- str) { | ||||||
|  |       value += c | ||||||
|  |       value *= 17 | ||||||
|  |       value %= 256 | ||||||
|  |     } | ||||||
|  |     return value | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getFocusingPower(): Long = { | ||||||
|  |     var power: Long = 0 | ||||||
|  |     for (box: Box <- boxes) { | ||||||
|  |       power += (box.id+1) * box.getFocusingPower() | ||||||
|  |     } | ||||||
|  |     return power | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def placeLenses(): Unit = { | ||||||
|  |     for (step: String <- steps) { | ||||||
|  |       val m: Match = regex.findFirstMatchIn(step).get | ||||||
|  |       val label: String = m.group(1) | ||||||
|  |       val labelHash: Int = hash(label) | ||||||
|  |       val action: Char = m.group(2)(0) | ||||||
|  |       val box: Box = boxes(labelHash) | ||||||
|  |       if (action == '=') { | ||||||
|  |         val lens: Lens = new Lens(label, m.group(2)(1) - '0') | ||||||
|  |         box.addLens(lens) | ||||||
|  |       } else { | ||||||
|  |         box.removeLens(label) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Long = { | ||||||
|  |     loadInput(path) | ||||||
|  |     placeLenses() | ||||||
|  |  | ||||||
|  |     val solution: Long = getFocusingPower() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Long = solve("./res/day15/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/day16/Mirrors.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/day16/Mirrors.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package day16 | ||||||
|  |  | ||||||
|  | object Mirrors extends Enumeration { | ||||||
|  |   type Mirror = Value | ||||||
|  |   val NONE = Value(".") | ||||||
|  |   val DIAGONAL_TL_BR = Value("\\") | ||||||
|  |   val DIAGONAL_BL_TR = Value("/") | ||||||
|  |   val SPLITTER_HOR = Value("-") | ||||||
|  |   val SPLITTER_VER = Value("|") | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								src/day16/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/day16/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | package day16 | ||||||
|  |  | ||||||
|  | import day16.Mirrors.{DIAGONAL_BL_TR, DIAGONAL_TL_BR, Mirror, NONE, SPLITTER_HOR, SPLITTER_VER} | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var mirrors: Array[Array[Mirror]] = Array.empty | ||||||
|  |   var energized: Array[Array[Boolean]] = Array.empty | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var height: Int = 0 | ||||||
|  |   val OFFSETS: Array[(Int, Int)] = Array( | ||||||
|  |     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||||
|  |   ) | ||||||
|  |   val path: ArrayBuffer[(Int, Int, Int)] = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     height = lines.length | ||||||
|  |     width = if (height == 0) 0 else lines(0).length | ||||||
|  |     mirrors = Array.ofDim(height, width) | ||||||
|  |     energized = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         val mirror: Mirror = Mirrors.withName(""+c) | ||||||
|  |         mirrors(y)(x) = mirror | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def shootBeam(startX: Int, startY: Int, startDir: Int): Unit = { | ||||||
|  |     if (startX < 0 || startX >= width || startY < 0 || startY >= height) return | ||||||
|  |     var x: Int = startX | ||||||
|  |     var y: Int = startY | ||||||
|  |     var dir: Int = startDir | ||||||
|  |     var continue: Boolean = true | ||||||
|  |     do { | ||||||
|  |       if (path.contains((x, y, dir))) return | ||||||
|  |       path.addOne((x, y, dir)) | ||||||
|  |  | ||||||
|  |       energized(y)(x) = true | ||||||
|  |  | ||||||
|  |       mirrors(y)(x) match { | ||||||
|  |         case NONE => {} | ||||||
|  |         case DIAGONAL_TL_BR => { | ||||||
|  |           dir = Math.floorDiv(dir, 2)*2 + 1 - (dir % 2) | ||||||
|  |         } | ||||||
|  |         case DIAGONAL_BL_TR => { | ||||||
|  |           dir = 3 - dir | ||||||
|  |         } | ||||||
|  |         case SPLITTER_HOR => { | ||||||
|  |           if (dir % 2 == 1) { | ||||||
|  |             continue = false | ||||||
|  |             shootBeam(x+1, y, 0) | ||||||
|  |             shootBeam(x-1, y, 2) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         case SPLITTER_VER => { | ||||||
|  |           if (dir % 2 == 0) { | ||||||
|  |             continue = false | ||||||
|  |             shootBeam(x, y+1, 1) | ||||||
|  |             shootBeam(x, y-1, 3) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (continue) { | ||||||
|  |         val offset: (Int, Int) = OFFSETS(dir) | ||||||
|  |         x += offset._1 | ||||||
|  |         y += offset._2 | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (x < 0 || x >= width) continue = false | ||||||
|  |       if (y < 0 || y >= height) continue = false | ||||||
|  |     } while (continue) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     shootBeam(0, 0, 0) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (energized(y)(x)) solution += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day16/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								src/day16/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/day16/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | package day16 | ||||||
|  |  | ||||||
|  | import day16.Mirrors.{DIAGONAL_BL_TR, DIAGONAL_TL_BR, Mirror, NONE, SPLITTER_HOR, SPLITTER_VER} | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var mirrors: Array[Array[Mirror]] = Array.empty | ||||||
|  |   var energized: Array[Array[Boolean]] = Array.empty | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var height: Int = 0 | ||||||
|  |   val OFFSETS: Array[(Int, Int)] = Array( | ||||||
|  |     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||||
|  |   ) | ||||||
|  |   var path: ArrayBuffer[(Int, Int, Int)] = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     height = lines.length | ||||||
|  |     width = if (height == 0) 0 else lines(0).length | ||||||
|  |     mirrors = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     for ((line: String, y: Int) <- lines.zipWithIndex) { | ||||||
|  |       for ((c: Char, x: Int) <- line.zipWithIndex) { | ||||||
|  |         val mirror: Mirror = Mirrors.withName(""+c) | ||||||
|  |         mirrors(y)(x) = mirror | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def shootBeam(startX: Int, startY: Int, startDir: Int): Unit = { | ||||||
|  |     if (startX < 0 || startX >= width || startY < 0 || startY >= height) return | ||||||
|  |     var x: Int = startX | ||||||
|  |     var y: Int = startY | ||||||
|  |     var dir: Int = startDir | ||||||
|  |     var continue: Boolean = true | ||||||
|  |     do { | ||||||
|  |       if (path.contains((x, y, dir))) return | ||||||
|  |       path.addOne((x, y, dir)) | ||||||
|  |  | ||||||
|  |       energized(y)(x) = true | ||||||
|  |  | ||||||
|  |       mirrors(y)(x) match { | ||||||
|  |         case NONE => {} | ||||||
|  |         case DIAGONAL_TL_BR => { | ||||||
|  |           dir = Math.floorDiv(dir, 2)*2 + 1 - (dir % 2) | ||||||
|  |         } | ||||||
|  |         case DIAGONAL_BL_TR => { | ||||||
|  |           dir = 3 - dir | ||||||
|  |         } | ||||||
|  |         case SPLITTER_HOR => { | ||||||
|  |           if (dir % 2 == 1) { | ||||||
|  |             continue = false | ||||||
|  |             shootBeam(x+1, y, 0) | ||||||
|  |             shootBeam(x-1, y, 2) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         case SPLITTER_VER => { | ||||||
|  |           if (dir % 2 == 0) { | ||||||
|  |             continue = false | ||||||
|  |             shootBeam(x, y+1, 1) | ||||||
|  |             shootBeam(x, y-1, 3) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (continue) { | ||||||
|  |         val offset: (Int, Int) = OFFSETS(dir) | ||||||
|  |         x += offset._1 | ||||||
|  |         y += offset._2 | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (x < 0 || x >= width) continue = false | ||||||
|  |       if (y < 0 || y >= height) continue = false | ||||||
|  |     } while (continue) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def countEnergized(): Int = { | ||||||
|  |     var count: Int = 0 | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (energized(y)(x)) count += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return count | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def clearEnergized(): Unit = { | ||||||
|  |     energized = Array.ofDim(height, width) | ||||||
|  |     path.clear() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |     val totalSteps: Int = (width + height)*2 | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       print(f"\r${2.0*y/totalSteps*100}%.2f%%") | ||||||
|  |       clearEnergized() | ||||||
|  |       shootBeam(0, y, 0) | ||||||
|  |       solution = Math.max(solution, countEnergized()) | ||||||
|  |  | ||||||
|  |       print(f"\r${(2.0*y+1)/totalSteps*100}%.2f%%") | ||||||
|  |       clearEnergized() | ||||||
|  |       shootBeam(width-1, y, 1) | ||||||
|  |       solution = Math.max(solution, countEnergized()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (x: Int <- 0 until width) { | ||||||
|  |       print(f"\r${(2.0*x+height*2)/totalSteps*100}%.2f%%") | ||||||
|  |       clearEnergized() | ||||||
|  |       shootBeam(x, 0, 1) | ||||||
|  |       solution = Math.max(solution, countEnergized()) | ||||||
|  |  | ||||||
|  |       print(f"\r${(2.0*x+height*2+1)/totalSteps*100}%.2f%%") | ||||||
|  |       clearEnergized() | ||||||
|  |       shootBeam(x, height-1, 3) | ||||||
|  |       solution = Math.max(solution, countEnergized()) | ||||||
|  |     } | ||||||
|  |     println("100%") | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day16/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								src/day18/Painter.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/day18/Painter.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | package day18 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  |  | ||||||
|  | class Painter(startX: Int, startY: Int, excludeX: Int, excludeY: Int) extends Walker(startX, startY, excludeX, excludeY) { | ||||||
|  |   def walk(grid: Array[Array[Byte]], zones: Array[Array[Int]], path: ArrayBuffer[(Int, Int)]): Unit = { | ||||||
|  |     val prevX: Int = lastX | ||||||
|  |     val prevY: Int = lastY | ||||||
|  |     val curX: Int = x | ||||||
|  |     val curY: Int = y | ||||||
|  |     val curTile: Byte = grid(y)(x) | ||||||
|  |     super.walk(grid) | ||||||
|  |     val newX: Int = x | ||||||
|  |     val newY: Int = y | ||||||
|  |     val dx: Int = newX - prevX | ||||||
|  |     val dy: Int = newY - prevY | ||||||
|  |  | ||||||
|  |     // West-East | ||||||
|  |     if (curTile == 5) { | ||||||
|  |       // Going east | ||||||
|  |       if (dx > 0) { | ||||||
|  |         setZone(path, zones, curX, curY - 1, 1) | ||||||
|  |         setZone(path, zones, curX, curY + 1, 2) | ||||||
|  |  | ||||||
|  |         // Going west | ||||||
|  |       } else { | ||||||
|  |         setZone(path, zones, curX, curY - 1, 2) | ||||||
|  |         setZone(path, zones, curX, curY + 1, 1) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     // North-South | ||||||
|  |     } else if (curTile == 10) { | ||||||
|  |       // Going south | ||||||
|  |       if (dy > 0) { | ||||||
|  |         setZone(path, zones, curX+1, curY, 1) | ||||||
|  |         setZone(path, zones, curX-1, curY, 2) | ||||||
|  |  | ||||||
|  |       // Going north | ||||||
|  |       } else { | ||||||
|  |         setZone(path, zones, curX+1, curY, 2) | ||||||
|  |         setZone(path, zones, curX-1, curY, 1) | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     // Corner | ||||||
|  |     } else if (curTile != 15) { | ||||||
|  |       val east: Boolean = (curTile & 1) != 0 | ||||||
|  |       val south: Boolean = (curTile & 2) != 0 | ||||||
|  |  | ||||||
|  |       val toEast: Boolean = dx > 0 | ||||||
|  |       val toSouth: Boolean = dy > 0 | ||||||
|  |  | ||||||
|  |       val offsetBottom: Boolean = east ^ toEast ^ toSouth | ||||||
|  |       val zoneY: Boolean = !toSouth ^ east | ||||||
|  |  | ||||||
|  |       val offsetRight: Boolean = south ^ toEast ^ toSouth | ||||||
|  |       val zoneX: Boolean = toEast ^ south | ||||||
|  |  | ||||||
|  |       val offsetY: Int = if (offsetBottom) 1 else -1 | ||||||
|  |       val zoneYVal: Int = if (zoneY) 2 else 1 | ||||||
|  |       setZone(path, zones, curX, curY+offsetY, zoneYVal) | ||||||
|  |  | ||||||
|  |       val offsetX: Int = if (offsetRight) 1 else -1 | ||||||
|  |       val zoneXVal: Int = if (zoneX) 2 else 1 | ||||||
|  |       setZone(path, zones, curX+offsetX, curY, zoneXVal) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private def setZone(path: ArrayBuffer[(Int, Int)], zones: Array[Array[Int]], posX: Int, posY: Int, zone: Int): Unit = { | ||||||
|  |     val height: Int = zones.length | ||||||
|  |     val width: Int = if (height == 0) 0 else zones(0).length | ||||||
|  |     if (0 <= posX && posX < width && 0 <= posY && posY < height) { | ||||||
|  |       if (!path.contains((posX, posY))) { | ||||||
|  |         zones(posY)(posX) = zone | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										234
									
								
								src/day18/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/day18/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | |||||||
|  | package day18 | ||||||
|  |  | ||||||
|  | import util.Ansi | ||||||
|  |  | ||||||
|  | import scala.collection.immutable.HashMap | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var grid: Array[Array[Byte]] = Array.empty | ||||||
|  |   var zones: Array[Array[Int]] = Array.empty | ||||||
|  |  | ||||||
|  |   val OFFSETS: Array[(Int, Int)] = Array( | ||||||
|  |     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||||
|  |   ) | ||||||
|  |   val DIRS: String = "RDLU" | ||||||
|  |  | ||||||
|  |   // bits: NWSE | ||||||
|  |   val TILES: Map[Char, Byte] = HashMap( | ||||||
|  |     '|' -> 10, | ||||||
|  |     '-' -> 5, | ||||||
|  |     'L' -> 9, | ||||||
|  |     'J' -> 12, | ||||||
|  |     '7' -> 6, | ||||||
|  |     'F' -> 3, | ||||||
|  |     '.' -> 0, | ||||||
|  |     'S' -> 15 | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   val TILE_CHARS: Map[Int, Char] = HashMap( | ||||||
|  |     10 -> '│', | ||||||
|  |     5 -> '─', | ||||||
|  |     9 -> '└', | ||||||
|  |     12 -> '┘', | ||||||
|  |     6 -> '┐', | ||||||
|  |     3 -> '┌', | ||||||
|  |     0 -> ' ', | ||||||
|  |     15 -> '█' | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   val TILE_CHARS_BOLD: Map[Int, Char] = HashMap( | ||||||
|  |     10 -> '┃', | ||||||
|  |     5 -> '━', | ||||||
|  |     9 -> '┗', | ||||||
|  |     12 -> '┛', | ||||||
|  |     6 -> '┓', | ||||||
|  |     3 -> '┏', | ||||||
|  |     0 -> ' ', | ||||||
|  |     15 -> '█' | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   var height: Int = 0 | ||||||
|  |   var width: Int = 0 | ||||||
|  |   var startX: Int = 0 | ||||||
|  |   var startY: Int = 0 | ||||||
|  |   var path: ArrayBuffer[(Int, Int)] = new ArrayBuffer() | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     var x: Int = 0 | ||||||
|  |     var y: Int = 0 | ||||||
|  |  | ||||||
|  |     var minX: Int = 0 | ||||||
|  |     var minY: Int = 0 | ||||||
|  |     var maxX: Int = 0 | ||||||
|  |     var maxY: Int = 0 | ||||||
|  |  | ||||||
|  |     for (line: String <- lines) { | ||||||
|  |       val parts: Array[String] = line.split(" ") | ||||||
|  |       val dir: Int = DIRS.indexOf(parts(0)) | ||||||
|  |       val dist: Int = parts(1).toInt | ||||||
|  |       val offset: (Int, Int) = OFFSETS(dir) | ||||||
|  |       x += offset._1*dist | ||||||
|  |       y += offset._2*dist | ||||||
|  |       minX = math.min(minX, x) | ||||||
|  |       minY = math.min(minY, y) | ||||||
|  |       maxX = math.max(maxX, x) | ||||||
|  |       maxY = math.max(maxY, y) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     width = maxX - minX + 1 | ||||||
|  |     height = maxY - minY + 1 | ||||||
|  |  | ||||||
|  |     grid = Array.ofDim(height, width) | ||||||
|  |     zones = Array.ofDim(height, width) | ||||||
|  |  | ||||||
|  |     x = -minX | ||||||
|  |     y = -minY | ||||||
|  |     var prevDir: Int = DIRS.indexOf(lines.last.split(" ")(0)) | ||||||
|  |     startX = x | ||||||
|  |     startY = y | ||||||
|  |  | ||||||
|  |     for ((line: String, i: Int) <- lines.zipWithIndex) { | ||||||
|  |       val parts: Array[String] = line.split(" ") | ||||||
|  |       val dir: Int = DIRS.indexOf(parts(0)) | ||||||
|  |       val dist: Int = parts(1).toInt | ||||||
|  |       val offset: (Int, Int) = OFFSETS(dir) | ||||||
|  |  | ||||||
|  |       for (_: Int <- 0 until dist) { | ||||||
|  |         val prevBit: Int = 1 << ((prevDir + 2) % 4) | ||||||
|  |         val curBit: Int = 1 << dir | ||||||
|  |         grid(y)(x) = (prevBit| curBit).toByte | ||||||
|  |         x += offset._1 | ||||||
|  |         y += offset._2 | ||||||
|  |         prevDir = dir | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def display(): Unit = { | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         val tile: Byte = grid(y)(x) | ||||||
|  |         val zone: Int = zones(y)(x) | ||||||
|  |         if (zone == 1) { | ||||||
|  |           print(Ansi.BG_RGB(163, 61, 61)) | ||||||
|  |         } else if (zone == 2) { | ||||||
|  |           print(Ansi.BG_RGB(77, 163, 61)) | ||||||
|  |         } | ||||||
|  |         if (path.contains((x, y))) { | ||||||
|  |           print(Ansi.BOLD) | ||||||
|  |           print(TILE_CHARS_BOLD(tile)) | ||||||
|  |         } else { | ||||||
|  |           print(TILE_CHARS(tile)) | ||||||
|  |         } | ||||||
|  |         print(Ansi.CLEAR) | ||||||
|  |       } | ||||||
|  |       println() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def calculateArea(): Int = { | ||||||
|  |     path = new ArrayBuffer() | ||||||
|  |     path.addOne((startX, startY)) | ||||||
|  |  | ||||||
|  |     val walker: Walker = new Walker(startX, startY) | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       walker.walk(grid) | ||||||
|  |       path.addOne(walker.getPos()) | ||||||
|  |     } while (walker.getX() != startX || walker.getY() != startY) | ||||||
|  |     println(s"Found path (length = ${path.length})") | ||||||
|  |  | ||||||
|  |     val painter: Painter = new Painter(startX, startY, 2*startX-path(1)._1, 2*startY-path(1)._2) | ||||||
|  |     do { | ||||||
|  |       painter.walk(grid, zones, path) | ||||||
|  |     } while (painter.getX() != startX || painter.getY() != startY) | ||||||
|  |     println("Painted path neighbours") | ||||||
|  |  | ||||||
|  |     var newZones: Array[Array[Int]] = Array.ofDim(height, width) | ||||||
|  |     var changed: Boolean = true | ||||||
|  |     var exteriorZone: Int = 0 | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       changed = false | ||||||
|  |       newZones = copyZones() | ||||||
|  |       for (y: Int <- 0 until height) { | ||||||
|  |         for (x: Int <- 0 until width) { | ||||||
|  |           if (zones(y)(x) != 0) { | ||||||
|  |             if (floodTile(x, y, newZones, path)) { | ||||||
|  |               changed = true | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       zones = newZones | ||||||
|  |     } while (changed) | ||||||
|  |     println("Flooded zones") | ||||||
|  |  | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (x == 0 || x == width - 1 || y == 0 || y == height - 1) { | ||||||
|  |           if (zones(y)(x) != 0) { | ||||||
|  |             exteriorZone = zones(y)(x) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     println(s"Found exterior zone: $exteriorZone") | ||||||
|  |  | ||||||
|  |     var area: Int = 0 | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         if (zones(y)(x) != exteriorZone) area += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     println(s"Calculated area: $area") | ||||||
|  |  | ||||||
|  |     return area | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def floodTile(x: Int, y: Int, newZones: Array[Array[Int]], path: ArrayBuffer[(Int, Int)]): Boolean = { | ||||||
|  |     var changed: Boolean = false | ||||||
|  |     for ((dx: Int, dy: Int) <- Walker.OFFSETS) { | ||||||
|  |       val x2: Int = x + dx | ||||||
|  |       val y2: Int = y + dy | ||||||
|  |       if (0 <= x2 && x2 < width && 0 <= y2 && y2 < height) { | ||||||
|  |         if (zones(y2)(x2) == 0 && !path.contains((x2, y2))) { | ||||||
|  |           newZones(y2)(x2) = zones(y)(x) | ||||||
|  |           changed = true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return changed | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def copyZones(): Array[Array[Int]] = { | ||||||
|  |     val result: Array[Array[Int]] = Array.ofDim(height, width) | ||||||
|  |     for (y: Int <- 0 until height) { | ||||||
|  |       for (x: Int <- 0 until width) { | ||||||
|  |         result(y)(x) = zones(y)(x) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     val solution: Int = calculateArea() | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day18/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |     //display() | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/day18/Walker.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/day18/Walker.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | package day18 | ||||||
|  |  | ||||||
|  | class Walker { | ||||||
|  |   protected var x: Int = 0 | ||||||
|  |   protected var y: Int = 0 | ||||||
|  |   protected var lastX: Int = -1 | ||||||
|  |   protected var lastY: Int = -1 | ||||||
|  |   protected var distance: Int = 0 | ||||||
|  |  | ||||||
|  |   def this(startX: Int, startY: Int) = { | ||||||
|  |     this() | ||||||
|  |     x = startX | ||||||
|  |     y = startY | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def this(startX: Int, startY: Int, excludeX: Int, excludeY: Int) = { | ||||||
|  |     this(startX, startY) | ||||||
|  |     lastX = excludeX | ||||||
|  |     lastY = excludeY | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getX(): Int = x | ||||||
|  |   def getY(): Int = y | ||||||
|  |   def getPos(): (Int, Int) = (x, y) | ||||||
|  |   def getDistance(): Int = distance | ||||||
|  |  | ||||||
|  |   def walk(grid: Array[Array[Byte]]): Unit = { | ||||||
|  |     val height: Int = grid.length | ||||||
|  |     val width: Int = if (height == 0) 0 else grid(0).length | ||||||
|  |     val (x2: Int, y2: Int) = getNextPos(grid, width, height) | ||||||
|  |     lastX = x | ||||||
|  |     lastY = y | ||||||
|  |     x = x2 | ||||||
|  |     y = y2 | ||||||
|  |     distance += 1 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private def getNextPos(grid: Array[Array[Byte]], width: Int, height: Int): (Int, Int) = { | ||||||
|  |     val curTile: Byte = grid(y)(x) | ||||||
|  |     for (((dx: Int, dy: Int), i: Int) <- Walker.OFFSETS.zipWithIndex) { | ||||||
|  |       val x2: Int = x + dx | ||||||
|  |       val y2: Int = y + dy | ||||||
|  |       if (x2 != lastX || y2 != lastY) { | ||||||
|  |         if (0 <= x2 && x2 < width && 0 <= y2 && y2 < height) { | ||||||
|  |           val bit: Byte = (1 << i).toByte | ||||||
|  |           val bit2: Byte = (1 << ((i + 2) % 4)).toByte | ||||||
|  |           if ((curTile & bit) != 0 && (grid(y2)(x2) & bit2) != 0) { | ||||||
|  |             return (x2, y2) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     throw new Exception("Dead-end path") | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | object Walker { | ||||||
|  |   val OFFSETS: Array[(Int, Int)] = Array( | ||||||
|  |     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								src/day5/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/day5/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | package day5 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   case class Range(startSrc: Long, startDst: Long, length: Long) | ||||||
|  |   var forwardingTables: Array[Array[Range]] = Array.empty | ||||||
|  |   var seeds: Array[Long] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |  | ||||||
|  |     val almanac: String = source.getLines().mkString("\n") | ||||||
|  |     val tables: Array[String] = almanac.split("\n\n") | ||||||
|  |  | ||||||
|  |     forwardingTables = new Array(tables.length - 1) | ||||||
|  |     seeds = tables(0).substring(7).split(" ").map(s => s.toLong) | ||||||
|  |  | ||||||
|  |     for (i: Int <- 1 until tables.length) { | ||||||
|  |       val lines: Array[String] = tables(i).split(":\n")(1).split("\n") | ||||||
|  |       val table: Array[Range] = new Array(lines.length) | ||||||
|  |       for ((line: String, j: Int) <- lines.zipWithIndex) { | ||||||
|  |         val values: Array[Long] = line.split(" ").map(v => v.toLong) | ||||||
|  |         table(j) = Range(values(1), values(0), values(2)) | ||||||
|  |       } | ||||||
|  |       forwardingTables(i-1) = table | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def sortTables(): Unit = { | ||||||
|  |     for (table: Array[Range] <- forwardingTables) { | ||||||
|  |       var changed: Boolean = false | ||||||
|  |       do { | ||||||
|  |         changed = false | ||||||
|  |         for (i: Int <- 0 until table.length-1) { | ||||||
|  |           if (table(i).startSrc >= table(i + 1).startSrc + table(i + 1).length) { | ||||||
|  |             changed = true | ||||||
|  |             val range: Range = table(i) | ||||||
|  |             table(i) = table(i + 1) | ||||||
|  |             table(i + 1) = range | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } while (changed) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   def map(seed: Long): Long = { | ||||||
|  |     var value: Long = seed | ||||||
|  |     for (i: Int <- forwardingTables.indices) { | ||||||
|  |       value = forward(value, i) | ||||||
|  |     } | ||||||
|  |     return value | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def forward(value: Long, tableI: Int): Long = { | ||||||
|  |     for (range: Range <- forwardingTables(tableI)) { | ||||||
|  |       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 = { | ||||||
|  |     loadInput(path) | ||||||
|  |     sortTables() | ||||||
|  |     var smallest: Long = -1 | ||||||
|  |     for (seed: Long <- seeds) { | ||||||
|  |       val location: Long = map(seed) | ||||||
|  |       if (smallest == -1 || location < smallest) { | ||||||
|  |         smallest = location | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return smallest | ||||||
|  |   } | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Long = solve("res/day5/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										205
									
								
								src/day5/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/day5/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | |||||||
|  | package day5 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   case class Range(startSrc: Long, startDst: Long, length: Long, dstTable: Int) { | ||||||
|  |     def endSrc: Long = startSrc + length | ||||||
|  |     def endDst: Long = startDst + length | ||||||
|  |     def containsSrc(src: Long): Boolean = startSrc <= src && src < endSrc | ||||||
|  |     def containsDst(dst: Long): Boolean = startDst <= dst && dst < endDst | ||||||
|  |     def forward(src: Long): Long = startDst + src - startSrc | ||||||
|  |     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 { | ||||||
|  |         changed = false | ||||||
|  |         for (i: Int <- 0 until ranges.length-1) { | ||||||
|  |           if (ranges(i).startSrc >= ranges(i + 1).startSrc + ranges(i + 1).length) { | ||||||
|  |             changed = true | ||||||
|  |             val range: Range = ranges(i) | ||||||
|  |             ranges(i) = ranges(i + 1) | ||||||
|  |             ranges(i + 1) = range | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } 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 | ||||||
|  |       var i: Int = 0 | ||||||
|  |       while (i < ranges.length) { | ||||||
|  |         r2 = ranges(i) | ||||||
|  |         val start: Long = r1.startSrc+r1.length | ||||||
|  |         if (start != r2.startSrc) { | ||||||
|  |           ranges.insert(i, Range(start, start, r2.startSrc - start, id+1)) | ||||||
|  |           i += 1 | ||||||
|  |         } | ||||||
|  |         r1 = r2 | ||||||
|  |         i += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 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) | ||||||
|  |       } | ||||||
|  |       return (dst, id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override def toString: String = s"Table $id:\n  " + ranges.mkString("\n  ") | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var forwardingTables: Array[Table] = Array.empty | ||||||
|  |   var seeds: Array[Long] = Array.empty | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |  | ||||||
|  |     val almanac: String = source.getLines().mkString("\n") | ||||||
|  |     val tables: Array[String] = almanac.split("\n\n") | ||||||
|  |  | ||||||
|  |     forwardingTables = new Array(tables.length - 1) | ||||||
|  |     seeds = tables(0).substring(7).split(" ").map(s => s.toLong) | ||||||
|  |  | ||||||
|  |     for (i: Int <- 1 until tables.length) { | ||||||
|  |       val lines: Array[String] = tables(i).split(":\n")(1).split("\n") | ||||||
|  |       val table: Table = new Table(i-1) | ||||||
|  |       for (line: String <- lines) { | ||||||
|  |         val values: Array[Long] = line.split(" ").map(v => v.toLong) | ||||||
|  |         table.addRange(Range(values(1), values(0), values(2), i)) | ||||||
|  |       } | ||||||
|  |       forwardingTables(i-1) = table | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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() | ||||||
|  |       table.fillGaps() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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) { | ||||||
|  |       val newTable: Table = collapseTable(forwardingTables(i-1), forwardingTables(i)) | ||||||
|  |       forwardingTables(i-1) = newTable | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * 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) { | ||||||
|  |       limitsSet.addOne(range.endSrc) | ||||||
|  |     } | ||||||
|  |     limitsSet.addOne(table1.backward(table2.ranges(0).startSrc)._1) | ||||||
|  |     for (range: Range <- table2.ranges) { | ||||||
|  |       limitsSet.addOne(table1.backward(range.endSrc)._1) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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) | ||||||
|  |       if (dst1._2 != table2.id) throw new Exception("Something weird happened") | ||||||
|  |       val dst2: (Long, Int) = table2.forward(dst1._1) | ||||||
|  |       newTable.addRange(Range(src1, dst2._1, limits(i+1)-src1, dst2._2)) | ||||||
|  |     } | ||||||
|  |     return newTable | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Long = { | ||||||
|  |     println("Solving puzzle 2") | ||||||
|  |     loadInput(path) | ||||||
|  |     println("Loaded input") | ||||||
|  |     sortTables() | ||||||
|  |     /*for (table: Table <- forwardingTables) { | ||||||
|  |       println(table) | ||||||
|  |     }*/ | ||||||
|  |     println("Sorted forwarding tables") | ||||||
|  |     collapseTables() | ||||||
|  |     println("Simplified forwarding tables") | ||||||
|  |     val table: Table = forwardingTables(0) | ||||||
|  |     //println(table) | ||||||
|  |  | ||||||
|  |     var smallest: Long = -1 | ||||||
|  |     for (i: Int <- seeds.indices by 2) { | ||||||
|  |       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 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return smallest | ||||||
|  |   } | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Long = solve("res/day5/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/day6/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/day6/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | package day6 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   case class Race(duration: Int, record: Int) | ||||||
|  |  | ||||||
|  |   var races: Array[Race] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     val durations: Array[Int] = lines(0).split(": +")(1).split(" +").map(d => d.toInt) | ||||||
|  |     val records: Array[Int] = lines(1).split(": +")(1).split(" +").map(r => r.toInt) | ||||||
|  |  | ||||||
|  |     races = new Array(durations.length) | ||||||
|  |  | ||||||
|  |     for (i: Int <- races.indices) { | ||||||
|  |       races(i) = Race(durations(i), records(i)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getMargin(race: Race): Int = { | ||||||
|  |     // dist = (dur - press) * press | ||||||
|  |     // dist-record > 0 <=> f(press) = dur*press - press^2 - record > 0 | ||||||
|  |     // x^2 - d*x + r = 0 | ||||||
|  |     // x = ( d +- sqrt(d^2 - 4r) ) / 2 | ||||||
|  |  | ||||||
|  |     val delta: Double = race.duration*race.duration - 4*race.record | ||||||
|  |     val x0: Double = (race.duration - Math.sqrt(delta))/2 | ||||||
|  |     val x1: Double = (race.duration + Math.sqrt(delta))/2 | ||||||
|  |  | ||||||
|  |     val margin: Int = (Math.ceil(x1) - Math.floor(x0) - 1).toInt | ||||||
|  |     return margin | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 1 | ||||||
|  |     for (race: Race <- races) { | ||||||
|  |       solution *= getMargin(race) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day6/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/day6/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/day6/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | package day6 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   case class Race(duration: Long, record: Long) | ||||||
|  |  | ||||||
|  |   var race: Race = Race(0, 0) | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     val duration: Long = lines(0).split(": +")(1).replace(" ", "").toLong | ||||||
|  |     val record: Long = lines(1).split(": +")(1).replace(" ", "").toLong | ||||||
|  |  | ||||||
|  |     race = Race(duration, record) | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def getMargin(race: Race): Int = { | ||||||
|  |     // dist = (dur - press) * press | ||||||
|  |     // dist-record > 0 <=> f(press) = dur*press - press^2 - record > 0 | ||||||
|  |     // x^2 - d*x + r = 0 | ||||||
|  |     // x = ( d +- sqrt(d^2 - 4r) ) / 2 | ||||||
|  |  | ||||||
|  |     val delta: Double = race.duration*race.duration - 4*race.record | ||||||
|  |     val x0: Double = (race.duration - Math.sqrt(delta))/2 | ||||||
|  |     val x1: Double = (race.duration + Math.sqrt(delta))/2 | ||||||
|  |  | ||||||
|  |     val margin: Int = (Math.ceil(x1) - Math.floor(x0) - 1).toInt | ||||||
|  |  | ||||||
|  |     return margin | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     return getMargin(race) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day6/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										47
									
								
								src/day7/Hand.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/day7/Hand.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  |  | ||||||
|  | class Hand(var cards: Array[Int], val bid: Int) { | ||||||
|  |   def getType(): Int = { | ||||||
|  |     val cardsMap: mutable.Map[Int, Int] = new mutable.HashMap() | ||||||
|  |     for (card: Int <- cards) { | ||||||
|  |       if (!cardsMap.contains(card)) { | ||||||
|  |         cardsMap(card) = 0 | ||||||
|  |       } | ||||||
|  |       cardsMap(card) += 1 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val keys: Array[Int] = cardsMap.keys.toArray | ||||||
|  |     val values: Array[Int] = cardsMap.values.toArray | ||||||
|  |     if (keys.length == 1) { | ||||||
|  |       return 6 | ||||||
|  |     } | ||||||
|  |     if (keys.length == 2) { | ||||||
|  |       if (values.contains(4)) return 5 | ||||||
|  |       if (values.contains(3) && values.contains(2)) return 4 | ||||||
|  |     } | ||||||
|  |     if (values.contains(3)) return 3 | ||||||
|  |     if (values.contains(2)) { | ||||||
|  |       if (values.count(v => v == 2) == 2) return 2 | ||||||
|  |       return 1 | ||||||
|  |     } | ||||||
|  |     return 0 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def isBetter(hand: Hand): Boolean = { | ||||||
|  |     val t1: Int = getType() | ||||||
|  |     val t2: Int = hand.getType() | ||||||
|  |     if (t1 > t2) return true | ||||||
|  |     if (t1 < t2) return false | ||||||
|  |  | ||||||
|  |     for ((c1: Int, c2: Int) <- cards.zip(hand.cards)) { | ||||||
|  |       if (c1 != c2) { | ||||||
|  |         return c1 > c2 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override def toString: String = s"Hand(${cards.mkString}, $bid, ${getType()})" | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								src/day7/Hand2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/day7/Hand2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  |  | ||||||
|  | class Hand2(var cards: Array[Int], val bid: Int) { | ||||||
|  |  | ||||||
|  |   def getType(): Int = { | ||||||
|  |     val cardsMap: mutable.Map[Int, Int] = new mutable.HashMap() | ||||||
|  |     val jokers: Int = cards.count(c => c == 0) | ||||||
|  |     for (card: Int <- cards) { | ||||||
|  |       if (card != 0) { | ||||||
|  |         if (!cardsMap.contains(card)) { | ||||||
|  |           cardsMap(card) = 0 | ||||||
|  |         } | ||||||
|  |         cardsMap(card) += 1 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val keys: Array[Int] = cardsMap.keys.toArray | ||||||
|  |     val values: Array[Int] = cardsMap.values.toArray | ||||||
|  |  | ||||||
|  |     if (jokers == 5) return 6 | ||||||
|  |  | ||||||
|  |     // Five of a kind | ||||||
|  |     for (value: Int <- values) { | ||||||
|  |       if (value + jokers == 5) return 6 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Four of a kind | ||||||
|  |     for (value: Int <- values) { | ||||||
|  |       if (value + jokers == 4) return 5 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Full house | ||||||
|  |     val sortedValues: Array[Int] = values.sorted(Ordering.Int.reverse) | ||||||
|  |     if (sortedValues(0) + sortedValues(1) + jokers >= 5) return 4 | ||||||
|  |  | ||||||
|  |     // Three of a kind | ||||||
|  |     if (sortedValues(0) + jokers >= 3) return 3 | ||||||
|  |  | ||||||
|  |     // Two pairs | ||||||
|  |     if (sortedValues(0) + sortedValues(1) + jokers >= 4) return 2 | ||||||
|  |  | ||||||
|  |     // One pair | ||||||
|  |     if (sortedValues(0) + jokers >= 2) return 1 | ||||||
|  |  | ||||||
|  |     return 0 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def isBetter(hand: Hand2): Boolean = { | ||||||
|  |     val t1: Int = getType() | ||||||
|  |     val t2: Int = hand.getType() | ||||||
|  |     if (t1 > t2) return true | ||||||
|  |     if (t1 < t2) return false | ||||||
|  |  | ||||||
|  |     for ((c1: Int, c2: Int) <- cards.zip(hand.cards)) { | ||||||
|  |       if (c1 != c2) { | ||||||
|  |         return c1 > c2 | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override def toString: String = s"Hand(${cards.mkString}, $bid, ${getType()})" | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								src/day7/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/day7/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   val CARDS: Array[Int] = Array('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') | ||||||
|  |   var hands: Array[Hand] = Array.empty | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     hands = new Array(lines.length) | ||||||
|  |  | ||||||
|  |     for ((line: String, i: Int) <- lines.zipWithIndex) { | ||||||
|  |       val parts: Array[String] = line.split(" ") | ||||||
|  |       val cards: Array[Int] = parts(0).map(c => CARDS.indexOf(c)).toArray | ||||||
|  |       val bid: Int = parts(1).toInt | ||||||
|  |       hands(i) = new Hand(cards, bid) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def sortHands(): Unit = { | ||||||
|  |     hands = hands.sortWith((h1: Hand, h2: Hand) => h2.isBetter(h1)) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     sortHands() | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for ((hand: Hand, i: Int) <- hands.zipWithIndex) { | ||||||
|  |       solution += hand.bid * (i+1) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day7/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								src/day7/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/day7/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   val CARDS: Array[Int] = Array('J', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'Q', 'K', 'A') | ||||||
|  |   var hands: Array[Hand2] = Array.empty | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     hands = new Array(lines.length) | ||||||
|  |  | ||||||
|  |     for ((line: String, i: Int) <- lines.zipWithIndex) { | ||||||
|  |       val parts: Array[String] = line.split(" ") | ||||||
|  |       val cards: Array[Int] = parts(0).map(c => CARDS.indexOf(c)).toArray | ||||||
|  |       val bid: Int = parts(1).toInt | ||||||
|  |       hands(i) = new Hand2(cards, bid) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def sortHands(): Unit = { | ||||||
|  |     hands = hands.sortWith((h1: Hand2, h2: Hand2) => h2.isBetter(h1)) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |     sortHands() | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for ((hand: Hand2, i: Int) <- hands.zipWithIndex) { | ||||||
|  |       solution += hand.bid * (i+1) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day7/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								src/day8/Ghost.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/day8/Ghost.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  |  | ||||||
|  | class Ghost(val startId: String) extends Iterator[Long] { | ||||||
|  |   var preLoopOffsets: ArrayBuffer[Int] = new ArrayBuffer() | ||||||
|  |   var loopOffsets: ArrayBuffer[Int] = new ArrayBuffer() | ||||||
|  |   var visited: ArrayBuffer[(String, Int)] = new ArrayBuffer() | ||||||
|  |   private var distIterator: Iterator[Long] = Iterator.empty | ||||||
|  |  | ||||||
|  |   def computePath(nodes: mutable.Map[String, Node], rule: Array[Int]): Unit = { | ||||||
|  |     var id: String = startId | ||||||
|  |     preLoopOffsets.addOne(0) | ||||||
|  |     var ruleI: Int = 0 | ||||||
|  |  | ||||||
|  |     // While not looped | ||||||
|  |     while (!visited.contains((id, ruleI))) { | ||||||
|  |       //println(s"  Visited $id (ruleI = $ruleI)") | ||||||
|  |       visited.addOne((id, ruleI)) | ||||||
|  |       id = nodes(id).nextNodes(rule(ruleI)) | ||||||
|  |       preLoopOffsets(preLoopOffsets.length-1) += 1 | ||||||
|  |       ruleI = (ruleI + 1) % rule.length | ||||||
|  |       if (id.last == 'Z') { | ||||||
|  |         preLoopOffsets.addOne(0) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val loopStartDist: Int = visited.indexOf((id, ruleI)) | ||||||
|  |     var dist: Int = 0 | ||||||
|  |     var loopStartI: Int = 0 | ||||||
|  |     while (dist < loopStartDist) { | ||||||
|  |       dist += preLoopOffsets(loopStartI) | ||||||
|  |       loopStartI += 1 | ||||||
|  |     } | ||||||
|  |     loopStartI -= 1 | ||||||
|  |     preLoopOffsets(preLoopOffsets.length-1) += preLoopOffsets(loopStartI) - loopStartDist | ||||||
|  |     println(s"  Loop from ($id, $ruleI) (index=$loopStartI)") | ||||||
|  |     println(s"  # of offsets: ${preLoopOffsets.length}") | ||||||
|  |  | ||||||
|  |     loopOffsets.addAll(preLoopOffsets.drop(loopStartI+1)) | ||||||
|  |     preLoopOffsets = preLoopOffsets.dropRight(preLoopOffsets.length - loopStartI - 1) | ||||||
|  |     println(preLoopOffsets.length) | ||||||
|  |     println(loopOffsets.length) | ||||||
|  |     distIterator = new GhostIterator(preLoopOffsets.toArray, loopOffsets.toArray) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override def hasNext: Boolean = true | ||||||
|  |  | ||||||
|  |   override def next(): Long = distIterator.next() | ||||||
|  |  | ||||||
|  |   private class GhostIterator(val preLoopOffsets: Array[Int], val loopOffsets: Array[Int]) extends Iterator[Long] { | ||||||
|  |     private var inLoop: Boolean = false | ||||||
|  |     private var offsetI: Int = 0 | ||||||
|  |     private var dist: Long = 0 | ||||||
|  |     override def hasNext: Boolean = true | ||||||
|  |  | ||||||
|  |     override def next(): Long = { | ||||||
|  |       var offset: Int = 0 | ||||||
|  |       if (inLoop) { | ||||||
|  |         offset = loopOffsets(offsetI) | ||||||
|  |       } else { | ||||||
|  |         offset = preLoopOffsets(offsetI) | ||||||
|  |       } | ||||||
|  |       dist += offset | ||||||
|  |  | ||||||
|  |       offsetI += 1 | ||||||
|  |       if (!inLoop) { | ||||||
|  |         if (offsetI == preLoopOffsets.length) { | ||||||
|  |           offsetI = 0 | ||||||
|  |           inLoop = true | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         offsetI %= loopOffsets.length | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return dist | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/day8/Node.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/day8/Node.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | class Node(val id: String, val nextNodes: Array[String]) { | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/day8/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/day8/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  | import scala.util.matching.Regex | ||||||
|  | import scala.util.matching.Regex.Match | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var rule: Array[Int] = Array.empty | ||||||
|  |   val nodes: mutable.Map[String, Node] = new mutable.HashMap() | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: String = source.getLines().mkString("\n") | ||||||
|  |     val parts: Array[String] = lines.split("\n\n") | ||||||
|  |  | ||||||
|  |     rule = parts(0).map(c => "LR".indexOf(c)).toArray | ||||||
|  |     val regexp: Regex = new Regex("([A-Z]{3}) = \\(([A-Z]{3}), ([A-Z]{3})\\)") | ||||||
|  |     for (line: String <- parts(1).split("\n")) { | ||||||
|  |       val matches: Match = regexp.findFirstMatchIn(line).get | ||||||
|  |       val id: String = matches.group(1) | ||||||
|  |       val left: String = matches.group(2) | ||||||
|  |       val right: String = matches.group(3) | ||||||
|  |       nodes(id) = new Node(id, Array(left, right)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |     var id: String = "AAA" | ||||||
|  |  | ||||||
|  |     while (id != "ZZZ") { | ||||||
|  |       id = nodes(id).nextNodes(rule(solution % rule.length)) | ||||||
|  |       solution += 1 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day8/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								src/day8/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/day8/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  | import scala.util.matching.Regex | ||||||
|  | import scala.util.matching.Regex.Match | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var rule: Array[Int] = Array.empty | ||||||
|  |   val nodes: mutable.Map[String, Node] = new mutable.HashMap() | ||||||
|  |   var ghosts: Array[Ghost] = Array.empty | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: String = source.getLines().mkString("\n") | ||||||
|  |     val parts: Array[String] = lines.split("\n\n") | ||||||
|  |  | ||||||
|  |     rule = parts(0).map(c => "LR".indexOf(c)).toArray | ||||||
|  |     val regexp: Regex = new Regex("([A-Z]{3}) = \\(([A-Z]{3}), ([A-Z]{3})\\)") | ||||||
|  |     for (line: String <- parts(1).split("\n")) { | ||||||
|  |       val matches: Match = regexp.findFirstMatchIn(line).get | ||||||
|  |       val id: String = matches.group(1) | ||||||
|  |       val left: String = matches.group(2) | ||||||
|  |       val right: String = matches.group(3) | ||||||
|  |       nodes(id) = new Node(id, Array(left, right)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Long = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     ghosts = nodes.keys.filter(k => k.last == 'A').map(k => new Ghost(k)).toArray | ||||||
|  |  | ||||||
|  |     val ghostDists: mutable.Map[Ghost, Long] = new mutable.HashMap() | ||||||
|  |     for (ghost: Ghost <- ghosts) { | ||||||
|  |       println(s"Ghost from ${ghost.startId}") | ||||||
|  |       ghost.computePath(nodes, rule) | ||||||
|  |       ghostDists(ghost) = ghost.next() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (ghostDists.values.toArray.distinct.length != 1) { | ||||||
|  |       val minimum: (Ghost, Long) = ghostDists.toArray.minBy(_._2) | ||||||
|  |       ghostDists(minimum._1) = minimum._1.next() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val dists: Array[Long] = ghostDists.values.toArray | ||||||
|  |  | ||||||
|  |     return dists(0) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Long = solve("./res/day8/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/day9/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/day9/Puzzle1.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | package day9 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle1 { | ||||||
|  |   var series: Array[Series] = Array.empty | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     series = new Array(lines.length) | ||||||
|  |  | ||||||
|  |     for ((line: String, i: Int) <- lines.zipWithIndex) { | ||||||
|  |       series(i) = new Series(line.split(" ").map(_.toInt)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (s: Series <- series) { | ||||||
|  |       s.computeDiffs() | ||||||
|  |       solution += s.extrapolate() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day9/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/day9/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/day9/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | package day9 | ||||||
|  |  | ||||||
|  | import scala.io.{BufferedSource, Source} | ||||||
|  |  | ||||||
|  | object Puzzle2 { | ||||||
|  |   var series: Array[Series] = Array.empty | ||||||
|  |  | ||||||
|  |   def loadInput(path: String): Unit = { | ||||||
|  |     val source: BufferedSource = Source.fromFile(path) | ||||||
|  |     val lines: Array[String] = source.getLines().toArray | ||||||
|  |  | ||||||
|  |     series = new Array(lines.length) | ||||||
|  |  | ||||||
|  |     for ((line: String, i: Int) <- lines.zipWithIndex) { | ||||||
|  |       series(i) = new Series(line.split(" ").map(_.toInt)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     source.close() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def solve(path: String): Int = { | ||||||
|  |     loadInput(path) | ||||||
|  |  | ||||||
|  |     var solution: Int = 0 | ||||||
|  |  | ||||||
|  |     for (s: Series <- series) { | ||||||
|  |       s.computeDiffs() | ||||||
|  |       solution += s.reverseExtrapolate() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return solution | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def main(args: Array[String]): Unit = { | ||||||
|  |     val solution: Int = solve("./res/day9/input1.txt") | ||||||
|  |     println(solution) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/day9/Series.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/day9/Series.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | package day9 | ||||||
|  |  | ||||||
|  | import scala.collection.mutable.ArrayBuffer | ||||||
|  |  | ||||||
|  | class Series { | ||||||
|  |   private val values: ArrayBuffer[Array[Int]] = new ArrayBuffer() | ||||||
|  |   def this(initialValues: Array[Int]) = { | ||||||
|  |     this() | ||||||
|  |     values.addOne(initialValues.concat(Array(0))) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def computeDiffs(): Unit = { | ||||||
|  |     while (!(values.last.dropRight(1).distinct.length == 1 && values.last(0) == 0)) { | ||||||
|  |       val diffs: Array[Int] = new Array(values.last.length-1) | ||||||
|  |       for (i: Int <- 0 until diffs.length-1) { | ||||||
|  |         diffs(i) = values.last(i+1) - values.last(i) | ||||||
|  |       } | ||||||
|  |       values.addOne(diffs) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def extrapolate(): Int = { | ||||||
|  |     val len: Int = values(0).length | ||||||
|  |     values.last(values.last.length-1) = 0 | ||||||
|  |     for (i: Int <- values.length-2 to 0 by -1) { | ||||||
|  |       values(i)(len-i-1) = values(i)(len-i-2) + values(i+1)(len-i-2) | ||||||
|  |     } | ||||||
|  |     return values(0).last | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   def reverseExtrapolate(): Int = { | ||||||
|  |     val len: Int = values(0).length | ||||||
|  |     values.last(values.last.length-1) = 0 | ||||||
|  |     for (i: Int <- values.length-2 to 0 by -1) { | ||||||
|  |       values(i)(len-i-1) = values(i)(0) - values(i+1)(len-i-2) | ||||||
|  |     } | ||||||
|  |     return values(0).last | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								src/util/Ansi.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/util/Ansi.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | package util | ||||||
|  |  | ||||||
|  | object Ansi { | ||||||
|  |   val ESC: String = "\u001b[" | ||||||
|  |   val CLEAR: String = code("0") | ||||||
|  |   val BOLD: String = code("1") | ||||||
|  |   val FAINT: String = code("2") | ||||||
|  |   val ITALIC: String = code("3") | ||||||
|  |   val UNDERLINE: String = code("4") | ||||||
|  |   val SLOW_BLINK: String = code("5") | ||||||
|  |   val RAPID_BLINK: String = code("6") | ||||||
|  |   val REVERSE: String = code("7") | ||||||
|  |   val CONCEAL: String = code("8") | ||||||
|  |   val STRIKETHROUGH: String = code("9") | ||||||
|  |   def FG_RGB(r: Int, g: Int, b: Int): String = code(s"38;2;$r;$g;${b}") | ||||||
|  |   def BG_RGB(r: Int, g: Int, b: Int): String = code(s"48;2;$r;$g;${b}") | ||||||
|  |  | ||||||
|  |   private def code(str: String): String = ESC+str+"m" | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								tests/day10/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/day10/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve 1") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day10/input1.txt") == 4) | ||||||
|  |   } | ||||||
|  |   test("Puzzle1.solve 2") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day10/input2.txt") == 8) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								tests/day10/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/day10/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | package day10 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve 1") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input1.txt") == 1) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 2") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input2.txt") == 1) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 3") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input3.txt") == 4) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 4") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input4.txt") == 4) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 5") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input5.txt") == 8) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 6") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day10/input6.txt") == 10) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day11/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day11/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day11 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day11/input1.txt") == 374) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								tests/day11/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/day11/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package day11 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve 1") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day11/input1.txt", 10) == 1030) | ||||||
|  |   } | ||||||
|  |   test("Puzzle2.solve 2") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day11/input1.txt", 100) == 8410) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day13/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day13/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day13 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day13/input1.txt") == 405) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day13/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day13/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day13 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day13/input1.txt") == 400) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day14/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day14/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day14 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day14/input1.txt") == 136) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day14/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day14/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day14 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day14/input1.txt", 1000000000) == 64) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day15/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day15/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day15/input1.txt") == 1320) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day15/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day15/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day15 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day15/input1.txt") == 145) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day16/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day16/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day16 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day16/input1.txt") == 46) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day16/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day16/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day16 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day16/input1.txt") == 51) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day18/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day18/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day18 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day18/input1.txt") == 62) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day5/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day5/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day5 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day5/input1.txt") == 35) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day5/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day5/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day5 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day5/input1.txt") == 46) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day6/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day6/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day6 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day6/input1.txt") == 288) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day6/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day6/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day6 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day6/input1.txt") == 71503) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day7/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day7/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day7/input1.txt") == 6440) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day7/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day7/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day7 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day7/input1.txt") == 5905) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day8/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day8/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day8/input1.txt") == 6) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day8/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day8/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day8 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day8/input1.txt") == 6) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day9/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day9/Puzzle1Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day9 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle1Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle1.solve") { | ||||||
|  |     assert(Puzzle1.solve("tests_res/day9/input1.txt") == 114) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								tests/day9/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/day9/Puzzle2Test.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | package day9 | ||||||
|  |  | ||||||
|  | import org.scalatest.funsuite.AnyFunSuite | ||||||
|  |  | ||||||
|  | class Puzzle2Test extends AnyFunSuite { | ||||||
|  |   test("Puzzle2.solve") { | ||||||
|  |     assert(Puzzle2.solve("tests_res/day9/input1.txt") == 2) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								tests_res/day10/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests_res/day10/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | ..... | ||||||
|  | .S-7. | ||||||
|  | .|.|. | ||||||
|  | .L-J. | ||||||
|  | ..... | ||||||
							
								
								
									
										5
									
								
								tests_res/day10/input2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests_res/day10/input2.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | ..F7. | ||||||
|  | .FJ|. | ||||||
|  | SJ.L7 | ||||||
|  | |F--J | ||||||
|  | LJ... | ||||||
							
								
								
									
										9
									
								
								tests_res/day10/input3.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests_res/day10/input3.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | ........... | ||||||
|  | .S-------7. | ||||||
|  | .|F-----7|. | ||||||
|  | .||.....||. | ||||||
|  | .||.....||. | ||||||
|  | .|L-7.F-J|. | ||||||
|  | .|..|.|..|. | ||||||
|  | .L--J.L--J. | ||||||
|  | ........... | ||||||
							
								
								
									
										9
									
								
								tests_res/day10/input4.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests_res/day10/input4.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | .......... | ||||||
|  | .S------7. | ||||||
|  | .|F----7|. | ||||||
|  | .||....||. | ||||||
|  | .||....||. | ||||||
|  | .|L-7F-J|. | ||||||
|  | .|..||..|. | ||||||
|  | .L--JL--J. | ||||||
|  | .......... | ||||||
							
								
								
									
										10
									
								
								tests_res/day10/input5.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests_res/day10/input5.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | .F----7F7F7F7F-7.... | ||||||
|  | .|F--7||||||||FJ.... | ||||||
|  | .||.FJ||||||||L7.... | ||||||
|  | FJL7L7LJLJ||LJ.L-7.. | ||||||
|  | L--J.L7...LJS7F-7L7. | ||||||
|  | ....F-J..F7FJ|L7L7L7 | ||||||
|  | ....L7.F7||L7|.L7L7| | ||||||
|  | .....|FJLJ|FJ|F7|.LJ | ||||||
|  | ....FJL-7.||.||||... | ||||||
|  | ....L---J.LJ.LJLJ... | ||||||
							
								
								
									
										10
									
								
								tests_res/day10/input6.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests_res/day10/input6.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | FF7FSF7F7F7F7F7F---7 | ||||||
|  | L|LJ||||||||||||F--J | ||||||
|  | FL-7LJLJ||||||LJL-77 | ||||||
|  | F--JF--7||LJLJ7F7FJ- | ||||||
|  | L---JF-JLJ.||-FJLJJ7 | ||||||
|  | |F|F-JF---7F7-L7L|7| | ||||||
|  | |FFJF7L7F-JF7|JL---7 | ||||||
|  | 7-L-JL7||F7|L7F-7F7| | ||||||
|  | L.L7LFJ|||||FJL7||LJ | ||||||
|  | L7JLJL-JLJLJL--JLJ.L | ||||||
							
								
								
									
										10
									
								
								tests_res/day11/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests_res/day11/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | ...#...... | ||||||
|  | .......#.. | ||||||
|  | #......... | ||||||
|  | .......... | ||||||
|  | ......#... | ||||||
|  | .#........ | ||||||
|  | .........# | ||||||
|  | .......... | ||||||
|  | .......#.. | ||||||
|  | #...#..... | ||||||
							
								
								
									
										15
									
								
								tests_res/day13/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests_res/day13/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | #.##..##. | ||||||
|  | ..#.##.#. | ||||||
|  | ##......# | ||||||
|  | ##......# | ||||||
|  | ..#.##.#. | ||||||
|  | ..##..##. | ||||||
|  | #.#.##.#. | ||||||
|  |  | ||||||
|  | #...##..# | ||||||
|  | #....#..# | ||||||
|  | ..##..### | ||||||
|  | #####.##. | ||||||
|  | #####.##. | ||||||
|  | ..##..### | ||||||
|  | #....#..# | ||||||
							
								
								
									
										10
									
								
								tests_res/day14/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests_res/day14/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | O....#.... | ||||||
|  | O.OO#....# | ||||||
|  | .....##... | ||||||
|  | OO.#O....O | ||||||
|  | .O.....O#. | ||||||
|  | O.#..O.#.# | ||||||
|  | ..O..#O..O | ||||||
|  | .......O.. | ||||||
|  | #....###.. | ||||||
|  | #OO..#.... | ||||||
							
								
								
									
										1
									
								
								tests_res/day15/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests_res/day15/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7 | ||||||
							
								
								
									
										10
									
								
								tests_res/day16/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests_res/day16/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | .|...\.... | ||||||
|  | |.-.\..... | ||||||
|  | .....|-... | ||||||
|  | ........|. | ||||||
|  | .......... | ||||||
|  | .........\ | ||||||
|  | ..../.\\.. | ||||||
|  | .-.-/..|.. | ||||||
|  | .|....-|.\ | ||||||
|  | ..//.|.... | ||||||
							
								
								
									
										14
									
								
								tests_res/day18/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests_res/day18/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | R 6 (#70c710) | ||||||
|  | D 5 (#0dc571) | ||||||
|  | L 2 (#5713f0) | ||||||
|  | D 2 (#d2c081) | ||||||
|  | R 2 (#59c680) | ||||||
|  | D 2 (#411b91) | ||||||
|  | L 5 (#8ceee2) | ||||||
|  | U 2 (#caa173) | ||||||
|  | L 1 (#1b58a2) | ||||||
|  | U 2 (#caa171) | ||||||
|  | R 2 (#7807d2) | ||||||
|  | U 3 (#a77fa3) | ||||||
|  | L 2 (#015232) | ||||||
|  | U 2 (#7a21e3) | ||||||
							
								
								
									
										33
									
								
								tests_res/day5/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests_res/day5/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | seeds: 79 14 55 13 | ||||||
|  |  | ||||||
|  | seed-to-soil map: | ||||||
|  | 50 98 2 | ||||||
|  | 52 50 48 | ||||||
|  |  | ||||||
|  | soil-to-fertilizer map: | ||||||
|  | 0 15 37 | ||||||
|  | 37 52 2 | ||||||
|  | 39 0 15 | ||||||
|  |  | ||||||
|  | fertilizer-to-water map: | ||||||
|  | 49 53 8 | ||||||
|  | 0 11 42 | ||||||
|  | 42 0 7 | ||||||
|  | 57 7 4 | ||||||
|  |  | ||||||
|  | water-to-light map: | ||||||
|  | 88 18 7 | ||||||
|  | 18 25 70 | ||||||
|  |  | ||||||
|  | light-to-temperature map: | ||||||
|  | 45 77 23 | ||||||
|  | 81 45 19 | ||||||
|  | 68 64 13 | ||||||
|  |  | ||||||
|  | temperature-to-humidity map: | ||||||
|  | 0 69 1 | ||||||
|  | 1 0 69 | ||||||
|  |  | ||||||
|  | humidity-to-location map: | ||||||
|  | 60 56 37 | ||||||
|  | 56 93 4 | ||||||
							
								
								
									
										2
									
								
								tests_res/day6/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests_res/day6/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | Time:      7  15   30 | ||||||
|  | Distance:  9  40  200 | ||||||
							
								
								
									
										5
									
								
								tests_res/day7/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests_res/day7/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | 32T3K 765 | ||||||
|  | T55J5 684 | ||||||
|  | KK677 28 | ||||||
|  | KTJJT 220 | ||||||
|  | QQQJA 483 | ||||||
							
								
								
									
										5
									
								
								tests_res/day8/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests_res/day8/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | LLR | ||||||
|  |  | ||||||
|  | AAA = (BBB, BBB) | ||||||
|  | BBB = (AAA, ZZZ) | ||||||
|  | ZZZ = (ZZZ, ZZZ) | ||||||
							
								
								
									
										3
									
								
								tests_res/day9/input1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests_res/day9/input1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | 0 3 6 9 12 15 | ||||||
|  | 1 3 6 10 15 21 | ||||||
|  | 10 13 16 21 30 45 | ||||||
		Reference in New Issue
	
	Block a user