day 10 puzzle 2
This commit is contained in:
		
							
								
								
									
										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<br>:star:    | | ||||
| |        11         |        12         |        13         |        14         |        15         |        16         |        17         | | ||||
| |        18         |        19         |        20         |        21         |        22         |        23         |        24         | | ||||
| |        25         |                   |                   |                   |                   |                   |                   | | ||||
| #### Stars: 20 / 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         |        12         |        13         |        14         |        15         |        16         |         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 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										147
									
								
								src/day10/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/day10/Puzzle2.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| package day10 | ||||
|  | ||||
| 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 | ||||
|   ) | ||||
|  | ||||
|   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) | ||||
|     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 calculateArea(): Int = { | ||||
|     val path: ArrayBuffer[(Int, Int)] = 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 filled: Boolean = false | ||||
|     var exteriorZone: Int = 0 | ||||
|  | ||||
|     do { | ||||
|       filled = true | ||||
|       newZones = copyZones() | ||||
|       for (y: Int <- 0 until height) { | ||||
|         for (x: Int <- 0 until width) { | ||||
|           if (zones(y)(x) == 0 && !path.contains((x, y))) { | ||||
|             filled = false | ||||
|             fillTile(x, y, newZones) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       zones = newZones | ||||
|     } while (!filled) | ||||
|     println("Filled 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 fillTile(x: Int, y: Int, newZones: Array[Array[Int]]): Unit = { | ||||
|     var zone: Int = 0 | ||||
|  | ||||
|     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) { | ||||
|           zone = zones(y2)(x2) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     newZones(y)(x) = zone | ||||
|   } | ||||
|  | ||||
|   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) | ||||
|   } | ||||
| } | ||||
| @@ -1,15 +1,11 @@ | ||||
| package day10 | ||||
|  | ||||
| class Walker { | ||||
|   private var x: Int = 0 | ||||
|   private var y: Int = 0 | ||||
|   private var lastX: Int = -1 | ||||
|   private var lastY: Int = -1 | ||||
|   private var distance: Int = 0 | ||||
|  | ||||
|   val OFFSETS: Array[(Int, Int)] = Array( | ||||
|     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||
|   ) | ||||
|   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() | ||||
| @@ -41,7 +37,7 @@ class Walker { | ||||
|  | ||||
|   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) <- OFFSETS.zipWithIndex) { | ||||
|     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) { | ||||
| @@ -58,3 +54,9 @@ class Walker { | ||||
|     throw new Exception("Dead-end path") | ||||
|   } | ||||
| } | ||||
|  | ||||
| object Walker { | ||||
|   val OFFSETS: Array[(Int, Int)] = Array( | ||||
|     (1, 0), (0, 1), (-1, 0), (0, -1) | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										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_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 | ||||
		Reference in New Issue
	
	Block a user