Day 16.
This commit is contained in:
		
							parent
							
								
									3ee70151f1
								
							
						
					
					
						commit
						76c01e01e5
					
				| @ -80,6 +80,19 @@ Prize: X=18641, Y=10279 | |||||||
| //            val t2 = bx - by | //            val t2 = bx - by | ||||||
| //            val t3 = px - py | //            val t3 = px - py | ||||||
| 
 | 
 | ||||||
|  |             // Linear Diophantine equations | ||||||
|  |             /* The simplest linear Diophantine equation takes the form | ||||||
|  |                a*x + b*x = c | ||||||
|  |                where a, b and c are given integers. | ||||||
|  |                The solutions are described by the following theorem: | ||||||
|  |                This Diophantine equation has a solution (where x and y are integers), | ||||||
|  |                IFF c is a multiple of the greatest common divisor of a and b. | ||||||
|  |                Moreover, if (x, y) is a solution, then the other solutions have the | ||||||
|  |                form (x + kv, y − ku), where | ||||||
|  |                - k is an arbitrary integer, and | ||||||
|  |                - u and v are the quotients of a and b (respectively) by the greatest common divisor of a and b. | ||||||
|  |              */ | ||||||
|  |             // With our input data, all equations have exactly one solution | ||||||
|             val pad = (px * by - py * bx) |             val pad = (px * by - py * bx) | ||||||
|             val pan = (ax * by - ay * bx) |             val pan = (ax * by - ay * bx) | ||||||
|             if (pad % pan == 0L) { |             if (pad % pan == 0L) { | ||||||
|  | |||||||
							
								
								
									
										162
									
								
								src/aoc2024/Day16.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/aoc2024/Day16.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | |||||||
|  | package aoc2024 | ||||||
|  | 
 | ||||||
|  | import CharGrid | ||||||
|  | import RelPos | ||||||
|  | import println | ||||||
|  | import readInput | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | --- Day 16: Reindeer Maze --- | ||||||
|  | https://adventofcode.com/2024/day/16 | ||||||
|  | */ | ||||||
|  | fun main() { | ||||||
|  | 
 | ||||||
|  |     val inlineTestInput = """ | ||||||
|  | ############### | ||||||
|  | #.......#....E# | ||||||
|  | #.#.###.#.###.# | ||||||
|  | #.....#.#...#.# | ||||||
|  | #.###.#####.#.# | ||||||
|  | #.#.#.......#.# | ||||||
|  | #.#.#####.###.# | ||||||
|  | #...........#.# | ||||||
|  | ###.#.#####.#.# | ||||||
|  | #...#.....#.#.# | ||||||
|  | #.#.#.###.#.#.# | ||||||
|  | #.....#...#.#.# | ||||||
|  | #.###.#.#.#.#.# | ||||||
|  | #S..#.....#...# | ||||||
|  | ############### | ||||||
|  | """ | ||||||
|  |     val inlineTestInput2 = """ | ||||||
|  | ################# | ||||||
|  | #...#...#...#..E# | ||||||
|  | #.#.#.#.#.#.#.#.# | ||||||
|  | #.#.#.#...#...#.# | ||||||
|  | #.#.#.#.###.#.#.# | ||||||
|  | #...#.#.#.....#.# | ||||||
|  | #.#.#.#.#.#####.# | ||||||
|  | #.#...#.#.#.....# | ||||||
|  | #.#.#####.#.###.# | ||||||
|  | #.#.#.......#...# | ||||||
|  | #.#.###.#####.### | ||||||
|  | #.#.#...#.....#.# | ||||||
|  | #.#.#.#####.###.# | ||||||
|  | #.#.#.........#.# | ||||||
|  | #.#.#.#########.# | ||||||
|  | #S#.............# | ||||||
|  | ################# | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  |     data class Node(val pos: RelPos, val dir: RelPos, val relLen: Int, val path: LinkedHashSet<Pair<RelPos, RelPos>>) | ||||||
|  | 
 | ||||||
|  |     val costMap = hashMapOf( | ||||||
|  |         RelPos(0, -1) to listOf(RelPos(0, -1) to 1, RelPos(1, 0) to 1001, RelPos(-1, 0) to 1001, RelPos(0, 1) to 2001), | ||||||
|  |         RelPos(1, 0) to listOf(RelPos(1, 0) to 1, RelPos(0, 1) to 1001, RelPos(0, -1) to 1001, RelPos(-1, 0) to 2001), | ||||||
|  |         RelPos(0, 1) to listOf(RelPos(0, 1) to 1, RelPos(-1, 0) to 1001, RelPos(1, 0) to 1001, RelPos(0, -1) to 2001), | ||||||
|  |         RelPos(-1, 0) to listOf(RelPos(-1, 0) to 1, RelPos(0, 1) to 1001, RelPos(0, -1) to 1001, RelPos(1, 0) to 2001) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     fun part1(input: List<String>): Int { | ||||||
|  |         val grid = CharGrid(input, '#') | ||||||
|  |         val queue = ArrayDeque<Node>() | ||||||
|  |         val (startc, startr) = grid.findMatches { it == 'S' }[0] | ||||||
|  |         val (endc, endr) = grid.findMatches { it == 'E' }[0] | ||||||
|  |         val endpos = RelPos(endc, endr) | ||||||
|  | 
 | ||||||
|  |         grid[startc, startr] = '.' | ||||||
|  |         grid[endpos] = '.' | ||||||
|  |         queue.add(Node(RelPos(startc, startr), RelPos(1, 0), 0, LinkedHashSet())) | ||||||
|  |         val bestCosts = HashMap<Pair<RelPos, RelPos>, Int>() | ||||||
|  |         var minCost = Int.MAX_VALUE | ||||||
|  |         while (queue.isNotEmpty()) { | ||||||
|  |             val node = queue.removeFirst() | ||||||
|  |             val pos = node.pos | ||||||
|  |             val dir = node.dir | ||||||
|  |             val cost = node.relLen | ||||||
|  |             val path = node.path | ||||||
|  |             path.add(pos to dir) | ||||||
|  |             if (pos == endpos) { | ||||||
|  |                 minCost = minCost.coerceAtMost(cost) | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             if ((bestCosts[pos to dir] ?: Int.MAX_VALUE) <= cost) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             bestCosts[pos to dir] = cost | ||||||
|  |             val nextDirs = | ||||||
|  |                 costMap[dir]!!.filter { grid[pos.translate(it.first)] == '.' && cost + it.second < minCost && !path.contains(pos.translate(it.first) to it.first) } | ||||||
|  |                     .map { Node(pos.translate(it.first), it.first, cost + it.second, LinkedHashSet(path)) } | ||||||
|  | 
 | ||||||
|  |             queue.addAll(nextDirs) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return minCost | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun part2(input: List<String>): Int { | ||||||
|  |         val grid = CharGrid(input, '#') | ||||||
|  |         val queue = ArrayDeque<Node>() | ||||||
|  |         val (startc, startr) = grid.findMatches { it == 'S' }[0] | ||||||
|  |         val (endc, endr) = grid.findMatches { it == 'E' }[0] | ||||||
|  |         val endpos = RelPos(endc, endr) | ||||||
|  | 
 | ||||||
|  |         grid[startc, startr] = '.' | ||||||
|  |         grid[endpos] = '.' | ||||||
|  |         queue.add(Node(RelPos(startc, startr), RelPos(1, 0), 0, LinkedHashSet())) | ||||||
|  |         val bestCosts = HashMap<Pair<RelPos, RelPos>, Int>() | ||||||
|  |         var minCost = Int.MAX_VALUE | ||||||
|  |         val bestNodes = HashSet<RelPos>() | ||||||
|  |         while (queue.isNotEmpty()) { | ||||||
|  |             val node = queue.removeFirst() | ||||||
|  |             val pos = node.pos | ||||||
|  |             val dir = node.dir | ||||||
|  |             val cost = node.relLen | ||||||
|  |             val path = node.path | ||||||
|  |             path.add(pos to dir) | ||||||
|  |             if (pos == endpos) { | ||||||
|  |                 if (cost < minCost) { | ||||||
|  |                     bestNodes.clear() | ||||||
|  |                     bestNodes.addAll(path.map { it.first }) | ||||||
|  |                 } | ||||||
|  |                 if (minCost == cost) { | ||||||
|  |                     bestNodes.addAll(path.map { it.first }) | ||||||
|  |                 } | ||||||
|  |                 minCost = minCost.coerceAtMost(cost) | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             if ((bestCosts[pos to dir] ?: Int.MAX_VALUE) < cost) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             bestCosts[pos to dir] = cost | ||||||
|  |             val nextDirs = | ||||||
|  |                 costMap[dir]!!.filter { grid[pos.translate(it.first)] == '.' && cost + it.second < minCost && !path.contains(pos.translate(it.first) to it.first) } | ||||||
|  |                     .map { Node(pos.translate(it.first), it.first, cost + it.second, LinkedHashSet(path)) } | ||||||
|  | 
 | ||||||
|  |             queue.addAll(nextDirs) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return bestNodes.size | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // test if implementation meets criteria from the description, like: | ||||||
|  |     val testInput = inlineTestInput.trim().reader().readLines() | ||||||
|  |     val testInput2 = inlineTestInput2.trim().reader().readLines() | ||||||
|  |     //val testInput = readInput("aoc2024/Day16_test") | ||||||
|  |     val testInputPart1Result = part1(testInput) | ||||||
|  |     println("Part 1 Test: $testInputPart1Result") | ||||||
|  |     val testInputPart1Result2 = part1(testInput2) | ||||||
|  |     println("Part 1 Test 2: $testInputPart1Result2") | ||||||
|  |     val testInputPart2Result = part2(testInput) | ||||||
|  |     println("Part 2 Test: $testInputPart2Result") | ||||||
|  |     val testInputPart2Result2 = part2(testInput2) | ||||||
|  |     println("Part 2 Test2: $testInputPart2Result2") | ||||||
|  |     check(testInputPart1Result == 7036) | ||||||
|  |     check(testInputPart1Result2 == 11048) | ||||||
|  |     check(testInputPart2Result == 45) | ||||||
|  |     check(testInputPart2Result2 == 64) | ||||||
|  | 
 | ||||||
|  |     val input = readInput("aoc2024/Day16") | ||||||
|  |     part1(input).println() | ||||||
|  |     part2(input).println() | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user