-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a4c0d96
commit 98736fb
Showing
2 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
namespace AoC2019 | ||
|
||
module IntCode = | ||
open System | ||
open System.IO | ||
open MyQueue | ||
|
||
let mem_size = 10000 | ||
|
||
let readInts (path:string) : int64 [] = | ||
use sw = new StreamReader (path) | ||
sw.ReadToEnd().Split(',') | ||
|> Array.map Int64.Parse | ||
|
||
type Status = | ||
{ | ||
position: int; finished: bool; mutable suspended: bool; | ||
mutable output: queue<int64>; mutable phase: queue<int64>; base_addr: int} | ||
type Destinations = |Immediate of int64| MemoryDest of int | Skip | ||
|
||
let getParamVal (mode:int64) (memory: byref<int64[]>) (pos:int) (base_addr:int) = | ||
match mode with | ||
| 0L -> memory.[ (int)memory.[pos] ] | ||
| 1L -> memory.[pos] | ||
| 2L -> memory.[base_addr + (int)memory.[pos] ] | ||
| _ -> failwith "wrong par mode" | ||
|
||
let runCmd (before: Status) (memory:byref<int64[]>) : Status = | ||
let opcodeStr = memory.[before.position] | ||
let opcode = opcodeStr % 100L | ||
let mode_op3 = opcodeStr / 10000L | ||
if mode_op3 = 1L then failwith "dest must be pos or rel mode" | ||
let mode_op2 =((opcodeStr / 100L) % 100L) / 10L | ||
let mode_op1 =((opcodeStr / 100L) % 100L) % 10L | ||
let dest = | ||
match opcode with | ||
| 1L | ||
| 2L | ||
| 7L | ||
| 8L -> MemoryDest ((int)memory.[before.position + 3] + (if mode_op3 = 2L then before.base_addr else 0)) | ||
| 3L -> MemoryDest ((int)memory.[before.position + 1] + (if mode_op1 = 2L then before.base_addr else 0)) | ||
| 4L | ||
| 5L | ||
| 6L | ||
| 9L -> Skip | ||
| _ -> failwith "wrong position" | ||
|
||
match dest with | ||
| Immediate _ -> failwith "dest can't be immediate" | ||
| MemoryDest dest -> | ||
if dest >= mem_size then failwith "please extend memory" | ||
memory.[dest] <- | ||
match opcode with | ||
| 1L -> | ||
(+) | ||
(getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
(getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
| 2L -> | ||
(*) | ||
(getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
(getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
| 3L -> | ||
let (a, q) = MyQueue.dequeue before.phase | ||
// https://stackoverflow.com/questions/59552476/copyofstruct-not-defined?noredirect=1 | ||
// https://github.com/dotnet/fsharp/issues/8069 | ||
This comment has been minimized.
Sorry, something went wrong. |
||
before.phase <- q | ||
a | ||
| 7L -> if ((getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
< (getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
) then 1L else 0L | ||
| 8L -> if ((getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
= (getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
) then 1L else 0L | ||
| _ -> failwith "wrong position" | ||
| Skip -> () | ||
if opcode = 4L then | ||
before.output <- MyQueue.enqueue before.output (getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
let position = | ||
match opcode with | ||
| 1L | ||
| 2L | ||
| 7L | ||
| 8L -> before.position + 4 | ||
| 3L | ||
| 4L | ||
| 9L -> before.position + 2 | ||
| 5L -> if ((getParamVal mode_op1 &memory (before.position + 1) before.base_addr) <> (int64) 0) | ||
then (int)(getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
else before.position + 3 | ||
| 6L -> if ((getParamVal mode_op1 &memory (before.position + 1) before.base_addr) = (int64) 0) | ||
then (int)(getParamVal mode_op2 &memory (before.position + 2) before.base_addr) | ||
else before.position + 3 | ||
| _ -> failwith "wrong position" | ||
|
||
let base_addr = | ||
if opcode = 9L | ||
then before.base_addr + (int)(getParamVal mode_op1 &memory (before.position + 1) before.base_addr) | ||
else before.base_addr | ||
|
||
let opcodeStrNext = memory.[position] | ||
let opcodeNext = opcodeStrNext % 100L | ||
|
||
{ | ||
position = position; finished = opcodeNext = 99L; | ||
suspended = (opcodeNext = 3L) && (MyQueue.length before.phase = 0); output = before.output; phase = before.phase; base_addr = base_addr} | ||
|
||
let extendMemory (memory:Int64[]) (initial:queue<int64>) : int64[] * Status = | ||
let extended_memory = | ||
[|0 .. mem_size - 1|] | ||
|> Array.mapi(fun i t -> | ||
if i < memory.Length | ||
then memory.[i] | ||
else (int64)0 | ||
) | ||
extended_memory, {position = 0; finished = false; suspended = false; output = MyQueue.empty; phase = initial; base_addr= 0} | ||
|
||
let bootstrap (input_path:string) (initial:queue<int64>) : int64[] * Status = | ||
let memory = readInts input_path | ||
extendMemory memory initial | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
open System | ||
open System.IO | ||
open Checked | ||
open System.Diagnostics | ||
open AoC2019.IntCode | ||
open System.Collections | ||
open MyQueue | ||
//https://adventofcode.com/2019/day/15 | ||
|
||
type Position = {X:int;Y:int} | ||
|
||
[<EntryPoint>] | ||
let main _ = | ||
|
||
let sw = Stopwatch() | ||
sw.Start() | ||
|
||
let mutable (memory, status) = bootstrap "C:\dev\FSharp\AoC2019\Day15\input.txt" MyQueue.empty | ||
let debug = false | ||
let mutable found = false | ||
let mutable distance = 0 | ||
let mutable possible_ways : queue<int64[]> = MyQueue.empty | ||
let mutable visited : Map<Position,char> = Map.empty | ||
let mutable position = {X=0;Y=0} | ||
|
||
let track = function | ||
//because of the reply of 2, you know you've found the oxygen system | ||
| 2L -> 'O' | ||
//a reply of 1 means the movement was successful | ||
| 1L -> '.' | ||
//If it replies with 0, you know that location is a wall | ||
| 0L -> '#' | ||
| _ -> failwith "can't track it" | ||
|
||
|
||
let show (visited:Map<Position,char>) = | ||
let visited_keys = visited |> Map.toArray |> Array.map fst | ||
let y = visited_keys |> Array.map(fun p -> p.Y) | ||
let yMin = y |> Array.min | ||
let yMax = y |> Array.max | ||
let x = visited_keys |> Array.map(fun p -> p.X) | ||
let xMin = x |> Array.min | ||
let xMax = x |> Array.max | ||
Console.Clear() | ||
for y in [|yMin..yMax|] |> Array.rev do | ||
for x in xMin..xMax do | ||
visited | ||
|> Map.tryFind {X=x;Y=y} | ||
|> Option.fold (fun _ c -> c) ' ' | ||
|> printf "%c" | ||
printfn "" | ||
|
||
|
||
//Only four movement commands are understood: north (1), south (2), west (3), and east (4). | ||
let back = function | ||
| 1L -> 2L | ||
| 2L -> 1L | ||
| 3L -> 4L | ||
| 4L -> 3L | ||
| _ -> failwith "wrong direction" | ||
let moveTo = function | ||
| 1L -> fun position -> {position with Y = position.Y + 1} | ||
| 2L -> fun position -> {position with Y = position.Y - 1} | ||
| 3L -> fun position -> {position with X = position.X - 1} | ||
| 4L -> fun position -> {position with X = position.X + 1} | ||
| _ -> failwith "wrong direction" | ||
for move in [1L..4L] do | ||
if found then () else | ||
status.phase <- MyQueue.enqueue status.phase move | ||
status.suspended <- false | ||
while not (status.suspended || status.finished) do | ||
status <- runCmd status &memory | ||
// https://stackoverflow.com/questions/59552476/copyofstruct-not-defined?noredirect=1 | ||
// https://github.com/dotnet/fsharp/issues/8069 | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
visited <- visited.Add( (position |> moveTo move) , track output ) | ||
match output with | ||
//because of the reply of 2, you know you've found the oxygen system | ||
| 2L -> | ||
found <- true | ||
//a reply of 1 means the movement was successful | ||
| 1L -> | ||
position <- position |> moveTo move | ||
distance <- distance + 1 | ||
possible_ways <- enqueue possible_ways [|move|] | ||
status.phase <- MyQueue.enqueue status.phase <| back move | ||
status.suspended <- false | ||
while not (status.suspended || status.finished) do | ||
status <- runCmd status &memory | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
if output <> 1L then failwith "path changed" | ||
position <- position |> moveTo (back move) | ||
distance <- distance - 1 | ||
//If it replies with 0, you know that location is a wall and that the droid didn't move | ||
| 0L -> | ||
() | ||
| _ -> failwith "wrong output" | ||
|
||
let mutable way = [||] | ||
while not found && MyQueue.length possible_ways > 0 do | ||
if debug then show visited | ||
let (way_, possible_ways_) = dequeue possible_ways | ||
// https://github.com/dotnet/fsharp/issues/8069 | ||
// https://stackoverflow.com/questions/59552476/copyofstruct-not-defined?noredirect=1&lq=1 | ||
way <- way_ | ||
possible_ways <- possible_ways_ | ||
if position.X <> 0 || position.Y <> 0 || distance <> 0 then failwith "position is not initial" | ||
|
||
for move in way do | ||
status.phase <- MyQueue.enqueue status.phase move | ||
status.suspended <- false | ||
while not <| status.suspended || status.finished do | ||
status <- runCmd status &memory | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
if output <> 1L then failwith "path changed" | ||
distance <- distance + 1 | ||
position <- position |> moveTo move | ||
|
||
for move in [1L..4L] do | ||
if found then () else | ||
if visited.ContainsKey(position |> moveTo move) then () else | ||
status.phase <- MyQueue.enqueue status.phase move | ||
status.suspended <- false | ||
while not (status.suspended || status.finished) do | ||
status <- runCmd status &memory | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
visited <- visited.Add( (position |> moveTo move) , track output ) | ||
match output with | ||
//because of the reply of 2, you know you've found the oxygen system | ||
| 2L -> | ||
distance <- distance + 1 | ||
found <- true | ||
//a reply of 1 means the movement was successful | ||
| 1L -> | ||
position <- position |> moveTo move | ||
distance <- distance + 1 | ||
possible_ways <- enqueue possible_ways <| Array.append way [|move|] | ||
status.phase <- MyQueue.enqueue status.phase <| back move | ||
status.suspended <- false | ||
while not (status.suspended || status.finished) do | ||
status <- runCmd status &memory | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
if output <> 1L then failwith "path changed" | ||
distance <- distance - 1 | ||
position <- position |> moveTo (back move) | ||
//If it replies with 0, you know that location is a wall and that the droid didn't move | ||
| 0L -> | ||
() | ||
| _ -> failwith "wrong output" | ||
|
||
if not found then | ||
for move in way |> Array.rev do | ||
status.phase <- MyQueue.enqueue status.phase <| back move | ||
status.suspended <- false | ||
while not <| status.suspended || status.finished do | ||
status <- runCmd status &memory | ||
let output, q = MyQueue.dequeue status.output | ||
status.output <- q | ||
if output <> 1L then failwith "path changed" | ||
distance <- distance - 1 | ||
position <- position |> moveTo (back move) | ||
if debug then show visited | ||
sw.Stop() | ||
|
||
if found then | ||
show visited | ||
printfn "Answer Part 1 is %d" distance | ||
else printfn "Answer Part 1 not found!" | ||
|
||
printfn "executed in %d ms" sw.ElapsedMilliseconds | ||
Console.ReadKey() |> ignore | ||
0 |
This is another example for dotnet/fsharp#8069 based on https://adventofcode.com/2019/day/15
Sic stantibus rebus, it's better to switch from F# to C#
Queue<T>.Dequeue
Indeed OCaml - by which F# is inspired - has such sort of queue: see https://github.com/bcc32/advent-of-code-2019/blob/master/15/main.ml#L63
Edit
See also
OCaml - how to create queue
https://stackoverflow.com/a/20647601
The standard Queue module is not a functor. Moreover, it provides imperative-style queues. In other words, the standard enqueue/dequeue operations mutate the state of the queue
From the functional style viewpoint, It's worth considering the
Breadth-first Search in F# (BFS) with queue head and tail (but anyway they are two operations, not a single one, which is better to guarantee code correctness)
Seehttps://stackoverflow.com/a/6068257
A smart idea from haskell
pop pattern
of @glguyhttps://github.com/glguy/advent2019/blob/master/common/Advent/Search.hs#L77
could be rendered by F# active pattern
Anyway, the functional approach doesn't use mutable but immutable queue so a simple
let
returns the new queue and the head element inone
instruction: in that case however, as per haskell above, one needs a recursive bfs. Therefore you need to save everything in the state, in AoC day 15 example also the intcode program and memory: I was not a fan of it in the first place: premature optimization or actually not efficient? Afaik it is not tail recursive, so it might be problematic.