Skip to content

Instantly share code, notes, and snippets.

@waynejo
Created August 26, 2016 13:11
Show Gist options
  • Save waynejo/87047e595f64b548fc7e379b84e803b5 to your computer and use it in GitHub Desktop.
Save waynejo/87047e595f64b548fc7e379b84e803b5 to your computer and use it in GitHub Desktop.
package lascala.turtle
import lascala.turtle.common.PenState.{Down, Up}
import lascala.turtle.common._
object BasicFP {
case class TurtleState(position : Position, angle : Angle, color : PenColor, penState : PenState)
def initialTurtleState = TurtleState(
position = initialPosition,
angle = Angle(0.0),
color = initialColor,
penState = initialPenState
)
def move(log: String => Unit)(distance: Distance)(state:TurtleState):TurtleState = {
log(s"Move $distance%.1f")
val newPosition = Position(state.position.x, state.position.y).move(distance, state.angle)
if (state.penState == PenState.Down) {
common.dummyDrawLine(log, state.position, newPosition, state.color)
}
state.copy(position = newPosition)
}
def turn(log: String => Unit)(angle:Angle)(state:TurtleState) = {
log("Pen up")
// calculate new angle
val newAngle = ((state.angle.degree + angle.degree) % 360.0).degree
state.copy(angle = newAngle)
}
def penUp(log: String => Unit)(state:TurtleState) = {
log("Pen up")
state.copy(penState = Up)
}
def penDown(log: String => Unit)(state:TurtleState) = {
log("Pen down")
state.copy(penState = Down)
}
def setColor(log: String => Unit)(color:PenColor, state:TurtleState) = {
log(s"SetColor $color")
state.copy(color = color)
}
}
package lascala.turtle
import lascala.turtle.BasicFP.TurtleState
import lascala.turtle.BasicOO.Turtle
import lascala.turtle.common._
import utest._
object BasicFPTest extends TestSuite {
import Functional._
val tests = this {
'PenUp {
val turtle = BasicFP.initialTurtleState
assert(BasicFP.penUp(println)(turtle).penState == PenState.Up)
}
'PenDown {
val turtle = BasicFP.penUp(println)(BasicFP.initialTurtleState)
assert(BasicFP.penDown(println)(turtle).penState == PenState.Down)
}
'Move {
val turtle = BasicFP.initialTurtleState
val movedTurtle = BasicFP.move(println)(1.0)(turtle)
assert((movedTurtle.position.x - 1.0).abs < 0.000001)
assert((movedTurtle.position.y - 0.0).abs < 0.000001)
}
'Turn {
val turtle = BasicFP.initialTurtleState
val turnedTurtle = BasicFP.turn(println)(90.0.degree)(turtle)
assert((turnedTurtle.angle.degree - 90.0).abs < 0.000001)
}
'SetColor {
val turtle = BasicFP.initialTurtleState
val redTurtle = BasicFP.setColor(println)(PenColor.Red, turtle)
assert(redTurtle.color == PenColor.Red)
}
def log:(String)=>Unit = println
def move = BasicFP.move(log) _
def turn = BasicFP.turn(log) _
def penDown = BasicFP.penDown(log) _
def penUp = BasicFP.penUp(log) _
def setColor = BasicFP.setColor(log) _
'DrawTriangle {
val turtle = BasicFP.initialTurtleState |>
move(100) |>
turn(120.0.degree) |>
move(100) |>
turn(120.0.degree) |>
move(100) |>
turn(120.0.degree)
assert((turtle.position.x - 0.0).abs < 0.000001)
assert((turtle.position.y - 0.0).abs < 0.000001)
assert((turtle.angle.degree - 0.0).abs < 0.000001)
}
'drawPolygon {
def drawPolygon(n:Int) = {
def angle = 360.0 / n.toDouble
def angleDegrees = angle.degree
// define a function that draws one side
def oneSide(state:TurtleState, sideNumber:Int) = state |> move(100.0) |> turn(angleDegrees)
(1 to n).foldLeft(BasicFP.initialTurtleState)(oneSide)
}
drawPolygon(5)
}
}
}
package lascala.turtle
package object common {
type Distance = Double
case class Angle(degree: Double) {
def toRadian: Double = degree / 180 * math.Pi
}
implicit class AngleUnit(val angle: Double) extends AnyVal {
def degree: Angle = Angle(angle)
}
sealed trait PenState
object PenState {
case object Up extends PenState
case object Down extends PenState
}
sealed trait PenColor
object PenColor {
case object Black extends PenColor
case object Red extends PenColor
case object Blue extends PenColor
}
case class Position(x: Double, y: Double) {
def move(distance: Distance, angle: Angle): Position =
Position(x + distance * (math cos angle.toRadian), y + distance * (math sin angle.toRadian))
override def toString(): String = f"($x%.1f, $y%.1f)"
}
implicit class DoubleRound2(val d: Double) extends AnyVal {
def round2: Double = (math rint d * 100) / 100.0
}
val (initialPosition, initialColor, initialPenState) = (Position(0, 0), PenColor.Black, PenState.Down)
def dummyDrawLine(log: String => Unit, oldPos: Position, newPos: Position, color: PenColor): Unit =
log(s"...Draw line from $oldPos to $newPos using $color")
object Functional {
class PipedObject[T] private[Functional] (value:T)
{
def |>[R] (f : T => R) = f(this.value)
}
implicit def toPiped[T] (value:T) = new PipedObject[T](value)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment