Compare commits
	
		
			17 Commits
		
	
	
		
			0f8f8c3f19
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c429c5bbb | |||
| 56b7f06e77 | |||
| abeeee237c | |||
| 78b3d77a29 | |||
| e6ef68d818 | |||
| 802404ac1f | |||
| 67c609e489 | |||
| d36d403402 | |||
| 6b134b55c5 | |||
| 0fb66d1bc7 | |||
| c693cd9259 | |||
| 470eeacbf2 | |||
| a234ea59b3 | |||
| dedcd1cc70 | |||
| 651418bea3 | |||
| 8d55a3c7b3 | |||
| ee3a02060b | 
							
								
								
									
										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 | ||||
|  | ||||
| ## Progress: | ||||
| #### Stars: 18 / 50 | ||||
| |        Mon        |        Tue        |        Wed        |        Thu        |        Fri        |        Sat        |        Sun        | | ||||
| |:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:|:-----------------:| | ||||
| |                   |                   |                   |                   | 1<br>:star::star: | 2<br>:star::star: | 3<br>:star::star: | | ||||
| | 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         | | ||||
| |        11         |        12         |        13         |        14         |        15         |        16         |        17         | | ||||
| |        18         |        19         |        20         |        21         |        22         |        23         |        24         | | ||||
| |        25         |                   |                   |                   |                   |                   |                   | | ||||
| #### Stars: 30 / 50 | ||||
| |        Mon         |        Tue        |        Wed         |        Thu         |        Fri         |        Sat         |        Sun         | | ||||
| |:------------------:|:-----------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:| | ||||
| |                    |                   |                    |                    | 1<br>:star::star:  | 2<br>:star::star:  | 3<br>:star::star:  | | ||||
| | 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<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         | | ||||
| |         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) | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										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) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										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) | ||||
		Reference in New Issue
	
	Block a user