Created
November 2, 2012 14:29
-
-
Save supki/4001694 to your computer and use it in GitHub Desktop.
Functional programming course at Coursera week 7.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package streams | |
import common._ | |
trait GameDef { | |
case class Pos(x: Int, y: Int) { | |
def dx(d: Int) = copy(x = x + d, y) | |
def dy(d: Int) = copy(x, y = y + d) | |
} | |
val startPos: Pos | |
val goal: Pos | |
type Terrain = Pos => Boolean | |
val terrain: Terrain | |
sealed abstract class Move | |
case object Left extends Move | |
case object Right extends Move | |
case object Up extends Move | |
case object Down extends Move | |
def startBlock: Block = Block(startPos, startPos) | |
case class Block(b1: Pos, b2: Pos) { | |
require(b1.x <= b2.x && b1.y <= b2.y, "Invalid block position: b1=" + b1 + ", b2=" + b2) | |
def dx(d1: Int, d2: Int) = Block(b1.dx(d1), b2.dx(d2)) | |
def dy(d1: Int, d2: Int) = Block(b1.dy(d1), b2.dy(d2)) | |
def left = if (isStanding) dy(-2, -1) | |
else if (b1.x == b2.x) dy(-1, -2) | |
else dy(-1, -1) | |
def right = if (isStanding) dy(1, 2) | |
else if (b1.x == b2.x) dy(2, 1) | |
else dy(1, 1) | |
def up = if (isStanding) dx(-2, -1) | |
else if (b1.x == b2.x) dx(-1, -1) | |
else dx(-1, -2) | |
def down = if (isStanding) dx(1, 2) | |
else if (b1.x == b2.x) dx(1, 1) | |
else dx(2, 1) | |
def neighbors: List[(Block, Move)] = List((left, Left), (right, Right), (up, Up), (down, Down)) | |
def legalNeighbors: List[(Block, Move)] = neighbors.filter(_._1.isLegal) | |
def isStanding: Boolean = b1 == b2 | |
def isLegal: Boolean = terrain(b1) && terrain(b2) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package streams | |
import common._ | |
trait Solver extends GameDef { | |
def done(b: Block): Boolean = b == Block(goal, goal) | |
def neighborsWithHistory(b: Block, history: List[Move]): Stream[(Block, List[Move])] = | |
b.legalNeighbors.map(x => x match {case (s,t) => (s, t :: history)}).toStream | |
def newNeighborsOnly(neighbors: Stream[(Block, List[Move])], | |
explored: Set[Block]): Stream[(Block, List[Move])] = | |
neighbors filterNot (explored contains _._1) | |
def from(initial: Stream[(Block, List[Move])], | |
explored: Set[Block]): Stream[(Block, List[Move])] = initial match { | |
case (b,h) #:: xs => { | |
val ys = newNeighborsOnly(neighborsWithHistory(b,h), explored) | |
(b,h) #:: from(xs ++ ys, explored + b) | |
} | |
case _ => Stream.empty | |
} | |
lazy val pathsFromStart: Stream[(Block, List[Move])] = from(Stream((startBlock, Nil)), Set.empty) | |
lazy val pathsToGoal: Stream[(Block, List[Move])] = pathsFromStart.filter(b => done(b._1)) | |
lazy val solution: List[Move] = pathsToGoal match { | |
case x #:: _ => x._2.reverse | |
case _ => Nil | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package streams | |
import common._ | |
trait StringParserTerrain extends GameDef { | |
val level: String | |
def terrainFunction(levelVector: Vector[Vector[Char]]): Pos => Boolean = | |
p => if (p.x < 0 || p.y < 0) false | |
else if (p.x > levelVector.length - 1) false | |
else if (p.y > levelVector(0).length - 1) false | |
else levelVector(p.x)(p.y) != '-' | |
def findChar(c: Char, levelVector: Vector[Vector[Char]]): Pos = { | |
def x = levelVector.indexWhere(_.indexWhere(_ == c) > -1) | |
def y = levelVector(x).indexWhere(_ == c) | |
Pos(x, y) | |
} | |
private lazy val vector: Vector[Vector[Char]] = | |
Vector(level.split("\n").map(str => Vector(str: _*)): _*) | |
lazy val terrain: Terrain = terrainFunction(vector) | |
lazy val startPos: Pos = findChar('S', vector) | |
lazy val goal: Pos = findChar('T', vector) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can also
initial.head #:: from(xs ++ ys, explored + b)
instead of(b,h) #:: from(xs ++ ys, explored + b)
. This better captures the fact that we leave first element intact.