Skip to content

Instantly share code, notes, and snippets.

@supki
Created November 2, 2012 14:29
Show Gist options
  • Save supki/4001694 to your computer and use it in GitHub Desktop.
Save supki/4001694 to your computer and use it in GitHub Desktop.
Functional programming course at Coursera week 7.
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)
}
}
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
}
}
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)
}
@valtih1978
Copy link

Isn't it better to use temporary variables, indexOf and avoid cases/conditions, like

  def terrainFunction(level: Vector[Vector[Char]]): Pos => Boolean = (p: Pos) => {
    val x = p.x ; val y = p.y
    !(x < 0 || y < 0) && (x < level.length && y < level(x).length) && (level(x)(y) != '-')
  } 


  def findChar(c: Char, level: Vector[Vector[Char]]): Pos = {
    val x = level.indexWhere { row: Vector[Char] => row.indexOf(c) != -1 }
    Pos(x, level(x).indexOf('c'))
  }

@valtih1978
Copy link

You can also decompose arguments with implicit case match

b.legalNeighbors.map {case (s,t) => (s, t :: history)}.toStream

@valtih1978
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment