Created
September 16, 2015 18:48
-
-
Save peteyoung/da38eac46f6a9318c4fa to your computer and use it in GitHub Desktop.
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._ | |
/** | |
* This component implements the solver for the Bloxorz game | |
*/ | |
trait Solver extends GameDef { | |
/** | |
* Returns `true` if the block `b` is at the final position | |
*/ | |
def done(b: Block): Boolean = b.b1 == goal && b.b2 == goal | |
/** | |
* This function takes two arguments: the current block `b` and | |
* a list of moves `history` that was required to reach the | |
* position of `b`. | |
* | |
* The `head` element of the `history` list is the latest move | |
* that was executed, i.e. the last move that was performed for | |
* the block to end up at position `b`. | |
* | |
* The function returns a stream of pairs: the first element of | |
* the each pair is a neighboring block, and the second element | |
* is the augmented history of moves required to reach this block. | |
* | |
* 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])] = | |
b.legalNeighbors.map { case (b2, m) => (b2, m :: 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])] = | |
neighbors.filter { case (b, h) => !explored.contains(b) }.toStream | |
/** | |
* The function `from` returns the stream of all possible paths | |
* that can be followed, starting at the `head` of the `initial` | |
* stream. | |
* | |
* The blocks in the stream `initial` are sorted by ascending path | |
* length: the block positions with the shortest paths (length of | |
* move list) are at the head of the stream. | |
* | |
* The parameter `explored` is a set of block positions that have | |
* been visited before, on the path to any of the blocks in the | |
* stream `initial`. When search reaches a block that has already | |
* been explored before, that position should not be included a | |
* second time to avoid circles. | |
* | |
* The resulting stream should be sorted by ascending path length, | |
* i.e. the block positions that can be reached with the fewest | |
* amount of moves should appear first in the stream. | |
* | |
* Note: the solution should not look at or compare the lengths | |
* of different paths - the implementation should naturally | |
* construct the correctly sorted stream. | |
*/ | |
def from(initial: Stream[(Block, List[Move])], | |
explored: Set[Block]): Stream[(Block, List[Move])] = { | |
if (initial.isEmpty) | |
Stream.empty | |
else { | |
val (block, history) = initial.head | |
val nwh = neighborsWithHistory(block, history) | |
val nno = newNeighborsOnly(nwh, explored) | |
val newInitial = initial.tail union nno | |
val newBlocks = nno.map(ns => ns._1).toSet | |
//val newExplored = explored union newBlocks | |
val newExplored = explored + block | |
initial.head #:: from(newInitial, newExplored) | |
} | |
} | |
/* | |
def from(openFifo, closedSet) | |
pop the head from the openFifo | |
generate (new) neighbors of the popped head | |
push neighbors to end of openFifo | |
add neighbors to closedSet | |
return head #:: from(updatedOpenFifo, updatedClosedSet) | |
*/ | |
/* | |
def from(paths: Set[Path], explored: Set[State]): Stream[Set[Path]] = | |
if (paths.isEmpty) | |
Stream.empty | |
else { | |
val more = for { | |
path <- paths | |
next <- moves map path.extend | |
if !(explored contains next.endState) | |
} yield next | |
paths #:: from(more, explored ++ (more map (_.endState))) | |
} | |
*/ | |
/** | |
* The stream of all paths that begin at the starting block. | |
*/ | |
lazy val pathsFromStart: Stream[(Block, List[Move])] = | |
from(Stream((startBlock, List[Move]())), Set()) | |
/** | |
* 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])] = | |
pathsFromStart.filter(x => done(x._1)) | |
/** | |
* The (or one of the) shortest sequence(s) of moves to reach the | |
* goal. If the goal cannot be reached, the empty list is returned. | |
* | |
* Note: the `head` element of the returned list should represent | |
* the first move that the player should perform from the starting | |
* position. | |
*/ | |
lazy val solution: List[Move] = | |
if (pathsToGoal == Stream.Empty) | |
List[Move]() | |
else | |
pathsToGoal.head._2.reverse | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment