commit 1a3280e7a01c2d9b0c4a933b27ba0ae0962cf9ea
Author: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date:   Sun Jan 28 23:30:54 2018 +0000

    Initial commit. I can't believe I haven't committed this already.....

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3951092
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,446 @@
+
+# Created by https://www.gitignore.io/api/csharp,git,monodevelop,visualstudio
+
+### Csharp ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Uncomment the next line to ignore your web deploy settings.
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+#*.pubxml
+*.pubxml.user
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+### Git ###
+*.orig
+
+### MonoDevelop ###
+#User Specific
+*.usertasks
+
+#Mono Project Files
+*.resources
+test-results/
+
+### VisualStudio ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+
+# Build results
+
+# Visual Studio 2015 cache/options directory
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+
+# NUNIT
+
+# Build Results of an ATL Project
+
+# .NET Core
+
+
+# Chutzpah Test files
+
+# Visual C++ cache files
+
+# Visual Studio profiler
+
+# TFS 2012 Local Workspace
+
+# Guidance Automation Toolkit
+
+# ReSharper is a .NET coding add-in
+
+# JustCode is a .NET coding add-in
+
+# TeamCity is a build add-in
+
+# DotCover is a Code Coverage Tool
+
+# Visual Studio code coverage results
+
+# NCrunch
+
+# MightyMoose
+
+# Web workbench (sass)
+
+# Installshield output folder
+
+# DocProject is a documentation generator add-in
+
+# Click-Once directory
+
+# Publish Web Output
+# TODO: Uncomment the next line to ignore your web deploy settings.
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+#*.pubxml
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+
+# NuGet Packages
+# The packages folder can be ignored because of Package Restore
+# except build/, which is used as an MSBuild target.
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+
+# Microsoft Azure Build Output
+
+# Microsoft Azure Emulator
+
+# Windows Store app package directories and files
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+# but keep track of directories ending in .cache
+
+# Others
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+
+# SQL Server files
+
+# Business Intelligence projects
+
+# Microsoft Fakes
+
+# GhostDoc plugin setting file
+
+# Node.js Tools for Visual Studio
+
+# Typescript v1 declaration files
+
+# Visual Studio 6 build log
+
+# Visual Studio 6 workspace options file
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+
+# Visual Studio LightSwitch build output
+
+# Paket dependency manager
+
+# FAKE - F# Make
+
+# JetBrains Rider
+
+# CodeRush
+
+# Python Tools for Visual Studio (PTVS)
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+
+# BizTalk build output
+
+### VisualStudio Patch ###
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+
+
+# End of https://www.gitignore.io/api/csharp,git,monodevelop,visualstudio
diff --git a/SimpleTurtle.sln b/SimpleTurtle.sln
new file mode 100755
index 0000000..2b8ec0e
--- /dev/null
+++ b/SimpleTurtle.sln
@@ -0,0 +1,17 @@
+﻿
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleTurtle", "SimpleTurtle\SimpleTurtle.csproj", "{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.ActiveCfg = Debug|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.Build.0 = Debug|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.ActiveCfg = Release|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+EndGlobal
diff --git a/SimpleTurtle/Help.txt b/SimpleTurtle/Help.txt
new file mode 100644
index 0000000..3e1b4bc
--- /dev/null
+++ b/SimpleTurtle/Help.txt
@@ -0,0 +1,45 @@
+﻿SimpleTurtle L-System Renderer
+    by Starbeamrainbowlabs
+
+Usage:
+    ./SimpleTurtle.exe {options}
+
+Options:
+    -i  --input-file    The file that contains the input L-System parameters. Defaults to stdin.
+    -o  --output-dir    The output directory in which to save the rendered / simulated L-System generations. Defaults to the name of the input file, minus the file extension - or just simply 'l-system'.
+    -s  --skip-gen      The starting generation. Default: 0.
+    -c  --count         The number of generations to render. Default: 5
+    -d  --step-dist     The number of pixels the turtle will walk forwards at a time. Default: 25.
+
+Input File Format:
+	The first line is the seed (the starting state).
+    Subsequent lines beginning with a # (hash sign) are ignored.
+    Rules are declared with an equals sign - i.e. f=f-f+, or fb=fbfafcfafb.
+    Lines starting with an exclamation mark specify directives that should be passed to the turtle - i.e. !angle=1.0472
+
+    Turtle Directives:
+        angle    Sets the angle by which the turtle should turn. Note that this is specified in radians.
+
+Example Input Files:
+
+Hilbert Curve:
+-----
+a
+a=-bf+afa+fb-
+b=+af-bfb-fa+
+-----
+
+Dragon Curve:
+-----
+f
+f=f-h
+h=f+h
+-----
+
+Sierpinski Triangle
+-----
+f
+!angle=1.0472
+h=f+h+f
+f=h-f-h
+-----
diff --git a/SimpleTurtle/LSystemEngine.cs b/SimpleTurtle/LSystemEngine.cs
new file mode 100755
index 0000000..b8f5d12
--- /dev/null
+++ b/SimpleTurtle/LSystemEngine.cs
@@ -0,0 +1,125 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security.Policy;
+
+namespace SBRL.Algorithms.LSystem
+{
+	public class Rule
+	{
+		public string Find;
+		public string Replace;
+
+		public Rule(string inFind, string inReplace)
+		{
+			Find = inFind;
+			Replace = inReplace;
+		}
+	}
+
+	/// <summary>
+	/// Simulates an L-System.
+	/// Implemented according to http://www.cs.unm.edu/~joel/PaperFoldingFractal/L-system-rules.html
+	/// </summary>
+	public class LSystemEngine
+	{
+		public readonly string Root;
+		private List<Rule> rules = new List<Rule>();
+
+		public int GenerationCount { get; private set; }
+		public string CurrentGeneration { get; private set; }
+
+		public Dictionary<string, string> Definitions { get; private set; }
+
+		public LSystemEngine(string inRoot)
+		{
+			CurrentGeneration = Root = inRoot;
+			Definitions = new Dictionary<string, string>();
+		}
+
+		public void AddRule(string find, string replace)
+		{
+			rules.Add(new Rule(find, replace));
+		}
+
+		public string Simulate()
+		{
+			List<KeyValuePair<int, Rule>> rulePositions = new List<KeyValuePair<int, Rule>>();
+			// Find all the current positions
+			foreach(Rule rule in rules)
+			{
+				List<int> positions = AllIndexesOf(CurrentGeneration, rule.Find);
+				foreach (int pos in positions)
+					rulePositions.Add(new KeyValuePair<int, Rule>(pos, rule));
+			}
+			rulePositions.Sort(compareRulePairs);
+
+			string nextGeneration = CurrentGeneration;
+			int replaceOffset = 0;
+			foreach(KeyValuePair<int, Rule> rulePos in rulePositions)
+			{
+				int nextPos = rulePos.Key + replaceOffset;
+				nextGeneration = nextGeneration.Substring(0, nextPos) + rulePos.Value.Replace + nextGeneration.Substring(nextPos + rulePos.Value.Find.Length);
+				replaceOffset += rulePos.Value.Replace.Length - rulePos.Value.Find.Length;
+			}
+			CurrentGeneration = nextGeneration;
+			GenerationCount++;
+			return CurrentGeneration;
+		}
+
+		private int compareRulePairs(KeyValuePair<int, Rule> a, KeyValuePair<int, Rule> b)
+		{
+			return a.Key - b.Key;
+		}
+
+		/// <summary>
+		/// From http://stackoverflow.com/a/2641383/1460422
+		/// </summary>
+		/// <param name="str"></param>
+		/// <param name="value"></param>
+		/// <returns></returns>
+		private List<int> AllIndexesOf(string str, string value) {
+			if (String.IsNullOrEmpty(value))
+				throw new ArgumentException("the string to find may not be empty", "value");
+			List<int> indexes = new List<int>();
+			for (int index = 0;; index += value.Length) {
+				index = str.IndexOf(value, index);
+				if (index == -1)
+					return indexes;
+				indexes.Add(index);
+			}
+		}
+
+		public static LSystemEngine FromFile(string filename)
+		{
+			return FromStream(new StreamReader(filename));
+		}
+		public static LSystemEngine FromStream(StreamReader source)
+		{
+			LSystemEngine resultSystem = new LSystemEngine(source.ReadLine());
+
+			string nextLine = string.Empty;
+			while (true) {
+				nextLine = source.ReadLine();
+				if (nextLine == null)
+					break;
+				if (!nextLine.Contains("=") || nextLine.StartsWith("#") || nextLine.Trim().Length == 0)
+					continue;
+				string[] parts = nextLine.Split(new char[]{'='}, 2);
+
+				if(parts[0].StartsWith("!"))
+				{
+					// This is a definition
+					resultSystem.Definitions.Add(parts[0].Trim('!'), parts[1]);
+				}
+				else
+				{
+					resultSystem.AddRule(parts[0].Trim(), parts[1].Trim());
+				}
+			}
+			return resultSystem;
+		}
+	}
+}
+
diff --git a/SimpleTurtle/Program.cs b/SimpleTurtle/Program.cs
new file mode 100755
index 0000000..f074e15
--- /dev/null
+++ b/SimpleTurtle/Program.cs
@@ -0,0 +1,113 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+
+using SBRL.Utilities;
+using SBRL.SimpleTurtle;
+
+namespace SBRL.Algorithms.LSystem
+{
+	class MainClass
+	{
+		public static void Main(string[] args)
+		{
+			string inputFilename = string.Empty;
+			string outputDirectory = string.Empty;
+			int startingGeneration = 0;
+			int generations = 5;
+			int stepCount = 25;
+
+			for (int i = 0; i < args.Length; i++)
+			{
+				switch (args[i].ToLower())
+				{
+					case "--help":
+						Console.WriteLine(EmbeddedFiles.ReadAllText("SimpleTurtle.Help.txt"));
+						return;
+
+					case "--input-file":
+					case "-i":
+						inputFilename = args[++i];
+						break;
+					case "--output-dir":
+					case "-o":
+						outputDirectory = args[++i];
+						break;
+					case "--skip-gen":
+					case "-s":
+						startingGeneration = int.Parse(args[++i]);
+						break;
+					case "--count":
+					case "-c":
+						generations = int.Parse(args[++i]);
+						break;
+					case "--step-dist":
+					case "-d":
+						stepCount = int.Parse(args[++i]);
+						break;
+				}
+			}
+
+			StreamReader source;
+			if (inputFilename == string.Empty)
+				source = new StreamReader(Console.OpenStandardInput());
+			else
+				source = new StreamReader(inputFilename);
+
+			if (outputDirectory == string.Empty) {
+				if (inputFilename == string.Empty)
+					outputDirectory = "./l-system";
+				else
+					outputDirectory = inputFilename.Substring(0, inputFilename.LastIndexOf("."));
+			}
+
+			// Normalise & create the output directory
+			outputDirectory = outputDirectory.TrimEnd('/') + "/";
+			if(!Directory.Exists(outputDirectory))
+				Directory.CreateDirectory(outputDirectory);
+
+			LSystemEngine lsystem = LSystemEngine.FromStream(source);
+			foreach (KeyValuePair<string, string> kvp in lsystem.Definitions)
+			{
+				Console.WriteLine("Setting {0} to {1}.", kvp.Key, kvp.Value);
+			}
+			Turtle turtle = new Turtle();
+
+			Timer gentimer = Timer.GetStarted(), stagetimer = Timer.GetStarted();
+			for (int i = startingGeneration; i < generations; i++)
+			{
+				stagetimer.Reset(); gentimer.Reset();
+
+				Console.Write($"Generation {i}: ");
+				turtle.ApplyDefinitions(lsystem.Definitions);
+				turtle.MovementStep = stepCount;
+				turtle.Commands(lsystem.CurrentGeneration);
+				turtle.Draw($"{outputDirectory}lsystem_generation_{i}.png");
+
+				Console.Write("{0:0.000000}s draw", stagetimer.GetElapsedSeconds());
+				stagetimer.Reset();
+
+				// ----------
+
+				lsystem.Simulate();
+
+				Console.Write(", {0:0.000000}s simulation", stagetimer.GetElapsedSeconds());
+				stagetimer.Reset();
+
+				// ----------
+
+				File.WriteAllText($"{outputDirectory}lsystem_generation_{i}.txt", lsystem.CurrentGeneration);
+
+				Console.WriteLine(
+					", {0:0.000000}s textoutput - {1:0.000000}s total",
+					stagetimer.GetElapsedSeconds(),
+					gentimer.GetElapsedSeconds()
+				);
+			}
+
+			//Turtle turtle = new Turtle();
+			//turtle.Commands(Console.ReadLine());
+			//turtle.Draw("turtleImage.png");
+		}
+	}
+}
\ No newline at end of file
diff --git a/SimpleTurtle/Properties/AssemblyInfo.cs b/SimpleTurtle/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4d3778d
--- /dev/null
+++ b/SimpleTurtle/Properties/AssemblyInfo.cs
@@ -0,0 +1,27 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("SimpleTurtle")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Starbeamrainbowlabs")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/SimpleTurtle/SimpleTurtle.csproj b/SimpleTurtle/SimpleTurtle.csproj
new file mode 100644
index 0000000..26215a9
--- /dev/null
+++ b/SimpleTurtle/SimpleTurtle.csproj
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProjectGuid>{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>SBRL.SimpleTurtle</RootNamespace>
+    <AssemblyName>SimpleTurtle</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Externalconsole>true</Externalconsole>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>full</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Externalconsole>true</Externalconsole>
+    <PlatformTarget>x86</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="Mono.Cairo" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Turtle.cs" />
+    <Compile Include="LSystemEngine.cs" />
+    <Compile Include="Utilities\EmbeddedFiles.cs" />
+    <Compile Include="Utilities\Timer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Help.txt">
+      <LogicalName>SimpleTurtle.Help.txt</LogicalName>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Utilities\" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/SimpleTurtle/SimpleTurtle.sln b/SimpleTurtle/SimpleTurtle.sln
new file mode 100755
index 0000000..ee91909
--- /dev/null
+++ b/SimpleTurtle/SimpleTurtle.sln
@@ -0,0 +1,17 @@
+﻿
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleTurtle", "SimpleTurtle.csproj", "{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.ActiveCfg = Debug|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.Build.0 = Debug|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.ActiveCfg = Release|x86
+		{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+EndGlobal
diff --git a/SimpleTurtle/Turtle.cs b/SimpleTurtle/Turtle.cs
new file mode 100755
index 0000000..76d426b
--- /dev/null
+++ b/SimpleTurtle/Turtle.cs
@@ -0,0 +1,186 @@
+﻿using System;
+using Cairo;
+using System.Text.RegularExpressions;
+using System.Resources;
+using System.Collections.Generic;
+
+namespace SBRL.SimpleTurtle
+{
+	public class Area
+	{
+		public double X;
+		public double Y;
+		public double Width;
+		public double Height;
+
+		public Area(double inX, double inY, double inWidth, double inHeight)
+		{
+			X = inX;
+			Y = inY;
+			Width = inWidth;
+			Height = inHeight;
+		}
+	}
+	public class Turtle
+	{
+		private bool strictMode = false;
+		private string commandQueue;
+
+		private PointD position;
+		private Area bounds;
+		private double heading;
+		private double headingStep;
+		public double HeadingStep
+		{
+			get { return headingStep; }
+			set { headingStep = value; }
+		}
+		private double movementStep ;
+		public double MovementStep
+		{
+			get { return movementStep; }
+			set { movementStep = value; }
+		}
+
+		public Turtle ()
+		{
+			Reset();
+		}
+
+		public void ApplyDefinitions(Dictionary<string, string> definitions)
+		{
+			foreach(KeyValuePair<string, string> 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;
+						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 Reset()
+		{
+			commandQueue = string.Empty;
+			position = new PointD(0, 0);
+			bounds = new Area(position.X, position.Y, 1, 1);
+			heading = 0;
+			headingStep = Math.PI / 2;
+			movementStep = 25;
+		}
+	}
+}
+
diff --git a/SimpleTurtle/Utilities/EmbeddedFiles.cs b/SimpleTurtle/Utilities/EmbeddedFiles.cs
new file mode 100644
index 0000000..9b101ba
--- /dev/null
+++ b/SimpleTurtle/Utilities/EmbeddedFiles.cs
@@ -0,0 +1,191 @@
+﻿using System;
+using System.Reflection;
+using System.IO;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+namespace SBRL.Utilities
+{
+	/// <summary>
+	/// A collection of static methods for manipulating embedded resources.
+	/// </summary>
+	/// <description>
+	/// v0.5, by Starbeamrainbowlabs <feedback@starbeamrainbowlabs.com>
+	/// Last updated 8th August 2016.
+	/// Licensed under MPL-2.0.
+	/// 
+	/// Changelog:
+	/// v0.1 (25th July 2016):
+	/// 	- Initial release.
+	/// v0.2 (8th August 2016):
+	/// 	- Changed namespace.
+	/// v0.3 (21st January 2017):
+	/// 	- Added GetRawReader().
+	/// v0.4 (8th April 2017):
+	/// 	- Removed unnecessary using statement.
+	/// v0.5 (3rd September 2017):
+	/// 	- Changed namespace
+	/// </description>
+	public static class EmbeddedFiles
+	{
+		/// <summary>
+		/// An array of the filenames of all the resources embedded in the calling assembly.
+		/// </summary>
+		/// <value>The resource list.</value>
+		public static string[] ResourceList
+		{
+			get
+			{
+				return Assembly.GetCallingAssembly().GetManifestResourceNames();
+			}
+		}
+
+		public static string GetResourceListText()
+		{
+			StringWriter result = new StringWriter();
+			result.WriteLine("Files embedded in {0}:", Assembly.GetCallingAssembly().GetName().Name);
+			foreach (string filename in ResourceList)
+				result.WriteLine(" - {0}", filename);
+			return result.ToString();
+		}
+		/// <summary>
+		/// Writes a list of embedded resources to the Console's standard output.
+		/// </summary>
+		public static void WriteResourceList()
+		{
+			Console.WriteLine(GetResourceListText());
+		}
+
+		/// <summary>
+		/// Gets a StreamReader attached to the specified embedded resource.
+		/// </summary>
+		/// <param name="filename">The filename of the embedded resource to get a StreamReader of.</param>
+		/// <returns>A StreamReader attached to the specified embedded resource.</returns>
+		public static StreamReader GetReader(string filename)
+		{
+			return new StreamReader(GetRawReader(filename));
+		}
+
+		/// <summary>
+		/// Gets a raw Stream that's attached to the specified embedded resource.
+		/// Useful when you want to copy an embedded resource to some other stream.
+		/// </summary>
+		/// <param name="filename">The path to the embedded resource.</param>
+		/// <returns>A raw Stream object attached to the specified file..</returns>
+		public static Stream GetRawReader(string filename)
+		{
+			return Assembly.GetCallingAssembly().GetManifestResourceStream(filename);
+		}
+
+		/// <summary>
+		/// Gets the specified embedded resource's content as a byte array.
+		/// </summary>
+		/// <param name="filename">The filename of the embedded resource to get conteent of.</param>
+		/// <returns>The specified embedded resource's content as a byte array.</returns>
+		public static byte[] ReadAllBytes(string filename)
+		{
+			// Referencing the Result property will block until the async method completes
+			return ReadAllBytesAsync(filename).Result;
+		}
+		/// <summary>
+		/// Gets the specified embedded resource's content as a byte array asynchronously.
+		/// </summary>
+		/// <param name="filename">The filename of the embedded resource to get conteent of.</param>
+		/// <returns>The specified embedded resource's content as a byte array.</returns>
+		public static async Task<byte[]> ReadAllBytesAsync(string filename)
+		{
+			using (Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(filename))
+			using (MemoryStream temp = new MemoryStream())
+			{
+				await resourceStream.CopyToAsync(temp);
+				return temp.ToArray();
+			}
+		}
+
+		/// <summary>
+		/// Gets all the text stored in the specified embedded resource.
+		/// </summary>
+		/// <param name="filename">The filename to fetch the content of.</param>
+		/// <returns>All the text stored in the specified embedded resource.</returns>
+		public static string ReadAllText(string filename)
+		{
+			using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename)))
+			{
+				return resourceReader.ReadToEnd();
+			}
+		}
+		/// <summary>
+		/// Gets all the text stored in the specified embedded resource asynchronously.
+		/// </summary>
+		/// <param name="filename">The filename to fetch the content of.</param>
+		/// <returns>All the text stored in the specified embedded resource.</returns>
+		public static async Task<string> ReadAllTextAsync(string filename)
+		{
+			using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename)))
+			{
+				return await resourceReader.ReadToEndAsync();
+			}
+		}
+
+		/// <summary>
+		/// Enumerates the lines of text in the specified embedded resource.
+		/// </summary>
+		/// <param name="filename">The filename of the embedded resource to enumerate.</param>
+		/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
+		public static IEnumerator<string> EnumerateLines(string filename)
+		{
+			using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename)))
+			{
+				string nextLine;
+				while ((nextLine = resourceReader.ReadLine()) != null)
+				{
+					yield return nextLine;
+				}
+			}
+		}
+		/// <summary>
+		/// Enumerates the lines of text in the specified embedded resource asynchronously.
+		/// Each successive call returns a task that, when complete, returns the next line of text stored
+		/// in the embedded resource.
+		/// </summary>
+		/// <param name="filename">The filename of the embedded resource to enumerate.</param>
+		/// <returns>An IEnumerator that enumerates the specified embedded resource.</returns>
+		public static IEnumerable<Task<string>> EnumerateLinesAsync(string filename)
+		{
+			using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename)))
+			{
+				while (!resourceReader.EndOfStream)
+				{
+					yield return resourceReader.ReadLineAsync();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets all the lines of text in the specified embedded resource.
+		/// You might find EnumerateLines(string filename) more useful depending on your situation.
+		/// </summary>
+		/// <param name="filename">The filename to obtain the lines of text from.</param>
+		/// <returns>A list of lines in the specified embedded resource.</returns>
+		public static List<string> GetAllLines(string filename)
+		{
+			// Referencing the Result property will block until the async method completes
+			return GetAllLinesAsync(filename).Result;
+		}
+		/// <summary>
+		/// Gets all the lines of text in the specified embedded resource asynchronously.
+		/// </summary>
+		/// <param name="filename">The filename to obtain the lines of text from.</param>
+		/// <returns>A list of lines in the specified embedded resource.</returns>
+		public static async Task<List<string>> GetAllLinesAsync(string filename)
+		{
+			List<string> lines = new List<string>();
+			IEnumerable<Task<string>> lineIterator = EnumerateLinesAsync(filename);
+			foreach (Task<string> nextLine in lineIterator)
+			{
+				lines.Add(await nextLine);
+			}
+			return lines;
+		}
+	}
+}
diff --git a/SimpleTurtle/Utilities/Timer.cs b/SimpleTurtle/Utilities/Timer.cs
new file mode 100755
index 0000000..547c7ad
--- /dev/null
+++ b/SimpleTurtle/Utilities/Timer.cs
@@ -0,0 +1,36 @@
+﻿﻿using System;
+
+namespace SBRL.Utilities
+{
+	public class Timer
+	{
+		public DateTime StartTime { get; private set; }
+
+		public Timer() { }
+
+		public static Timer GetStarted()
+		{
+			Timer result = new Timer();
+			result.Start();
+			return result;
+		}
+
+		public void Start()
+		{
+			StartTime = DateTime.Now;
+		}
+
+		public float GetElapsedSeconds()
+		{
+			DateTime now = DateTime.Now;
+			TimeSpan elasped = now - StartTime;
+			StartTime = now;
+			return (float)elasped.Ticks / TimeSpan.TicksPerSecond;
+		}
+
+		public void Reset()
+		{
+			Start();
+		}
+	}
+}
\ No newline at end of file
