module Execute open System open System.Drawing open System.IO open Load open LogoRep // stav zelvy type state = { mutable x : float; mutable y : float; mutable r : int; mutable rgb : Color; mutable w : int; mutable prog : stmt list; mutable args : int array list; } type Simulation(prog : stmt list, methods : userfun array, graphics : Graphics) = let mutable pen = new Pen(Color.Black) let mutable instructions = 0 let rec eval (vars:int array) = function | Int n -> n | Var i -> vars.[i] | Plus (x, y) -> eval vars x + eval vars y | Minus (x, y) -> eval vars x - eval vars y | Mul (x, y) -> eval vars x * eval vars y | Div (x, y) -> eval vars x / eval vars y let rec step acc = function | [] -> List.rev acc | t::others when t.prog.IsEmpty -> step acc others | t::others as ts -> let ev e = eval t.args.Head e let col c = Math.Min(255, Math.Max(0, ev c)) instructions <- instructions + 1 if instructions >= 1000000 then failwith "Interrupting simulation after 1M instructions" let instruction = t.prog.Head t.prog <- t.prog.Tail match instruction with | Turn ang -> t.r <- (t.r + ev ang) % 360 step acc ts | Pen w -> t.w <- ev w step acc ts | Color (r, g, b) -> t.rgb <- Color.FromArgb(col r, col g, col b) step acc ts | If (cond, body) -> if ev cond > 0 then t.prog <- body @ t.prog step acc ts | Repeat (num, body) -> let num = ev num if num > 0 then t.prog <- body @ (Repeat (Int (num-1), body) :: t.prog) step acc ts | Split body -> step acc (t :: {t with prog = body} :: others) | UserFun ([], -1) -> t.args <- t.args.Tail step acc ts | UserFun (args, i) -> if List.length args <> fst methods.[i] then failwithf "Bad number of arguments, expected %d, found %d" (fst methods.[i]) (List.length args) t.prog <- snd methods.[i] @ (UserFun ([], -1) :: t.prog) t.args <- Array.ofList (List.map ev args) :: t.args step acc ts | Forward len -> let len = float (ev len) let nx = t.x + len * cos (float t.r * Math.PI / 180.) let ny = t.y - len * sin (float t.r * Math.PI / 180.) if t.w > 0 then pen.Color <- t.rgb pen.Width <- float32 t.w graphics.DrawLine(pen, float32 (t.x + 350.), float32 (t.y + 350.), float32 (nx + 350.), float32 (ny + 350.)) t.x <- nx t.y <- ny if t.w > 0 then step (t :: acc) others else step acc ts member s.start steps = let mutable turtles = [{x = 0.; y = 0.; r = 90; rgb = Color.Black; w = 0; prog = prog; args = [null]}] let mutable lines = 0 while not turtles.IsEmpty && (steps = 0 || lines < steps) do turtles <- step [] turtles lines <- lines + 1 let draw (Prog (prog, methods)) steps stream = use bitmap = new Bitmap(700, 700, Imaging.PixelFormat.Format24bppRgb) use graphics = Graphics.FromImage(bitmap) use pen = new Pen(Color.Black) graphics.Clear(Color.White) let simulation = new Simulation(prog, methods, graphics) simulation.start steps bitmap.Save((stream:Stream), Imaging.ImageFormat.Png) let draw_to_file prog steps file = use stream = new FileStream(file, FileMode.Create) draw prog steps stream