Tuesday 21 April 2009

LOGO Translator

open System;
let sin = Math.Sin
let cos = Math.Cos
let atan = Math.Atan
let float_of_int x = (float x)
let int_of_float x = (int x)


type logo = | Home
| Forward of int
| Turn of int
| For of int * logo list


let pi = 4.0 * atan 1.0
let dsin t = sin(pi * (float_of_int t) / 180.0)
and dcos t = cos(pi * (float_of_int t) / 180.0)


let forward (x,y,d) steps = (x + (int_of_float ((float steps) * dsin d)),
y + (int_of_float ((float steps) * dcos d)), d)


let compose f g = fun x -> f(g(x))


let rec translate h x = match x with
| Home -> fun pos -> pos
| Forward(n) -> fun pos ->
let
pos' = forward pos n
h pos'
| Turn(n) -> fun (x,y,d) -> (x,y,d + n)
| For(i, xs) -> let f = translate_prog h xs in
if
i > 1 then
compose f (translate h (For(i - 1, xs)))
else
f
and translate_prog h xs = List.fold_left (fun f x -> compose (translate h x) f) (fun pos -> pos) xs
let current = (0, 0, 0)
let sample = [Home; For(20, [Turn 18; For(36, [Forward 10; Turn 10])])]


let f = translate_prog (fun x -> (printf "%A" x) ; x) sample


let result = f current

2 comments:

Ben said...

Okay so I'm a bit behind. Text parsing to follow:

package logo

import javax.swing.JPanel;
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Color;

class Drawing(points: List[(Int,Int)]) extends JPanel {
val HEIGHT = 500
val WIDTH = 500

var frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH,HEIGHT);
frame.setVisible(true);
frame.getContentPane().add(this,java.awt.BorderLayout.CENTER);

override def paintComponent(g: Graphics) = {
var lastPoint : (Int,Int) = null
for (p <- points.map(p => (p._1+WIDTH/2,p._2+HEIGHT/2))){
if(lastPoint != null)
g.drawLine(lastPoint._1, lastPoint._2,p._1,p._2)
lastPoint = p;
}
}
}

abstract class LogoOp
case class Home extends LogoOp
case class Forward(x: Int) extends LogoOp
case class Turn(x: Int) extends LogoOp
case class For(i: Int, e: List[LogoOp]) extends LogoOp

object Logo {
implicit def dblToInt(d: Double): Int = if (d > 0 ) (d +0.5).toInt else (d-0.5).toInt

def forward(pos: (Int,Int), x: Int, d: Int) : (Int,Int) =
(pos._1 + x * Math.sin(Math.toRadians(d)), pos._2 + x * Math.cos(Math.toRadians(d)))

def evaluate(e : List[LogoOp], pos: (Int,Int), heading: Int)
: List[(Int,Int)]= e match {
case Nil => Nil
case Home() :: _ => (0,0) :: evaluate(e.tail, (0,0),0)
case Forward(x) :: _ => forward(pos,x,heading) ::
evaluate(e.tail, forward(pos,x,heading), heading)
case Turn(x) :: _ => evaluate(e.tail, pos, heading + x)
case For(0, y) :: _ => evaluate(e.tail, pos, heading)
case For(x, y) :: tail => evaluate(y ::: For(x-1, y)::e.tail,pos, heading)
case _ => Nil
}
}

object Main {
def main(args: Array[String]) = {
val d = new Drawing(Logo.evaluate(List(Home(), For(18,List(Turn(20),For(36,List(Forward(10),Turn(10)))))),(0,0),0));
}
}

Ben said...

Done :D logo in Scala.
Parses the command _string_ then does some pattern matching, and spits out a list of points. These are consumed by a thin JPanel derivation which renders them.
Should run with:
fsc Logo.scala && logo.Main

//--

package logo

import javax.swing.JPanel
import javax.swing.JFrame
import java.awt.Graphics
import scala.util.parsing.combinator.JavaTokenParsers

class Drawing(points: List[(Int,Int)]) extends JPanel {
val HEIGHT = 350
val WIDTH = 350

var frame = new JFrame();
frame.getContentPane().add(this,java.awt.BorderLayout.CENTER)
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
frame.setSize(WIDTH,HEIGHT)
frame.setVisible(true)

override def paintComponent(g: Graphics) = {
var lastPoint : (Int,Int) = null
for (p <- points.map(p => (p._1+WIDTH/2,p._2+HEIGHT/2))){
if(lastPoint != null)
g.drawLine(lastPoint._1, lastPoint._2,p._1,p._2)
lastPoint = p;
}
}
}

object Logo {
abstract class LogoOp
case class Home extends LogoOp
case class Forward(x: Int) extends LogoOp
case class Turn(x: Int) extends LogoOp
case class For(i: Int, e: List[LogoOp]) extends LogoOp

implicit def dblToInt(d: Double): Int =
if (d > 0 ) (d +0.5).toInt
else (d-0.5).toInt

def forward(pos: (Int,Int), x: Int, d: Int) : (Int,Int) =
(pos._1 + x * Math.sin(Math.toRadians(d)),
pos._2 + x * Math.cos(Math.toRadians(d)))

def parse(s: String) : List[LogoOp] = LogoParser.parse(s).get

def evaluate(s : String) : List[(Int,Int)]= evaluate(parse(s),(0,0),0);

def evaluate(e : List[LogoOp], pos: (Int,Int), heading: Int)
: List[(Int,Int)]= e match {
case Nil => Nil
case Home() :: _ => (0,0) :: evaluate(e.tail, (0,0),0)
case Forward(x) :: _ => forward(pos,x,heading) ::
evaluate(e.tail, forward(pos,x,heading), heading)
case Turn(x) :: _ => evaluate(e.tail, pos, heading + x)
case For(0, y) :: _ => evaluate(e.tail, pos, heading)
case For(x, y) :: tail => evaluate(y ::: For(x-1, y)::e.tail,pos, heading)
case _ => Nil
}

object LogoParser extends JavaTokenParsers {
def expr : Parser[List[LogoOp]] = rep(home|forward|turn|forexp)
def home : Parser[Home] = "HOME" ^^ (x => Home())
def forward : Parser[Forward] = "FORWARD"~wholeNumber ^^
{case x~fwd => Forward(fwd.toInt)}
def turn : Parser[Turn] = "TURN"~wholeNumber ^^
{case x~trn => Turn(trn.toInt)}
def forexp : Parser[For] = "FOR("~wholeNumber~","~expr~")" ^^
{ case x~i~y~e~z => For(i.toInt,e)}

def parse(text : String) = parseAll(expr, text)
}
}

object Main {
def main(args: Array[String]) = {
val d = new Drawing(Logo.evaluate(
"HOME FOR(36, TURN 10 FOR(36, FORWARD 10 TURN 10))"));
}
}