d20p1
This commit is contained in:
		
							
								
								
									
										89
									
								
								day20/d20p1.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								day20/d20p1.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| import Foundation | ||||
|  | ||||
| struct Coord : Hashable, CustomStringConvertible { | ||||
|     let (x, y): (Int, Int) | ||||
|     var description: String { return "(\(x), \(y))" } | ||||
| } | ||||
|  | ||||
| struct Maze : CustomStringConvertible { | ||||
|     let (w, h): (Int, Int) | ||||
|     let walls: Set<Coord> | ||||
|     let (start, end): (Coord, Coord) | ||||
|     var description: String { | ||||
|         var s = Array(repeating: Array(repeating: " ", count: w), count: (h+1)/2) | ||||
|         walls.forEach { wall in | ||||
|             if wall.y.isMultiple(of: 2) { | ||||
|                 s[wall.y/2][wall.x] = (s[wall.y/2][wall.x] == " ") ? "▀" : "█" | ||||
|             } else { | ||||
|                 s[wall.y/2][wall.x] = (s[wall.y/2][wall.x] == " ") ? "▄" : "█" | ||||
|             } | ||||
|         } | ||||
|         return s.map { $0.joined() + "\n" }.joined() + "\(start), \(end)" | ||||
|     } | ||||
|     let adjacents = [(-1, 0), (1, 0), (0, -1), (0, 1)] | ||||
|  | ||||
|     func valid(_ cell: Coord) -> Bool { | ||||
|         return cell.x >= 0 && cell.x < w && cell.y >= 0 && cell.y < h && | ||||
|             !walls.contains(cell) | ||||
|     } | ||||
|  | ||||
|     func neighbor(of cell: Coord, isNot excluded: Coord) -> Coord { | ||||
|         return adjacents | ||||
|             .map { dx, dy in Coord(x: cell.x + dx, y: cell.y + dy) } | ||||
|             .filter { newCell in valid(newCell) && newCell != excluded }[0] | ||||
|     } | ||||
|  | ||||
|     func cheats(of cell: Coord) -> [Coord] { | ||||
|         return adjacents | ||||
|             .map { dx, dy in Coord(x: cell.x + dx*2, y: cell.y + dy*2) } | ||||
|             .filter(valid) | ||||
|     } | ||||
|  | ||||
|     func cheats() -> [Int] { | ||||
|         var cost: [Coord: Int] = [start: 0] | ||||
|         var jumps: [(Coord, Coord)] = [] | ||||
|         var cell = start | ||||
|         var prevCell = start | ||||
|         var currentCost = 0 | ||||
|         while cell != end { | ||||
|             // list potential cheats from current cell | ||||
|             let newCheats = cheats(of: cell).filter { !cost.keys.contains($0) } | ||||
|             jumps.append(contentsOf: newCheats.map { dest in (cell, dest)} ) | ||||
|             // add cost to table | ||||
|             let nextCell = neighbor(of: cell, isNot: prevCell) | ||||
|             currentCost += 1 | ||||
|             cost[nextCell] = currentCost  | ||||
|             prevCell = cell | ||||
|             cell = nextCell | ||||
|         } | ||||
|         return jumps.map { from, to in cost[to]! - cost[from]! - 2 } | ||||
|     } | ||||
| } | ||||
|  | ||||
| func readInput(_ filePath: String) throws -> Maze { | ||||
|     let content = try String(contentsOfFile: filePath, encoding: .ascii) | ||||
|     let lines = content.split(separator: "\n") | ||||
|     let (w, h) = (lines[0].count, lines.count) | ||||
|     var walls: Set<Coord> = [] | ||||
|     var (start, end) = (Coord(x: 0, y: 0), Coord(x: w-1, y: h-1)) | ||||
|     lines.enumerated().forEach { i, line in | ||||
|         line.enumerated().forEach { j, cell in | ||||
|             let coord = Coord(x: j, y: i) | ||||
|             if cell == "#" { | ||||
|                 walls.insert(coord) | ||||
|             } else if cell == "S" { | ||||
|                 start = coord | ||||
|             } else if cell == "E" { | ||||
|                 end = coord | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return Maze(w: w, h: h, walls: walls, start: start, end: end) | ||||
| } | ||||
|  | ||||
| let maze = try readInput(CommandLine.arguments[1]) | ||||
| print(maze) | ||||
| let cheats = maze.cheats() | ||||
| print(cheats.reduce(into: [:]) { counts, t in counts[t, default: 0] += 1 }) | ||||
| print(cheats.filter { $0 >= 100 }.count) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user