Skip to content

Instantly share code, notes, and snippets.

@js1972
Created June 17, 2014 02:27
Show Gist options
  • Save js1972/e87794839e1ac894c2ec to your computer and use it in GitHub Desktop.
Save js1972/e87794839e1ac894c2ec to your computer and use it in GitHub Desktop.
Solution to the Water Pouring problem in Scala
package week7
class Pouring(capacity: Vector[Int]) {
// States
type State = Vector[Int]
val initialState = capacity map (x => 0)
// Moves
trait Move {
def change(state: State): State
}
case class Empty(glass: Int) extends Move {
def change(state: State) = state updated (glass, 0)
}
case class Fill(glass: Int) extends Move {
def change(state: State) = state updated (glass, capacity(glass))
}
case class Pour(from: Int, to: Int) extends Move {
def change(state: State) = {
val amount = state(from) min (capacity(to) - state(to))
state updated (from, state(from) - amount) updated (to, state(to) + amount)
}
}
val glasses = 0 until capacity.length
val moves =
(for (g <- glasses) yield Empty(g)) ++
(for (g <- glasses) yield Fill(g)) ++
(for (from <- glasses; to <- glasses if from != to) yield Pour(from, to))
// Paths
class Path(history: List[Move], val endState: State) {
def extend(move: Move) = new Path(move :: history, move change endState)
override def toString = (history.reverse mkString " ") + "--> " + endState
}
val initialPath = new Path(Nil, initialState)
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)))
}
val pathSets = from(Set(initialPath), Set(initialState))
def solutions(target: Int): Stream[Path] =
for {
pathSet <- pathSets
path <- pathSet
if path.endState contains target
} yield path
}
package week7
object test {
/**
* Solution to the Water Pouring Problem.
*
* Create a pouring object specifying the number of glasses (containers)
* and their volumes in a Vector.
* Call the solutions method with the volume you wish to find with
* the result being a Stream of the moves required to get there...
*/
val problem = new Pouring(Vector(4, 9, 19)) //> problem : week7.Pouring = week7.Pouring@6dce272d
problem.moves //> res0: scala.collection.immutable.IndexedSeq[Product with Serializable with w
//| eek7.test.problem.Move] = Vector(Empty(0), Empty(1), Empty(2), Fill(0), Fill
//| (1), Fill(2), Pour(0,1), Pour(0,2), Pour(1,0), Pour(1,2), Pour(2,0), Pour(2,
//| 1))
problem.solutions(17) //> res1: Stream[week7.test.problem.Path] = Stream(Fill(0) Pour(0,2) Fill(1) Fil
//| l(0) Pour(0,2) Pour(1,2)--> Vector(0, 0, 17), ?)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment