Skip to content

Commit

Permalink
Implement 6th assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-myltsev committed Dec 14, 2013
1 parent a97100f commit d57cae0
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 23 deletions.
12 changes: 7 additions & 5 deletions a6-streams/src/main/scala/streams/GameDef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ trait GameDef {
* This function returns the block at the start position of
* the game.
*/
def startBlock: Block = ???
def startBlock: Block = Block(startPos, startPos)

/**
* A block is represented by the position of the two cubes that
Expand Down Expand Up @@ -134,23 +134,25 @@ trait GameDef {
* Returns the list of blocks that can be obtained by moving
* the current block, together with the corresponding move.
*/
def neighbors: List[(Block, Move)] = ???
def neighbors: List[(Block, Move)] =
List((left, Left), (right, Right), (up, Up), (down, Down))

/**
* Returns the list of positions reachable from the current block
* which are inside the terrain.
*/
def legalNeighbors: List[(Block, Move)] = ???
def legalNeighbors: List[(Block, Move)] =
neighbors filter (_._1.isLegal)

/**
* Returns `true` if the block is standing.
*/
def isStanding: Boolean = ???
def isStanding: Boolean = b1 == b2

/**
* Returns `true` if the block is entirely inside the terrain.
*/
def isLegal: Boolean = ???
def isLegal: Boolean = terrain(b1) && terrain(b2)

}
}
42 changes: 35 additions & 7 deletions a6-streams/src/main/scala/streams/Solver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ trait Solver extends GameDef {
/**
* Returns `true` if the block `b` is at the final position
*/
def done(b: Block): Boolean = ???
def done(b: Block): Boolean =
b.isStanding && b.b1 == goal

/**
* This function takes two arguments: the current block `b` and
Expand All @@ -28,15 +29,17 @@ trait Solver extends GameDef {
* It should only return valid neighbors, i.e. block positions
* that are inside the terrain.
*/
def neighborsWithHistory(b: Block, history: List[Move]): Stream[(Block, List[Move])] = ???
def neighborsWithHistory(b: Block, history: List[Move]): Stream[(Block, List[Move])] =
(b.legalNeighbors map (x => (x._1, x._2 :: history))).toStream

/**
* This function returns the list of neighbors without the block
* positions that have already been explored. We will use it to
* make sure that we don't explore circular paths.
*/
def newNeighborsOnly(neighbors: Stream[(Block, List[Move])],
explored: Set[Block]): Stream[(Block, List[Move])] = ???
explored: Set[Block]): Stream[(Block, List[Move])] =
neighbors filter (b => !(explored contains b._1))

/**
* The function `from` returns the stream of all possible paths
Expand All @@ -62,18 +65,41 @@ trait Solver extends GameDef {
* construct the correctly sorted stream.
*/
def from(initial: Stream[(Block, List[Move])],
explored: Set[Block]): Stream[(Block, List[Move])] = ???
explored: Set[Block]): Stream[(Block, List[Move])] = {
if (initial.isEmpty) initial
else {
// Depth-first search
// val (currentBlock, history) = initial.head
// val newExplored = currentBlock.legalNeighbors.map(_._1)
// val newNeighbors = newNeighborsOnly(neighborsWithHistory(currentBlock, history), explored)
// initial #::: from(newNeighbors, explored ++ newExplored)

// Breadth-first search
val more: Stream[(Block, List[Move])] =
for {
(block, path) <- initial
(newBlock, newPath) <- newNeighborsOnly(neighborsWithHistory(block, path), explored)
if !(explored contains newBlock)
} yield (newBlock, newPath)
initial #::: from(more, explored ++ more.unzip._1)
}
}

/**
* The stream of all paths that begin at the starting block.
*/
lazy val pathsFromStart: Stream[(Block, List[Move])] = ???
lazy val pathsFromStart: Stream[(Block, List[Move])] = {
val initialStream = Stream((startBlock, List()))
val initialExplored: Set[Solver.this.type#Block] = Set(startBlock)
from(initialStream, initialExplored)
}

/**
* Returns a stream of all possible pairs of the goal block along
* with the history how it was reached.
*/
lazy val pathsToGoal: Stream[(Block, List[Move])] = ???
lazy val pathsToGoal: Stream[(Block, List[Move])] =
pathsFromStart filter (x => done(x._1))

/**
* The (or one of the) shortest sequence(s) of moves to reach the
Expand All @@ -83,5 +109,7 @@ trait Solver extends GameDef {
* the first move that the player should perform from the starting
* position.
*/
lazy val solution: List[Move] = ???
lazy val solution: List[Move] =
if (pathsToGoal.isEmpty) Nil
else pathsToGoal.head._2
}
12 changes: 10 additions & 2 deletions a6-streams/src/main/scala/streams/StringParserTerrain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ trait StringParserTerrain extends GameDef {
* a valid position (not a '-' character) inside the terrain described
* by `levelVector`.
*/
def terrainFunction(levelVector: Vector[Vector[Char]]): Pos => Boolean = ???
def terrainFunction(levelVector: Vector[Vector[Char]]): Pos => Boolean = { case pos =>
pos.y >= 0 && pos.x >= 0 &&
pos.x < levelVector.size && pos.y < levelVector(pos.x).size &&
levelVector(pos.x)(pos.y) != '-'
}

/**
* This function should return the position of character `c` in the
Expand All @@ -62,7 +66,11 @@ trait StringParserTerrain extends GameDef {
* Hint: you can use the functions `indexWhere` and / or `indexOf` of the
* `Vector` class
*/
def findChar(c: Char, levelVector: Vector[Vector[Char]]): Pos = ???
def findChar(c: Char, levelVector: Vector[Vector[Char]]): Pos = {
val x = levelVector indexWhere (_.contains(c))
val y = levelVector(x).indexOf(c)
Pos(x, y)
}

private lazy val vector: Vector[Vector[Char]] =
Vector(level.split("\n").map(str => Vector(str: _*)): _*)
Expand Down
69 changes: 60 additions & 9 deletions a6-streams/src/test/scala/streams/BloxorzSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ class BloxorzSuite extends FunSuite {
* is a valid solution, i.e. leads to the goal.
*/
def solve(ls: List[Move]): Block =
ls.foldLeft(startBlock) { case (block, move) => move match {
case Left => block.left
case Right => block.right
case Up => block.up
case Down => block.down
ls.foldLeft(startBlock) {
case (block, move) => move match {
case Left => block.left
case Right => block.right
case Up => block.up
case Down => block.down
}
}
}
}

trait Level1 extends SolutionChecker {
Expand All @@ -42,14 +43,53 @@ class BloxorzSuite extends FunSuite {

test("terrain function level 1") {
new Level1 {
assert(terrain(Pos(0,0)), "0,0")
assert(!terrain(Pos(4,11)), "4,11")
assert(terrain(Pos(0, 0)), "0,0")
assert(!terrain(Pos(4, 11)), "4,11")
}
}

test("findChar level 1") {
new Level1 {
assert(startPos == Pos(1,1))
assert(startPos == Pos(1, 1))
}
}

test("neighborsWithHistory") {
new Level1 {
val actual = neighborsWithHistory(Block(Pos(1, 1), Pos(1, 1)), List(Left, Up)).toSet
val expected = Set(
(Block(Pos(1, 2), Pos(1, 3)), List(Right, Left, Up)),
(Block(Pos(2, 1), Pos(3, 1)), List(Down, Left, Up)))
assert(actual == expected)
}
}

test("newNeighborsOnly") {
new Level1 {
val actual: Stream[(Block, List[Move])] = newNeighborsOnly(
Set(
(Block(Pos(1, 2), Pos(1, 3)), List(Right, Left, Up)),
(Block(Pos(2, 1), Pos(3, 1)), List(Down, Left, Up))).toStream,
Set(Block(Pos(1, 2), Pos(1, 3)), Block(Pos(1, 1), Pos(1, 1))))

val expected: Set[(Block, List[Move])] = Set(
(Block(Pos(2, 1), Pos(3, 1)), List(Down, Left, Up)))

assert(actual.toSet == expected)
}
}

test("`pathsFromStart` are legal") {
new Level1 {
assert(pathsFromStart.take(30).toList.forall(_._1.isLegal))
}
}

test("`pathsFromStart` path lengths are increasing") {
new Level1 {
val lengthStream: Stream[Int] = pathsFromStart map (x => x._2.length)
val ascendingPairs: Stream[(Int, Int)] = lengthStream zip lengthStream.drop(1)
assert(ascendingPairs.take(30).toList.forall(x => x._1 <= x._2))
}
}

Expand All @@ -64,4 +104,15 @@ class BloxorzSuite extends FunSuite {
assert(solution.length == optsolution.length)
}
}

test("no solution") {
new SolutionChecker {
val level =
"""----------
|oSoTooo---
|----------""".stripMargin

assert(solution.isEmpty)
}
}
}

0 comments on commit d57cae0

Please sign in to comment.