let solve maze =
let maze = maze |> Seq.map Seq.toArray |> Seq.toArray
let pos x =
maze |> Seq.mapi (fun y m -> y, m)
|> Seq.tryPick ((<||) (fun y -> Seq.tryFindIndex ((=) x) >> Option.map (fun x -> y, x)))
let distance
(y1
, x1
) (y2
, x2
) = abs (y1
- y2
) + abs (x1
- x2
) let rec solve goal cls = function
| [] -> None
| ((p, ds), _)::ops when p = goal -> ds |> List.rev |> String.concat "" |> Some
| (((y, x), ds), (g, h)) as op::ops ->
let cls = cls |> Map.add (y, x) op
[(y - 1, x), "u"; (y + 1, x), "d"; (y, x - 1), "l"; (y, x + 1), "r"]
|> List.choose (fun ((y, x) as p, d) ->
if maze.[y].[x] <> '#' then Some((p, d::ds), (g + 1, distance goal p)) else None)
|> List.fold (fun (cls, ops) (((p, _), (g, h)) as x) ->
match Map.tryFind p cls with
| Some
(_
, (cg
, ch
)) when g
+ h
< cg
+ ch
-> Map.
remove p cls
, x
::ops | Some _ -> cls, ops
| None -> cls, x::ops) (cls, ops)
|> (fun (cls, ops) -> cls, ops |> Seq.sortBy (snd >> (<||) (+)) |> Seq.distinctBy (fst >> fst) |> Seq.toList)
||> solve goal
match pos 'S', pos 'G' with
| None, _ | _, None -> invalidArg "maze" "Either start or goal is not found."
| Some start, Some goal -> solve goal Map.empty [(start, []), (0, distance start goal)]
Seq.initInfinite (fun _ -> System.Console.ReadLine()) |> Seq.takeWhile ((<>) "")
|> solve |> printfn "%A"
bGV0IHNvbHZlIG1hemUgPQogICAgbGV0IG1hemUgPSBtYXplIHw+IFNlcS5tYXAgU2VxLnRvQXJyYXkgfD4gU2VxLnRvQXJyYXkKICAgIGxldCBwb3MgeCA9CiAgICAgICAgbWF6ZSB8PiBTZXEubWFwaSAoZnVuIHkgbSAtPiB5LCBtKQogICAgICAgIHw+IFNlcS50cnlQaWNrICgoPHx8KSAoZnVuIHkgLT4gU2VxLnRyeUZpbmRJbmRleCAoKD0pIHgpID4+IE9wdGlvbi5tYXAgKGZ1biB4IC0+IHksIHgpKSkKICAgIGxldCBkaXN0YW5jZSAoeTEsIHgxKSAoeTIsIHgyKSA9IGFicyAoeTEgLSB5MikgKyBhYnMgKHgxIC0geDIpCiAgICBsZXQgcmVjIHNvbHZlIGdvYWwgY2xzID0gZnVuY3Rpb24KICAgICAgICB8IFtdIC0+IE5vbmUKICAgICAgICB8ICgocCwgZHMpLCBfKTo6b3BzIHdoZW4gcCA9IGdvYWwgLT4gZHMgfD4gTGlzdC5yZXYgfD4gU3RyaW5nLmNvbmNhdCAiIiB8PiBTb21lCiAgICAgICAgfCAoKCh5LCB4KSwgZHMpLCAoZywgaCkpIGFzIG9wOjpvcHMgLT4KICAgICAgICAgICAgbGV0IGNscyA9IGNscyB8PiBNYXAuYWRkICh5LCB4KSBvcAogICAgICAgICAgICBbKHkgLSAxLCB4KSwgInUiOyAoeSArIDEsIHgpLCAiZCI7ICh5LCB4IC0gMSksICJsIjsgKHksIHggKyAxKSwgInIiXQogICAgICAgICAgICB8PiBMaXN0LmNob29zZSAoZnVuICgoeSwgeCkgYXMgcCwgZCkgLT4KICAgICAgICAgICAgICAgIGlmIG1hemUuW3ldLlt4XSA8PiAnIycgdGhlbiBTb21lKChwLCBkOjpkcyksIChnICsgMSwgZGlzdGFuY2UgZ29hbCBwKSkgZWxzZSBOb25lKQogICAgICAgICAgICB8PiBMaXN0LmZvbGQgKGZ1biAoY2xzLCBvcHMpICgoKHAsIF8pLCAoZywgaCkpIGFzIHgpIC0+CiAgICAgICAgICAgICAgICBtYXRjaCBNYXAudHJ5RmluZCBwIGNscyB3aXRoCiAgICAgICAgICAgICAgICB8IFNvbWUoXywgKGNnLCBjaCkpIHdoZW4gZyArIGggPCBjZyArIGNoIC0+IE1hcC5yZW1vdmUgcCBjbHMsIHg6Om9wcwogICAgICAgICAgICAgICAgfCBTb21lIF8gLT4gY2xzLCBvcHMKICAgICAgICAgICAgICAgIHwgTm9uZSAtPiBjbHMsIHg6Om9wcykgKGNscywgb3BzKQogICAgICAgICAgICB8PiAoZnVuIChjbHMsIG9wcykgLT4gY2xzLCBvcHMgfD4gU2VxLnNvcnRCeSAoc25kID4+ICg8fHwpICgrKSkgfD4gU2VxLmRpc3RpbmN0QnkgKGZzdCA+PiBmc3QpIHw+IFNlcS50b0xpc3QpCiAgICAgICAgICAgIHx8PiBzb2x2ZSBnb2FsCiAgICBtYXRjaCBwb3MgJ1MnLCBwb3MgJ0cnIHdpdGgKICAgIHwgTm9uZSwgXyB8IF8sIE5vbmUgLT4gaW52YWxpZEFyZyAibWF6ZSIgIkVpdGhlciBzdGFydCBvciBnb2FsIGlzIG5vdCBmb3VuZC4iCiAgICB8IFNvbWUgc3RhcnQsIFNvbWUgZ29hbCAtPiBzb2x2ZSBnb2FsIE1hcC5lbXB0eSBbKHN0YXJ0LCBbXSksICgwLCBkaXN0YW5jZSBzdGFydCBnb2FsKV0KClNlcS5pbml0SW5maW5pdGUgKGZ1biBfIC0+IFN5c3RlbS5Db25zb2xlLlJlYWRMaW5lKCkpIHw+IFNlcS50YWtlV2hpbGUgKCg8PikgIiIpCnw+IHNvbHZlIHw+IHByaW50Zm4gIiVBIgo=