Saturday, November 12, 2011

ScalaFX: Jumping Frogs Puzzle







Expect frequent changes.





ScalaFX example: Jumping Frogs Puzzle


This is my first ScalaFX example. It is a jumping frogs puzzle.




Here is the top level JumpingFrogsPuzzle application code.




Note that the definition of stage makes use of a kind of object literal notation (one of the main goals of the ScalaFX library is to provide a DSL to use JavaFX in a more convenient way).






object JumpingFrogsPuzzle extends JFXApp {

import theViewValues.canvasShape
import theViewValues.stoneShapes
import theViewValues.dummyFrogShape
import theView.optionalFrogShapes

val frogShapes =
for {
optionalFrogShape <- optionalFrogShapes
} yield {
optionalFrogShape match {
case Some(frogShape) => frogShape
case None => dummyFrogShape
}
}

stage = new Stage {
title = TITLE
scene = new Scene {
content =
canvasShape :: stoneShapes ::: frogShapes
}
}
}




theView


The top level application code uses an object theView.






import theViewValues.optionalFrogShapes

object theView extends View(theModel, theControl, optionalFrogShapes)




theViewValues


The top level application code also uses an object theViewValues.






object theViewValues {
val canvasShape =
theCanvasShape

val stoneShapes =
for {
i <- STONE_NUMBER_LIST
} yield StoneShape(i)

val dummyFrogShape =
theDummyFrogShape

import theModelValues.optionalFrogMap

val optionalFrogShapes =
for {
i <- STONE_NUMBER_LIST
} yield {
if (i < NUMBER_OF_FROGS) {
Some(new GreenFrogShape(i, optionalFrogMap(i).get))
} else if (i == NUMBER_OF_FROGS) {
None
} else {
Some(new RedFrogShape(i, optionalFrogMap(i).get))
}
}
}




theControl and theModel


The theView code uses objects theControl and theModel.






object theControl extends Control

import theModelValues.optionalFrogMap

object theModel extends Model(optionalFrogMap)




Shapes


The theViewValues code uses shapes code.






case object theCanvasShape extends Rectangle {
width = CANVAS_WIDTH
height = CANVAS_HEIGHT
fill = CANVAS_FILL
}

case class StoneShape(position: Int) extends Rectangle {
x = FIRST_STONE_X + STONE_STEP * position
y = STONE_Y
width = STONE_WIDTH
height = STONE_HEIGHT
fill = STONE_FILL
stroke = STONE_STROKE
strokeWidth = STONE_STROKE_WIDTH
}

abstract class FrogShape(startPosition: Int, frog: Frog) extends Circle {
val getFrog = frog
centerX = FIRST_FROG_CENTER_X + STONE_STEP * startPosition
centerY = FROG_CENTER_Y
radius = FROG_RADIUS
}

class GreenFrogShape(startPosition: Int, frog: Frog)
extends FrogShape(startPosition, frog) {
fill = GREEN_FROG_FILL
}

class RedFrogShape(startPosition: Int, frog: Frog)
extends FrogShape(startPosition, frog) {
fill = RED_FROG_FILL
}

case object theDummyFrogShape extends FrogShape(-1, theDummyFrog)




theModelValues


The theModel code uses an object theModelValues.






object theModelValues {
val optionalFrogMap =
(for {
i <- STONE_NUMBER_LIST
} yield {
if (i < NUMBER_OF_FROGS) {
i -> Some(new LeftFrog())
} else if (i == NUMBER_OF_FROGS) {
i -> None
} else {
i -> Some(new RightFrog())
}
}).toMap
}




Frogs


The frog shapes code uses frogs code.






trait Frog {
def movesToRight: Boolean

def movesToLeft: Boolean
}

class LeftFrog() extends Frog {
def movesToRight = true

def movesToLeft = false
}

class RightFrog() extends Frog {
def movesToRight = false

def movesToLeft = true
}

case object theDummyFrog extends Frog {
def movesToRight = false

def movesToLeft = false
}




Control code


Here is the control code.






class Control {
def update(model: Model, view: View) {
view.optionalFrogShapes.foreach {
case Some(frogShape) => frogShape.onMouseClicked =
if (model.canJumpOneRight(frogShape.getFrog)) {
view.jumpOneRight(frogShape)
} else if (model.canJumpTwoRight(frogShape.getFrog)) {
view.jumpTwoRight(frogShape)
} else if (model.canJumpOneLeft(frogShape.getFrog)) {
view.jumpOneLeft(frogShape)
} else if (model.canJumpTwoLeft(frogShape.getFrog)) {
view.jumpTwoLeft(frogShape)
}
case None =>
}
}
}




Model code


Here is the model code.






class Model(var optionalFrogMap: Map[Int, Option[Frog]]) {
private def isAtRight(i: Int) =
i == NUMBER_OF_STONES - 1

private def isAtRightOrOneButRight(i: Int) =
i == NUMBER_OF_STONES - 1 || i == NUMBER_OF_STONES - 2

private def isAtLeft(i: Int) =
i == 0

private def isAtLeftOrOneButLeft(i: Int) =
i == 0 || i == 1

private def canMoveOneRightAt(i: Int) =
!isAtRight(i) &&
optionalFrogMap(i + 1) == None

private def canMoveTwoRightAt(i: Int) =
!isAtRightOrOneButRight(i) &&
optionalFrogMap(i + 1).get.movesToLeft &&
optionalFrogMap(i + 2) == None

private def canMoveOneLeftAt(i: Int) =
!isAtLeft(i) &&
optionalFrogMap(i - 1) == None

private def canMoveTwoLeftAt(i: Int) =
!isAtLeftOrOneButLeft(i) &&
optionalFrogMap(i - 1).get.movesToRight &&
optionalFrogMap(i - 2) == None

def positionOf(frog: Frog) =
(for {
(i, optionalFrog) <- optionalFrogMap
if (optionalFrog == Some(frog))
} yield i).head

def canJumpOneRight(frog: Frog) =
canMoveOneRightAt(positionOf(frog))

def canJumpTwoRight(frog: Frog) =
canMoveTwoRightAt(positionOf(frog))

def canJumpOneLeft(frog: Frog) =
canMoveOneLeftAt(positionOf(frog))

def canJumpTwoLeft(frog: Frog) =
canMoveTwoLeftAt(positionOf(frog))

def update(next: Int => Int)(frog: Frog) {
val j = positionOf(frog)
optionalFrogMap = for {
entry@(i, _) <- optionalFrogMap
} yield {
if (i == j) {
(i, None)
} else if (i == next(j)) {
(i, optionalFrogMap(j))
} else {
entry
}
}
}
}




View code


Here is the view code.






class View(model: Model, control: Control, val optionalFrogShapes: List[Option[FrogShape]]) {
control.update(model, this)

private def update(length: Int, next: (Double, Double) => Double)(frogShape: FrogShape) {
val frogShapeCenterX = FIRST_FROG_CENTER_X + STONE_STEP * model.positionOf(frogShape.getFrog)
val frogShapeCenterY = FROG_CENTER_Y

Timeline(Seq(
at(length * TIME s) {
frogShape.centerY -> (frogShapeCenterY - length * STONE_STEP / 2)
},
at(length * TIME s) {
frogShape.centerX -> next(frogShapeCenterX, length * STONE_STEP / 2)
},
at(2 * length * TIME s) {
frogShape.centerY -> frogShapeCenterY
},
at(2 * length * TIME s) {
frogShape.centerX -> next(frogShapeCenterX, length * STONE_STEP)
}
)).play()
}

def jumpOneRight(frogShape: FrogShape) {
update(1, _ + _)(frogShape)
model.update(_ + 1)(frogShape.getFrog)
control.update(model, this)
}

def jumpTwoRight(frogShape: FrogShape) {
update(2, _ + _)(frogShape)
model.update(_ + 2)(frogShape.getFrog)
control.update(model, this)
}

def jumpOneLeft(frogShape: FrogShape) {
update(1, _ - _)(frogShape)
model.update(_ - 1)(frogShape.getFrog)
control.update(model, this)
}

def jumpTwoLeft(frogShape: FrogShape) {
update(2, _ - _)(frogShape)
model.update(_ - 2)(frogShape.getFrog)
control.update(model, this)
}
}


1 comment:

  1. The full source in a zip would be really helpful!

    ReplyDelete