Skip to content

Instantly share code, notes, and snippets.

@Khubajsn
Created June 14, 2013 16:27
Show Gist options
  • Select an option

  • Save Khubajsn/5783322 to your computer and use it in GitHub Desktop.

Select an option

Save Khubajsn/5783322 to your computer and use it in GitHub Desktop.
Twitach - Turtle WITh A CHalk.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Twitach
{
class TwitachException : Exception
{
public TwitachException(string message) : base(message) { }
}
class Command
{
public string Identifier;
private List<Argument> Arguments;
public static string[] Identifiers = new string[] {
"forward", "left", "right", "pen", "color"
};
public static bool IsValidIdentifier(string id)
{
return Command.Identifiers.Contains(id);
}
public T GetArgument<T>(int index) where T : struct
{
try
{
return (T)this.Arguments[index].Value;
}
catch
{
return default(T);
}
}
public Command(string id, List<string> rawArguments)
{
id = id.ToLower();
if (!Command.IsValidIdentifier(id))
{
throw new TwitachException("Unknown command identifier '" + id + "'");
}
this.Identifier = id;
this.Arguments = new List<Argument>();
foreach (string rawArgument in rawArguments)
{
this.Arguments.Add(new Argument(rawArgument));
}
}
public override string ToString()
{
if (this.Identifier == null)
{
throw new InvalidOperationException("Command.ToString was called, whilst the command's identifier is null.");
}
if (this.Arguments.Count == 0)
{
return this.Identifier + "(void)";
}
else
{
return this.Identifier + "(" + String.Join(",", this.Arguments) + ")";
}
}
}
class Argument
{
public object Value;
public Type ValueType;
public Argument(string value)
{
bool isString = false;
foreach (char c in value)
{
if (!Char.IsDigit(c))
{
isString = false;
break;
}
}
if (isString)
{
this.Value = value;
this.ValueType = Type.GetType("System.String");
}
else
{
try
{
this.Value = Convert.ToInt32(value);
this.ValueType = Type.GetType("System.Int32");
}
catch (System.FormatException)
{
throw new TwitachException("Argument value '" + value + "' was found to be integer, however, Convert.ToInt32 has thrown a System.FormatException.");
}
}
}
public override string ToString()
{
return (string)this.Value;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Twitach
{
public partial class TwitachForm : Form
{
public TwitachForm()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Bitmap b = new Bitmap(700, 700);
this.pictureBox1.Image = b;
}
private void loadScriptFromFileButton_Click(object sender, EventArgs e)
{
Lexer l = new Lexer();
l.Commands.Clear();
FileStream fs = new FileStream("D:\\inputs\\twitach1.txt", FileMode.Open);
try
{
l.Lex(fs);
Renderer.Commands = l.Commands;
Renderer.Position = new TurtlePos();
Renderer.Angle = new TurtleAngle();
Renderer.RenderSteps(1000, Graphics.FromImage(this.pictureBox1.Image));
this.pictureBox1.Invalidate();
}
finally
{
fs.Close();
}
}
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Invalidate();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace Twitach
{
class Lexer
{
public List<Command> Commands = new List<Command>();
public void Lex(Stream s)
{
string buffer = "";
string identifier = "";
List<string> arguments = new List<string>();
int lineNumber = 1;
int caretPosition = 0;
LexerAction currentAction = LexerAction.None;
StreamReader sr = new StreamReader(s);
while (!sr.EndOfStream)
{
char c = (char)sr.Read();
char nextChar = (char)sr.Peek();
caretPosition++;
if (c == '\n')
{
lineNumber++;
caretPosition = 0;
}
if (currentAction == LexerAction.None)
{
if (Char.IsLetter(c)) {
currentAction = LexerAction.ReadingIdentifier;
buffer += c;
}
else if (Char.IsWhiteSpace(c))
{
continue;
}
else
{
throw new LexerException("Character not allowed here - at line " + lineNumber + " character " + caretPosition + ": '" + c + "'.");
}
}
else if (currentAction == LexerAction.ReadingIdentifier)
{
if (Char.IsLetter(c))
{
buffer += c;
}
else if (Char.IsWhiteSpace(c)) {
continue;
}
else if (c == '(')
{
identifier = buffer;
buffer = "";
currentAction = LexerAction.ReadingArguments;
}
else
{
throw new LexerException("Expected letter, got '" + c + "' - line " + lineNumber + " character " + caretPosition + " instead of '" + c + "'.");
}
}
else if (currentAction == LexerAction.ReadingArguments)
{
if (Char.IsLetterOrDigit(c) || c == '-' || c == '+')
{
buffer += c;
}
else if (c == ',')
{
arguments.Add(buffer);
buffer = "";
}
else if (c == ')')
{
if (buffer.Length > 0) // If the command only receives one argument, it's still in the buffer.
{
arguments.Add(buffer);
}
this.Commands.Add(new Command(identifier, arguments));
buffer = "";
identifier = "";
arguments.Clear();
currentAction = LexerAction.None;
}
}
}
}
}
enum LexerAction
{
None,
ReadingIdentifier,
ReadingArguments
}
class LexerException : Exception {
public LexerException(string message) : base(message) { }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Twitach
{
class Renderer
{
public static List<Command> Commands;
public static TurtlePos Position;
public static TurtleAngle Angle;
public static Pen Pen = Pens.Black;
public static bool PenActive;
public static Graphics G;
public static void RenderSteps(int stepsAmount, Graphics g)
{
Renderer.Position = new TurtlePos();
Renderer.Angle = new TurtleAngle();
Renderer.PenActive = false;
Renderer.Pen = Pens.Black;
Renderer.G = g;
Renderer.G.FillRectangle(Brushes.White, 0, 0, 700, 700);
if (Renderer.Commands == null)
{
return;
}
foreach (Command cmd in Renderer.Commands)
{
if (cmd.Identifier == "pen")
{
Renderer.PenActive = cmd.GetArgument<int>(0) > 0;
Renderer.Pen = new Pen(Renderer.Pen.Color, cmd.GetArgument<int>(0));
}
else if (cmd.Identifier == "color")
{
Renderer.Pen = new Pen(Color.FromArgb(cmd.GetArgument<int>(0), cmd.GetArgument<int>(1), cmd.GetArgument<int>(2)), Renderer.Pen.Width);
}
else if (cmd.Identifier == "forward")
{
Renderer.Move(cmd.GetArgument<int>(0));
}
else if (cmd.Identifier == "left" || cmd.Identifier == "right")
{
Renderer.Angle.RotateByDegrees((cmd.Identifier == "left" ? 1 : -1) * cmd.GetArgument<int>(0));
}
}
}
private static void Move(int px)
{
double targetX = Renderer.Position.x + (px * Math.Cos(Renderer.Angle.Radians));
double targetY = Renderer.Position.y - (px * Math.Sin(Renderer.Angle.Radians));
if (Renderer.PenActive)
{
int tx = (int)Math.Round(targetX);
int ty = (int)Math.Round(targetY);
Renderer.G.DrawLine(Renderer.Pen, Renderer.Position.ToPoint(), new Point(tx, ty));
}
Renderer.Position.x = targetX;
Renderer.Position.y = targetY;
}
}
public class TurtlePos
{
public double x = 350.0;
public double y = 350.0;
public Point ToPoint()
{
return new Point((int)Math.Round(this.x), (int)Math.Round(this.y));
}
}
public class TurtleAngle
{
public int Degrees;
public double Radians;
public TurtleAngle()
{
this.Degrees = 90;
this.Radians = 0.0;
this.RecalculateRadians();
}
private void RecalculateRadians()
{
this.Radians = this.Degrees * Math.PI / 180;
}
public void RotateByDegrees(int degreesAmount)
{
if (degreesAmount < 0)
{
degreesAmount = Math.Abs(degreesAmount);
if ((this.Degrees - degreesAmount) >= 0)
{
this.Degrees -= degreesAmount;
}
else
{
this.Degrees = 360 - Math.Abs(degreesAmount - this.Degrees);
}
}
else
{
this.Degrees = this.Degrees + degreesAmount;
}
this.Degrees %= 360;
this.RecalculateRadians();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment