| 100 | * Make a path from a sequence of moves. |
| 101 | */ |
| 102 | const pathFromSequence = ( |
| 103 | terrain: Terrain, |
| 104 | sequence: Sequence |
| 105 | ): Graph.PathResult<number> => { |
| 106 | const [start, rest] = sequence.split(";") as [`${number}:${number}`, string] |
| 107 | if (!terrain.nodes.has(start)) { |
| 108 | throw new Error(`Start location ${start} not found in terrain`) |
| 109 | } |
| 110 | |
| 111 | const index = terrain.nodes.get(start)! |
| 112 | const node = Graph.getNode(terrain.graph, index).pipe( |
| 113 | Option.getOrThrowWith(() => new Error(`Start location ${start} not found in terrain`)) |
| 114 | ) |
| 115 | |
| 116 | const output: Types.Mutable<Graph.PathResult<number>> = { |
| 117 | distance: node.weight, |
| 118 | costs: [node.weight], |
| 119 | path: [index] |
| 120 | } |
| 121 | |
| 122 | let [x, y] = start.split(":").map(Number) as [number, number] |
| 123 | for (const location of rest.split("")) { |
| 124 | if (location === "↓") { |
| 125 | y++ |
| 126 | } else if (location === "↑") { |
| 127 | y-- |
| 128 | } else if (location === "←") { |
| 129 | x-- |
| 130 | } else if (location === "→") { |
| 131 | x++ |
| 132 | } else { |
| 133 | throw new Error(`Invalid move ${location} in sequence ${sequence}`) |
| 134 | } |
| 135 | |
| 136 | if (!terrain.nodes.has(`${x}:${y}`)) { |
| 137 | continue |
| 138 | } |
| 139 | |
| 140 | const index = terrain.nodes.get(`${x}:${y}`)! |
| 141 | const node = Graph.getNode(terrain.graph, index).pipe( |
| 142 | Option.getOrThrowWith(() => new Error(`Location ${location} not found in terrain`)) |
| 143 | ) |
| 144 | |
| 145 | output.distance += node.weight |
| 146 | output.costs.push(node.weight) |
| 147 | output.path.push(index) |
| 148 | } |
| 149 | |
| 150 | return output |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Derive a sequence of moves from a path. |