using System; using Cairo; using System.Text.RegularExpressions; using System.Resources; using System.Collections.Generic; namespace SBRL.SimpleTurtle { public class TurtleState : ICloneable { public PointD Position; public double Heading; public TurtleState(PointD inPosition, double inHeading) { Position = inPosition; Heading = inHeading; } public object Clone() { return new TurtleState(Position, Heading); } } public class Turtle { public bool StrictMode = false; private string commandQueue; private readonly Stack states = new Stack(); public PointD Position { get => states.Peek().Position; protected set => states.Peek().Position = value; } public double Heading { get => states.Peek().Heading; protected set => states.Peek().Heading = value; } private Area bounds; public double HeadingStep { get; set; } public double MovementStep { get; set; } public Turtle () { Reset(); } public void ApplyDefinitions(Dictionary definitions) { foreach(KeyValuePair definition in definitions) { switch(definition.Key.ToLower()) { case "angle": HeadingStep = double.Parse(definition.Value); break; } } } public bool Commands(string commandText) { // Remove all whitespace commandText = Regex.Replace(commandText, @"\s+", ""); commandText = commandText.Replace("h", "f"); string okCommands = "f+-"; foreach(char ch in commandText) { if(okCommands.Contains(ch.ToString())) { switch(ch) { case 'f': Forwards(); break; case '+': Turn(false); break; case '-': Turn(true); break; case '[': Save(); break; case ']': Restore(); break; default: if (StrictMode) { Console.WriteLine("The unexpected character '{0}' slipped through the net!", ch); return false; } break; } } else if(StrictMode) { Console.Error.WriteLine("Error: unexpected character '{0}'", ch); return false; } } commandQueue += commandText; return true; } public void Draw(string filename, bool reset = true) { ImageSurface canvas = new ImageSurface(Format.ARGB32, (int)Math.Ceiling(bounds.Width + 10), (int)Math.Ceiling(bounds.Height + 10)); Context context = new Context(canvas); Position = new PointD(-bounds.X + 5, -bounds.Y + 5); Heading = 0; context.LineWidth = 3; context.MoveTo(Position); foreach(char ch in commandQueue) { switch(ch) { case 'f': PointD newPosition = new PointD( Position.X + MovementStep * Math.Sin(Heading), Position.Y + MovementStep * Math.Cos(Heading) ); context.LineTo(newPosition); Position = newPosition; break; case '+': Heading += HeadingStep; break; case '-': Heading -= HeadingStep; break; } } context.Stroke(); canvas.WriteToPng(string.Format(filename)); context.Dispose(); canvas.Dispose(); if(reset) Reset(); } public void Forwards() { PointD newPosition = new PointD( Position.X + MovementStep * Math.Sin(Heading), Position.Y + MovementStep * Math.Cos(Heading) ); if (newPosition.X > bounds.X + bounds.Width) bounds.Width += newPosition.X - Position.X; if (newPosition.Y > bounds.Y + bounds.Height) bounds.Height += newPosition.Y - Position.Y; if (newPosition.X < bounds.X) { bounds.X = newPosition.X; bounds.Width += Position.X - newPosition.X; } if (newPosition.Y < bounds.Y) { bounds.Y = newPosition.Y; bounds.Height += Position.Y - newPosition.Y; } Position = newPosition; } public void Turn(bool anticlockwise = false) { if (!anticlockwise) Heading += HeadingStep; else Heading -= HeadingStep; } public void Save() { states.Push(states.Peek().Clone() as TurtleState); } public bool Restore() { if (states.Count <= 1) return false; states.Pop(); return true; } public void Reset() { states.Clear(); states.Push(new TurtleState(new PointD(0, 0), 0)); commandQueue = string.Empty; bounds = new Area(Position.X, Position.Y, 1, 1); HeadingStep = Math.PI / 2; MovementStep = 25; } } }