diff --git a/src/main/scala/leon/synthesis/PartialSolution.scala b/src/main/scala/leon/synthesis/PartialSolution.scala
index d3443db76c73e403ca287f754a07a0c6c0f060f1..8de57e6e43d7b7610627fb5386b436967ddb2cd8 100644
--- a/src/main/scala/leon/synthesis/PartialSolution.scala
+++ b/src/main/scala/leon/synthesis/PartialSolution.scala
@@ -7,7 +7,9 @@ import purescala.Expressions._
 
 import graph._
 
-class PartialSolution(g: Graph, includeUntrusted: Boolean = false) {
+class PartialSolution(search: Search, includeUntrusted: Boolean = false) {
+  val g = search.g
+  val strat = search.strat
 
   def includeSolution(s: Solution) = {
     includeUntrusted || s.isTrusted
@@ -66,11 +68,9 @@ class PartialSolution(g: Graph, includeUntrusted: Boolean = false) {
         }
 
         if (n.isExpanded) {
-          val descs = on.descendants.filterNot(_.isDeadEnd)
-          if (descs.isEmpty) {
-            completeProblem(on.p)
-          } else {
-            getSolutionFor(descs.minBy(_.cost))
+          strat.bestAlternative(on) match {
+            case None => completeProblem(on.p)
+            case Some(d) => getSolutionFor(d)
           }
         } else {
           completeProblem(on.p)
diff --git a/src/main/scala/leon/synthesis/Search.scala b/src/main/scala/leon/synthesis/Search.scala
new file mode 100644
index 0000000000000000000000000000000000000000..84655917890f948e4fa1b05a93fd5df046b31462
--- /dev/null
+++ b/src/main/scala/leon/synthesis/Search.scala
@@ -0,0 +1,96 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+
+import strategies._
+import graph._
+
+import scala.annotation.tailrec
+
+import scala.collection.mutable.ArrayBuffer
+import leon.utils.Interruptible
+import java.util.concurrent.atomic.AtomicBoolean
+
+class Search(val ctx: LeonContext, ci: SourceInfo, p: Problem, val strat: Strategy) extends Interruptible {
+  val g = new Graph(p)
+
+  val interrupted = new AtomicBoolean(false)
+
+  strat.init(g.root)
+
+  def doExpand(n: Node, sctx: SynthesisContext): Unit = {
+    ctx.timers.synthesis.step.timed {
+      n match {
+        case an: AndNode =>
+          ctx.timers.synthesis.applications.get(an.ri.asString(sctx.context)).timed {
+            an.expand(SearchContext(sctx, ci, an, this))
+          }
+
+        case on: OrNode =>
+          on.expand(SearchContext(sctx, ci, on, this))
+      }
+    }
+  }
+
+  @tailrec
+  final def searchFrom(sctx: SynthesisContext, from: Node): Boolean = {
+    strat.getNextToExpand(from) match {
+      case Some(n) =>
+        strat.beforeExpand(n)
+
+        doExpand(n, sctx)
+
+        strat.afterExpand(n)
+
+        if (from.isSolved) {
+          true
+        } else if (interrupted.get) {
+          false
+        } else {
+          searchFrom(sctx, from)
+        }
+      case None =>
+        false
+    }
+  }
+
+  def traversePathFrom(n: Node, path: List[Int]): Option[Node] = path match {
+    case Nil =>
+      Some(n)
+    case x :: xs =>
+      if (n.isExpanded && n.descendants.size > x) {
+        traversePathFrom(n.descendants(x), xs)
+      } else {
+        None
+      }
+  }
+
+  def traversePath(path: List[Int]): Option[Node] = {
+    traversePathFrom(g.root, path)
+  }
+
+  def search(sctx: SynthesisContext): Stream[Solution] = {
+    if (searchFrom(sctx, g.root)) {
+      g.root.generateSolutions()
+    } else {
+      Stream.empty
+    }
+  }
+
+  def interrupt(): Unit = {
+    interrupted.set(true)
+    strat.interrupt()
+  }
+
+  def recoverInterrupt(): Unit = {
+    interrupted.set(false)
+    strat.recoverInterrupt()
+  }
+
+  ctx.interruptManager.registerForInterrupts(this)
+}
+
+/*
+
+*/
diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala
index efd1ad13e0538f855487e94b7b1a35d7d893627f..011d390e124eaef4ce54df0a69de7332aaf2ce3b 100644
--- a/src/main/scala/leon/synthesis/Synthesizer.scala
+++ b/src/main/scala/leon/synthesis/Synthesizer.scala
@@ -13,6 +13,7 @@ import leon.utils._
 import scala.concurrent.duration._
 
 import synthesis.graph._
+import synthesis.strategies._
 
 class Synthesizer(val context : LeonContext,
                   val program: Program,
@@ -28,11 +29,22 @@ class Synthesizer(val context : LeonContext,
   implicit val debugSection = leon.utils.DebugSectionSynthesis
 
   def getSearch: Search = {
-    if (settings.manualSearch.isDefined) {
-      new ManualSearch(context, ci, problem, settings.costModel, settings.manualSearch)
+    val strat0 = new CostBasedStrategy(context, settings.costModel)
+
+    val strat1 = if (settings.manualSearch.isDefined) {
+      new ManualStrategy(context, settings.manualSearch, strat0)
     } else {
-      new SimpleSearch(context, ci, problem, settings.costModel, settings.searchBound)
+      strat0
+    }
+
+    val strat2 = settings.searchBound match {
+      case Some(b) =>
+        BoundedStrategy(strat1, b)
+      case None =>
+        strat1
     }
+
+    new Search(context, ci, problem, strat1)
   }
 
   private var lastTime: Long = 0
@@ -104,7 +116,7 @@ class Synthesizer(val context : LeonContext,
     }(DebugSectionReport)
 
     (s, if (result.isEmpty && allowPartial) {
-      List((new PartialSolution(s.g, true).getSolution, false)).toStream
+      List((new PartialSolution(s, true).getSolution, false)).toStream
     } else {
       result
     })
@@ -134,7 +146,7 @@ class Synthesizer(val context : LeonContext,
         reporter.warning("Solution was invalid:")
         reporter.warning(fds.map(ScalaPrinter(_)).mkString("\n\n"))
         reporter.warning(vcreport.summaryString)
-        (new PartialSolution(search.g, false).getSolution, false)
+        (new PartialSolution(search, false).getSolution, false)
       }
     } finally {
       solverf.shutdown()
diff --git a/src/main/scala/leon/synthesis/graph/DotGenerator.scala b/src/main/scala/leon/synthesis/graph/DotGenerator.scala
index 78ef7b371487a6711d3508b9712f7806e9c551e0..f6ccf140c68e07daf9b7b5bd86aa1df443bd78b2 100644
--- a/src/main/scala/leon/synthesis/graph/DotGenerator.scala
+++ b/src/main/scala/leon/synthesis/graph/DotGenerator.scala
@@ -1,16 +1,17 @@
 /* Copyright 2009-2015 EPFL, Lausanne */
 
-package leon.synthesis.graph
+package leon.synthesis
+package graph
 
 import leon.utils.UniqueCounter
 
 import java.io.{File, FileWriter, BufferedWriter}
 
 class DotGenerator(search: Search) {
-
   implicit val ctx = search.ctx
 
   val g = search.g
+  val strat = search.strat
 
   private val idCounter = new UniqueCounter[Unit]
   idCounter.nextGlobal // Start with 1
@@ -35,7 +36,7 @@ class DotGenerator(search: Search) {
 
     // Print all nodes
     val edges = collectEdges(g.root)
-    val nodes = edges.flatMap(e => Set(e._1, e._2))
+    val nodes = edges.flatMap(e => Set(e._1, e._3))
 
     var nodesToNames = Map[Node, String]()
 
@@ -52,12 +53,12 @@ class DotGenerator(search: Search) {
       nodesToNames += n -> name
     }
 
-    for ((f,t) <- edges) {
+    for ((f,i,t) <- edges) {
       val label = f match {
         case ot: OrNode =>
           "or"
         case at: AndNode =>
-          ""
+          i.toString
       }
 
       val style = if (f.selected contains t) {
@@ -74,7 +75,7 @@ class DotGenerator(search: Search) {
     res.toString
   }
 
-  def limit(o: Any, length: Int = 40): String = {
+  def limit(o: Any, length: Int = 200): String = {
     val str = o.toString
     if (str.length > length) {
       str.substring(0, length-3)+"..."
@@ -107,13 +108,7 @@ class DotGenerator(search: Search) {
 
     res append " "+name+" [ label = <<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">"
     
-    //cost
-    n match {
-      case an: AndNode =>
-        res append "<TR><TD BORDER=\"0\">"+escapeHTML(n.cost.asString)+" ("+escapeHTML(g.cm.andNode(an, None).asString)+")</TD></TR>"
-      case on: OrNode =>
-        res append "<TR><TD BORDER=\"0\">"+escapeHTML(n.cost.asString)+"</TD></TR>"
-    }
+    res append "<TR><TD BORDER=\"0\">"+escapeHTML(strat.debugInfoFor(n))+"</TD></TR>"
 
     res append "<TR><TD BORDER=\"1\" BGCOLOR=\""+color+"\">"+escapeHTML(limit(index + nodeDesc(n)))+"</TD></TR>"
 
@@ -125,9 +120,9 @@ class DotGenerator(search: Search) {
 
   }
 
-  private def collectEdges(from: Node): Set[(Node, Node)] = {
-    from.descendants.flatMap { d =>
-      Set(from -> d) ++ collectEdges(d)
+  private def collectEdges(from: Node): Set[(Node, Int, Node)] = {
+    from.descendants.zipWithIndex.flatMap { case (d, i) =>
+      Set((from, i, d)) ++ collectEdges(d)
     }.toSet
   }
 }
diff --git a/src/main/scala/leon/synthesis/graph/Graph.scala b/src/main/scala/leon/synthesis/graph/Graph.scala
index 154eba291d63310b0f89875211a731cf6f75865d..d967c60735679641d901685b6b3e565224b0f81c 100644
--- a/src/main/scala/leon/synthesis/graph/Graph.scala
+++ b/src/main/scala/leon/synthesis/graph/Graph.scala
@@ -7,8 +7,8 @@ package graph
 import leon.utils.StreamUtils.cartesianProduct
 import leon.utils.DebugSectionSynthesis
 
-sealed class Graph(val cm: CostModel, problem: Problem) {
-  val root = new RootNode(cm, problem)
+sealed class Graph(problem: Problem) {
+  val root = new RootNode(problem)
 
   // Returns closed/total
   def getStats(from: Node = root): (Int, Int) = {
@@ -27,13 +27,14 @@ sealed class Graph(val cm: CostModel, problem: Problem) {
   }
 }
 
-sealed abstract class Node(cm: CostModel, val parent: Option[Node]) {
+sealed abstract class Node(val parent: Option[Node]) {
 
   def asString(implicit ctx: LeonContext): String
 
   var descendants: List[Node] = Nil
   // indicates whether this particular node has already been expanded
   var isExpanded: Boolean = false
+
   def expand(hctx: SearchContext)
 
   val p: Problem
@@ -45,12 +46,9 @@ sealed abstract class Node(cm: CostModel, val parent: Option[Node]) {
   var solutions: Option[Stream[Solution]] = None
   var selectedSolution = -1
 
-  // Costs
-  var cost: Cost = computeCost()
+  var isDeadEnd: Boolean = false
 
-  def isDeadEnd: Boolean = {
-    cm.isImpossible(cost)
-  }
+  def isOpen = !isDeadEnd && !isSolved
 
   // For non-terminals, selected children for solution
   var selected: List[Node] = Nil
@@ -63,38 +61,6 @@ sealed abstract class Node(cm: CostModel, val parent: Option[Node]) {
       composeSolutions(selected.map(n => n.generateSolutions()))
     }
   }
-
-  def computeCost(): Cost = solutions match {
-    case Some(sols) if sols.isEmpty =>
-      cm.impossible
-
-    case Some(sols) =>
-      if (sols.hasDefiniteSize) {
-        sols.map { sol => cm.solution(sol) } .min
-      } else {
-        cm.solution(sols.head)
-      }
-
-    case None =>
-      val costs = if (isExpanded) {
-        Some(descendants.map { _.cost })
-      } else {
-        None
-      }
-
-      this match {
-        case an: AndNode =>
-          cm.andNode(an, costs)
-
-        case on: OrNode =>
-          costs.map(_.min).getOrElse(cm.problem(on.p))
-      }
-  }
-
-  def updateCost(): Unit = {
-    cost = computeCost()
-    parent.foreach(_.updateCost())
-  }
 }
 
 /** Represents the conjunction of search nodes.
@@ -102,7 +68,7 @@ sealed abstract class Node(cm: CostModel, val parent: Option[Node]) {
   * @param parent Some node. None if it is the root node.
   * @param ri The rule instantiation that created this AndNode.
   **/
-class AndNode(cm: CostModel, parent: Option[Node], val ri: RuleInstantiation) extends Node(cm, parent) {
+class AndNode(parent: Option[Node], val ri: RuleInstantiation) extends Node(parent) {
   val p = ri.problem
 
   override def asString(implicit ctx: LeonContext) = "\u2227 "+ri.asString
@@ -124,19 +90,17 @@ class AndNode(cm: CostModel, parent: Option[Node], val ri: RuleInstantiation) ex
         solutions = Some(sols)
         selectedSolution = 0
 
-        updateCost()
-
         isSolved = sols.nonEmpty
 
         if (sols.isEmpty) {
           info(prefix+"Failed")
+          isDeadEnd = true
         } else {
           val sol = sols.head
           info(prefix+"Solved"+(if(sol.isTrusted) "" else " (untrusted)")+" with: "+sol.asString+"...")
         }
 
         parent.foreach{ p =>
-          p.updateCost()
           if (isSolved) {
             p.onSolved(this)
           }
@@ -148,11 +112,13 @@ class AndNode(cm: CostModel, parent: Option[Node], val ri: RuleInstantiation) ex
           info(prefix+"     - "+p.asString)
         }
 
-        descendants = probs.map(p => new OrNode(cm, Some(this), p))
+        descendants = probs.map(p => new OrNode(Some(this), p))
 
-        selected = descendants
+        if (descendants.isEmpty) {
+          isDeadEnd = true
+        }
 
-        updateCost()
+        selected = descendants
     }
   }
 
@@ -177,7 +143,7 @@ class AndNode(cm: CostModel, parent: Option[Node], val ri: RuleInstantiation) ex
 
 }
 
-class OrNode(cm: CostModel, parent: Option[Node], val p: Problem) extends Node(cm, parent) {
+class OrNode(parent: Option[Node], val p: Problem) extends Node(parent) {
 
   override def asString(implicit ctx: LeonContext) = "\u2228 "+p.asString
 
@@ -203,6 +169,7 @@ class OrNode(cm: CostModel, parent: Option[Node], val p: Problem) extends Node(c
         return if (prio == RulePriorityNormalizing) results.take(1) else results
       }
     }
+
     Nil
   }
 
@@ -211,11 +178,9 @@ class OrNode(cm: CostModel, parent: Option[Node], val p: Problem) extends Node(c
 
     val ris = getInstantiations(hctx)
 
-    descendants = ris.map(ri => new AndNode(cm, Some(this), ri))
+    descendants = ris.map(ri => new AndNode(Some(this), ri))
     selected = List()
 
-    updateCost()
-
     isExpanded = true
   }
 
@@ -230,4 +195,4 @@ class OrNode(cm: CostModel, parent: Option[Node], val p: Problem) extends Node(c
   }
 }
 
-class RootNode(cm: CostModel, p: Problem) extends OrNode(cm, None, p)
+class RootNode(p: Problem) extends OrNode(None, p)
diff --git a/src/main/scala/leon/synthesis/graph/Search.scala b/src/main/scala/leon/synthesis/graph/Search.scala
deleted file mode 100644
index c630e315d9777110b5dcde7adc42cf6172161af3..0000000000000000000000000000000000000000
--- a/src/main/scala/leon/synthesis/graph/Search.scala
+++ /dev/null
@@ -1,323 +0,0 @@
-/* Copyright 2009-2015 EPFL, Lausanne */
-
-package leon
-package synthesis
-package graph
-
-import scala.annotation.tailrec
-
-import scala.collection.mutable.ArrayBuffer
-import leon.utils.Interruptible
-import java.util.concurrent.atomic.AtomicBoolean
-
-abstract class Search(val ctx: LeonContext, ci: SourceInfo, p: Problem, costModel: CostModel) extends Interruptible {
-  val g = new Graph(costModel, p)
-
-  def findNodeToExpandFrom(n: Node): Option[Node]
-
-  val interrupted = new AtomicBoolean(false)
-
-  def doStep(n: Node, sctx: SynthesisContext): Unit = {
-    ctx.timers.synthesis.step.timed {
-      n match {
-        case an: AndNode =>
-          ctx.timers.synthesis.applications.get(an.ri.asString(sctx.context)).timed {
-            an.expand(SearchContext(sctx, ci, an, this))
-          }
-
-        case on: OrNode =>
-          on.expand(SearchContext(sctx, ci, on, this))
-      }
-    }
-  }
-
-  @tailrec
-  final def searchFrom(sctx: SynthesisContext, from: Node): Boolean = {
-    findNodeToExpandFrom(from) match {
-      case Some(n) =>
-        doStep(n, sctx)
-
-        if (from.isSolved) {
-          true
-        } else if (interrupted.get) {
-          false
-        } else {
-          searchFrom(sctx, from)
-        }
-      case None =>
-        false
-    }
-  }
-
-  def traversePathFrom(n: Node, path: List[Int]): Option[Node] = path match {
-    case Nil =>
-      Some(n)
-    case x :: xs =>
-      if (n.isExpanded && n.descendants.size > x) {
-        traversePathFrom(n.descendants(x), xs)
-      } else {
-        None
-      }
-  }
-
-  def traversePath(path: List[Int]): Option[Node] = {
-    traversePathFrom(g.root, path)
-  }
-
-  def search(sctx: SynthesisContext): Stream[Solution] = {
-    if (searchFrom(sctx, g.root)) {
-      g.root.generateSolutions()
-    } else {
-      Stream.empty
-    }
-  }
-
-  def interrupt(): Unit = {
-    interrupted.set(true)
-  }
-
-  def recoverInterrupt(): Unit = {
-    interrupted.set(false)
-  }
-
-  ctx.interruptManager.registerForInterrupts(this)
-}
-
-class SimpleSearch(ctx: LeonContext, ci: SourceInfo, p: Problem, costModel: CostModel, bound: Option[Int]) extends Search(ctx, ci, p, costModel) {
-  val expansionBuffer = ArrayBuffer[Node]()
-
-  def findIn(n: Node) {
-    if (!n.isExpanded) {
-      expansionBuffer += n
-    } else if (!n.isDeadEnd) {
-      n match {
-        case an: AndNode =>
-          an.descendants.foreach(findIn)
-
-        case on: OrNode =>
-          if (on.descendants.nonEmpty) {
-            findIn(on.descendants.minBy(_.cost))
-          }
-      }
-    }
-  }
-
-  var counter = 0
-
-  def findNodeToExpandFrom(from: Node): Option[Node] = {
-    counter += 1
-    ctx.timers.synthesis.search.find.timed {
-      if (bound.isEmpty || counter <= bound.get) {
-        if (expansionBuffer.isEmpty) {
-          findIn(from)
-        }
-
-        if (expansionBuffer.nonEmpty) {
-          Some(expansionBuffer.remove(0))
-        } else {
-          None
-        }
-      } else {
-        None
-      }
-    }
-  }
-}
-
-class ManualSearch(ctx: LeonContext, ci: SourceInfo, problem: Problem, costModel: CostModel, initCmd: Option[String]) extends Search(ctx, ci, problem, costModel) {
-  import ctx.reporter._
-
-  abstract class Command
-  case class Cd(path: List[Int]) extends Command
-  case object Parent extends Command
-  case object Quit extends Command
-  case object Noop extends Command
-
-  // Manual search state:
-  var cd       = List[Int]()
-  var cmdQueue = initCmd.map( str => parseCommands(parseString(str))).getOrElse(Nil)
-
-  def getNextCommand(): Command = cmdQueue match {
-    case c :: cs =>
-      cmdQueue = cs
-      c
-
-    case Nil =>
-      print("Next action? (q to quit) "+cd.mkString(" ")+" $ ")
-      val line = scala.io.StdIn.readLine()
-      val parts = parseString(line)
-
-      cmdQueue = parseCommands(parts)
-      getNextCommand()
-  }
-
-  def parseString(s: String): List[String] = {
-    s.trim.split("\\s+|,").toList
-  }
-
-  def parseCommands(tokens: List[String]): List[Command] = tokens match {
-    case "cd" :: ".." :: ts =>
-      Parent :: parseCommands(ts)
-
-    case "cd" :: ts =>
-      val path = ts.takeWhile { t => t.forall(_.isDigit) }
-
-      if (path.isEmpty) {
-        parseCommands(ts)
-      } else {
-        Cd(path.map(_.toInt)) :: parseCommands(ts.drop(path.size))
-      }
-
-    case "q" :: ts =>
-      Quit :: Nil
-
-    case Nil  | "" :: Nil =>
-      Nil
-
-    case ts =>
-      val path = ts.takeWhile { t => t.forall(_.isDigit) }
-
-      if (path.isEmpty) {
-        error("Unknown command "+ts.head)
-        parseCommands(ts.tail)
-      } else {
-        Cd(path.map(_.toInt)) :: parseCommands(ts.drop(path.size))
-      }
-  }
-
-  override def doStep(n: Node, sctx: SynthesisContext) = {
-    super.doStep(n, sctx)
-
-    // Backtrack view to a point where node is neither closed nor solved
-    if (n.isDeadEnd || n.isSolved) {
-      var from: Node = g.root
-      var newCd = List[Int]()
-
-      while (!from.isSolved && !from.isDeadEnd && newCd.size < cd.size) {
-        val cdElem = cd(newCd.size)
-        from = traversePathFrom(from, List(cdElem)).get
-        if (!from.isSolved && !from.isDeadEnd) {
-          newCd = cdElem :: newCd
-        }
-      }
-
-      cd = newCd.reverse
-    }
-  }
-
-
-  def printGraph() {
-    def title(str: String)    = "\u001b[1m"  + str + "\u001b[0m"
-    def failed(str: String)   = "\u001b[31m" + str + "\u001b[0m"
-    def solved(str: String)   = "\u001b[32m" + str + "\u001b[0m"
-    def expanded(str: String) = "\u001b[33m" + str + "\u001b[0m"
-
-    def displayNode(n: Node): String = n match {
-      case an: AndNode =>
-        val app = an.ri.asString(ctx)
-        s"(${n.cost.asString}) ${indent(app)}"
-      case on: OrNode =>
-        val p = on.p.asString(ctx)
-        s"(${n.cost.asString}) ${indent(p)}"
-    }
-
-    def indent(a: String): String = {
-      a.replaceAll("\n", "\n"+(" "*12))
-    }
-
-    def pathToString(cd: List[Int]): String = {
-      cd.map(i => f"$i%2d").mkString(" ")
-    }
-
-    def displayPath(n: Node, cd: List[Int]) {
-      if (cd.isEmpty) {
-        println(title(pathToString(cd)+" \u2510 "+displayNode(n)))
-
-        for ((sn, i) <- n.descendants.zipWithIndex) {
-          val sp = cd ::: List(i)
-
-          if (sn.isSolved) {
-            println(solved(pathToString(sp)+" \u2508 "+displayNode(sn)))
-          } else if (sn.isDeadEnd) {
-            println(failed(pathToString(sp)+" \u2508 "+displayNode(sn)))
-          } else if (sn.isExpanded) {
-            println(expanded(pathToString(sp)+" \u2508 "+displayNode(sn)))
-          } else {
-            println(pathToString(sp)+" \u2508 "+displayNode(sn))
-          }
-        }
-      } else {
-        displayPath(n.descendants(cd.head), cd.tail)
-      }
-    }
-
-    println("-"*120)
-    displayPath(g.root, cd)
-    println("-"*120)
-  }
-
-  var continue = true
-
-  def findNodeToExpandFrom(from: Node): Option[Node] = {
-    if (!from.isExpanded) {
-      Some(from)
-    } else {
-      var res: Option[Option[Node]] = None
-
-      while(res.isEmpty) {
-        printGraph()
-
-        try {
-          getNextCommand() match {
-            case Quit =>
-              continue = false
-              res = Some(None)
-            case Parent =>
-              if (cd.nonEmpty) {
-                cd = cd.dropRight(1)
-              } else {
-                error("Already at root node")
-              }
-
-            case Cd(path) =>
-              var currentNode = from
-              var currentPath = cd ++ path
-              cd = Nil
-              while (currentPath.nonEmpty && currentNode.isExpanded && res.isEmpty) {
-                traversePathFrom(currentNode, List(currentPath.head)) match {
-                  case Some(n) =>
-                    cd = cd ::: List(currentPath.head)
-                    currentNode = n
-                    currentPath = currentPath.tail
-
-                  case None =>
-                    warning("Unknown path: "+ (path mkString "/"))
-                    //res = Some(None)
-                    return findNodeToExpandFrom(from) 
-                }
-              }
-
-              if (currentPath.nonEmpty) {
-                cmdQueue = Cd(currentPath) :: cmdQueue
-              }
-
-              if (!currentNode.isExpanded) {
-                res = Some(Some(currentNode))
-              }
-          }
-        } catch {
-          case e: java.lang.NumberFormatException =>
-
-          case e: java.io.IOException =>
-            continue = false
-
-          case e: Throwable =>
-            error("Woops: "+e.getMessage)
-            e.printStackTrace()
-        }
-      }
-
-      res.get
-    }
-  }
-}
diff --git a/src/main/scala/leon/synthesis/rules/CEGISLike.scala b/src/main/scala/leon/synthesis/rules/CEGISLike.scala
index 2ec9fa496351eb770aa2e314086da1bc308748ae..72f3cffb3cb590dacc70cf1165c2c9e5bca36955 100644
--- a/src/main/scala/leon/synthesis/rules/CEGISLike.scala
+++ b/src/main/scala/leon/synthesis/rules/CEGISLike.scala
@@ -327,7 +327,7 @@ abstract class CEGISLike[T <: Typed](name: String) extends Rule(name) {
       private val (innerProgram, origFdMap) = {
 
         val outerSolution = {
-          new PartialSolution(hctx.search.g, true)
+          new PartialSolution(hctx.search, true)
             .solutionAround(hctx.currentNode)(FunctionInvocation(solFd.typed, p.as.map(_.toVariable)))
             .getOrElse(ctx.reporter.fatalError("Unable to get outer solution"))
         }
diff --git a/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala b/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
index 81fa4359a2ce5fc0914253289316beef19f93955..0fd46b52de757470b79a1f2b0baf42c29d34f1c4 100644
--- a/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
+++ b/src/main/scala/leon/synthesis/rules/IntroduceRecCall.scala
@@ -54,7 +54,7 @@ case object IntroduceRecCall extends Rule("Introduce rec. calls") {
 
         def apply(nohctx: SearchContext) = {
 
-          val psol = new PartialSolution(hctx.search.g, true)
+          val psol = new PartialSolution(hctx.search, true)
             .solutionAround(hctx.currentNode)(Error(p.outType, "Encountered choose!"))
             .getOrElse(hctx.sctx.context.reporter.fatalError("Unable to get outer solution"))
             .term
diff --git a/src/main/scala/leon/synthesis/strategies/BoundedStrategy.scala b/src/main/scala/leon/synthesis/strategies/BoundedStrategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..2c58a5d310ceac9f5445ebbda3c057e4d3946991
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/BoundedStrategy.scala
@@ -0,0 +1,24 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import synthesis.graph._
+
+case class BoundedStrategy(underlying: Strategy, bound: Int) extends WrappedStrategy(underlying) {
+  private[this] var nSteps = 0
+
+  override def getNextToExpand(from: Node): Option[Node] = {
+    if (nSteps < bound) {
+      super.getNextToExpand(from)
+    } else {
+      None
+    }
+  }
+
+  override def afterExpand(n: Node) = {
+    super.afterExpand(n);
+    nSteps += 1
+  }
+}
diff --git a/src/main/scala/leon/synthesis/strategies/CostBasedStrategy.scala b/src/main/scala/leon/synthesis/strategies/CostBasedStrategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..52255b2cd361e045f0bcceed9ac9ff8e12c8e62b
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/CostBasedStrategy.scala
@@ -0,0 +1,96 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import synthesis.graph._
+
+import purescala.Expressions._
+
+class CostBasedStrategy(ctx: LeonContext, cm: CostModel) extends Strategy {
+  var bestSols = Map[Node, Option[Solution]]()
+  var bestCosts = Map[Node, Cost]()
+
+  override def init(root: RootNode): Unit = {
+    super.init(root)
+    computeBestSolutionFor(root)
+  }
+
+  def computeBestSolutionFor(n: Node): Option[Solution] = {
+    val res = if (n.isSolved) {
+      Some(n.generateSolutions().head)
+    } else if (n.isDeadEnd) {
+      None
+    } else if (!n.isExpanded) {
+      n match {
+        case an: AndNode =>
+          an.ri.onSuccess match {
+            case SolutionBuilderCloser(_) =>
+              Some(Solution.simplest(an.p.outType))
+
+            case SolutionBuilderDecomp(types, recomp) =>
+              recomp(types.toList.map(Solution.simplest))
+          }
+        case on: OrNode =>
+          Some(Solution.simplest(n.p.outType))
+      }
+    } else {
+      n match {
+        case an: AndNode =>
+          val subs = an.descendants.map(bestSolutionFor)
+
+          if (subs.forall(_.isDefined)) {
+            an.ri.onSuccess(subs.flatten)
+          } else {
+            None
+          }
+        case on: OrNode =>
+          on.descendants.foreach(bestSolutionFor)
+
+          bestSolutionFor(on.descendants.minBy(bestCosts))
+      }
+    }
+
+    bestSols += n -> res
+    bestCosts += n -> res.map(cm.solution _).getOrElse(cm.impossible)
+
+    res
+  }
+
+  def bestAlternative(on: OrNode): Option[Node] = {
+    if (on.isDeadEnd) {
+      None
+    } else {
+      Some(on.descendants.minBy(bestCosts))
+    }
+  }
+
+  def bestSolutionFor(n: Node): Option[Solution] = {
+    bestSols.get(n) match {
+      case Some(os) => os
+      case None => computeBestSolutionFor(n)
+    }
+  }
+
+  def recomputeCost(n: Node): Unit = {
+    val oldCost = bestCosts(n)
+    computeBestSolutionFor(n)
+
+    if (bestCosts(n) != oldCost) {
+      n.parent.foreach(recomputeCost)
+    }
+  }
+
+  override def afterExpand(n: Node): Unit = {
+    super.afterExpand(n)
+
+    for (d <- n.descendants) {
+      bestSolutionFor(d)
+    }
+
+    recomputeCost(n)
+  }
+
+  def debugInfoFor(n: Node) = bestCosts.get(n).map(_.asString).getOrElse("?")
+}
diff --git a/src/main/scala/leon/synthesis/strategies/ManualStrategy.scala b/src/main/scala/leon/synthesis/strategies/ManualStrategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..68257d6ddc0f38a66c35f83f0bcf3ddd31766686
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/ManualStrategy.scala
@@ -0,0 +1,250 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import graph._
+
+class ManualStrategy(ctx: LeonContext, initCmd: Option[String], strat: Strategy) extends Strategy {
+  implicit val ctx_ = ctx
+
+  import ctx.reporter._
+
+  abstract class Command
+  case class Cd(path: List[Int]) extends Command
+  case object Parent extends Command
+  case object Quit extends Command
+  case object Noop extends Command
+  case object Best extends Command
+
+  // Manual search state:
+  var rootNode: Node    = _
+
+  var path              = List[Int]()
+
+  override def init(n: RootNode) = {
+    super.init(n)
+    strat.init(n)
+
+    rootNode = n
+  }
+
+  def currentNode(path: List[Int]): Node = {
+    def findFrom(n: Node, path: List[Int]): Node = {
+      path match {
+        case Nil => n
+        case p :: ps =>
+          findDescendent(n, p) match {
+            case Some(d) =>
+              findFrom(d, ps)
+            case None =>
+              n
+          }
+      }
+    }
+
+    findFrom(rootNode, path)
+  }
+
+  override def beforeExpand(n: Node) = {
+    super.beforeExpand(n)
+    strat.beforeExpand(n)
+  }
+
+  override def afterExpand(n: Node) = {
+    super.afterExpand(n)
+    strat.afterExpand(n)
+
+    // Backtrack view to a point where node is neither closed nor solved
+    if (n.isDeadEnd || n.isSolved) {
+      val backtrackTo = findAncestor(n, n => !n.isDeadEnd && !n.isSolved)
+
+      path = backtrackTo.map(pathTo).getOrElse(Nil)
+    }
+  }
+
+  private def findAncestor(n: Node, f: Node => Boolean): Option[Node] = {
+    n.parent.flatMap { n =>
+      if (f(n)) Some(n) else findAncestor(n, f)
+    }
+  }
+
+  private def pathTo(n: Node): List[Int] = {
+    n.parent match {
+      case None => Nil
+      case Some(p) => pathTo(p) :+ p.descendants.indexOf(n)
+    }
+  }
+
+
+  def bestAlternative(n: OrNode) = strat.bestAlternative(n)
+
+
+  def printGraph() {
+    def title(str: String)    = "\u001b[1m"  + str + "\u001b[0m"
+    def failed(str: String)   = "\u001b[31m" + str + "\u001b[0m"
+    def solved(str: String)   = "\u001b[32m" + str + "\u001b[0m"
+    def expanded(str: String) = "\u001b[33m" + str + "\u001b[0m"
+
+    def displayNode(n: Node): String = {
+      n match {
+        case an: AndNode =>
+          val app = an.ri.asString(ctx)
+          s"(${debugInfoFor(n)}) ${indent(app)}"
+        case on: OrNode =>
+          val p = on.p.asString(ctx)
+          s"(${debugInfoFor(n)}) ${indent(p)}"
+      }
+    }
+
+    def indent(a: String): String = {
+      a.replaceAll("\n", "\n"+(" "*12))
+    }
+
+    def pathToString(cd: List[Int]): String = {
+      cd.map(i => f"$i%2d").mkString(" ")
+    }
+
+    val c = currentNode(path)
+
+    println("-"*120)
+    val at = path.lastOption.map(p => pathToString(List(p))).getOrElse("R")
+
+    println(title(at+" \u2510 "+displayNode(c)))
+
+    for ((sn, i) <- c.descendants.zipWithIndex) {
+      val sp = List(i)
+
+      if (sn.isSolved) {
+        println(solved("  "+pathToString(sp)+" \u2508 "+displayNode(sn)))
+      } else if (sn.isDeadEnd) {
+        println(failed("  "+pathToString(sp)+" \u2508 "+displayNode(sn)))
+      } else if (sn.isExpanded) {
+        println(expanded("  "+pathToString(sp)+" \u2508 "+displayNode(sn)))
+      } else {
+        println("  "+pathToString(sp)+" \u2508 "+displayNode(sn))
+      }
+    }
+    println("-"*120)
+  }
+
+  var continue = true
+
+  def findDescendent(n: Node, index: Int): Option[Node] = {
+    n.descendants.zipWithIndex.find(_._2 == index).map(_._1)
+  }
+
+  def manualGetNext(): Option[Node] = {
+    val c = currentNode(path)
+
+    if (!c.isExpanded) {
+      Some(c)
+    } else {
+      printGraph()
+
+      nextCommand() match {
+        case Quit =>
+          None
+
+        case Parent =>
+          if (path.nonEmpty) {
+            path = path.dropRight(1)
+          } else {
+            error("Already at root node!")
+          }
+
+          manualGetNext()
+
+        case Best =>
+          strat.bestNext(c) match {
+            case Some(n) =>
+              val i = c.descendants.indexOf(n)
+              path = path :+ i
+              Some(currentNode(path))
+
+            case None =>
+              error("Woot?")
+              manualGetNext()
+          }
+
+
+        case Cd(Nil) =>
+          error("Woot?")
+          None
+
+        case Cd(next :: rest) =>
+          findDescendent(c, next) match {
+            case Some(_) =>
+              path = path :+ next
+            case None =>
+              warning("Unknown descendent: "+next)
+          }
+
+          if (rest.nonEmpty) {
+            cmdQueue = Cd(rest) :: cmdQueue
+          }
+          manualGetNext()
+      }
+    }
+  }
+
+  override def getNextToExpand(root: Node): Option[Node] = {
+    manualGetNext()
+  }
+
+  def debugInfoFor(n: Node) = strat.debugInfoFor(n)
+
+  var cmdQueue = initCmd.map( str => parseCommands(parseString(str))).getOrElse(Nil)
+
+  private def parseString(s: String): List[String] = {
+    s.trim.split("\\s+|,").toList
+  }
+
+  private def nextCommand(): Command = cmdQueue match {
+    case c :: cs =>
+      cmdQueue = cs
+      c
+
+    case Nil =>
+      print("Next action? (q to quit) "+path.mkString(" ")+" $ ")
+      val line = scala.io.StdIn.readLine()
+      val parts = parseString(line)
+
+      cmdQueue = parseCommands(parts)
+      nextCommand()
+  }
+
+  private def parseCommands(tokens: List[String]): List[Command] = tokens match {
+    case "cd" :: ".." :: ts =>
+      Parent :: parseCommands(ts)
+
+    case "cd" :: ts =>
+      val path = ts.takeWhile { t => t.forall(_.isDigit) }
+
+      if (path.isEmpty) {
+        parseCommands(ts)
+      } else {
+        Cd(path.map(_.toInt)) :: parseCommands(ts.drop(path.size))
+      }
+
+    case "b" :: ts =>
+      Best :: parseCommands(ts)
+
+    case "q" :: ts =>
+      Quit :: Nil
+
+    case Nil  | "" :: Nil =>
+      Nil
+
+    case ts =>
+      val path = ts.takeWhile { t => t.forall(_.isDigit) }
+
+      if (path.isEmpty) {
+        error("Unknown command "+ts.head)
+        parseCommands(ts.tail)
+      } else {
+        Cd(path.map(_.toInt)) :: parseCommands(ts.drop(path.size))
+      }
+  }
+}
diff --git a/src/main/scala/leon/synthesis/strategies/Strategy.scala b/src/main/scala/leon/synthesis/strategies/Strategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..b9142229d9767703a99b867797e365852e77ba22
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/Strategy.scala
@@ -0,0 +1,59 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import synthesis.graph._
+
+import leon.utils.Interruptible
+
+abstract class Strategy extends Interruptible {
+
+  // Nodes to consider next
+  private var openNodes = Set[Node]()
+
+  def init(root: RootNode): Unit = {
+    openNodes += root
+  }
+
+  /**
+   * Standard-next for AndNodes, strategy-best for OrNodes
+   */
+  def bestNext(n: Node): Option[Node] = {
+    n match {
+      case an: AndNode =>
+        an.descendants.find(_.isOpen)
+
+      case on: OrNode =>
+        bestAlternative(on)
+    }
+  }
+
+  def bestAlternative(on: OrNode): Option[Node]
+
+  def getNextToExpand(root: Node): Option[Node] = {
+    if (openNodes(root)) {
+      Some(root)
+    } else if (openNodes.isEmpty) {
+      None
+    } else {
+      bestNext(root).flatMap(getNextToExpand)
+    }
+  }
+
+  def beforeExpand(n: Node): Unit = {}
+
+  def afterExpand(n: Node): Unit = {
+    openNodes -= n
+    openNodes ++= n.descendants
+  }
+
+  def interrupt() = {}
+
+  def recoverInterrupt() = {}
+
+  def debugInfoFor(n: Node): String
+}
+
+
diff --git a/src/main/scala/leon/synthesis/strategies/TimeSlicingStrategy.scala b/src/main/scala/leon/synthesis/strategies/TimeSlicingStrategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..4001a31ad2360fe0b39a1e8c296d88e5e472d88a
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/TimeSlicingStrategy.scala
@@ -0,0 +1,40 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import synthesis.graph._
+
+class TimeSlicingStrategy(ctx: LeonContext) extends Strategy {
+
+  var timeSpent = Map[Node, Long]().withDefaultValue(0l)
+
+  def bestAlternative(on: OrNode): Option[Node] = {
+    on.descendants.filter(_.isOpen).sortBy(timeSpent).headOption
+  }
+
+  def recordTime(from: Node, t: Long): Unit = {
+    timeSpent += from -> (timeSpent(from) + t)
+
+    from.parent.foreach {
+      recordTime(_, t)
+    }
+  }
+
+  var tstart: Long = 0;
+
+  override def beforeExpand(n: Node): Unit = {
+    super.beforeExpand(n)
+    tstart = System.currentTimeMillis
+  }
+
+  override def afterExpand(n: Node): Unit = {
+    super.afterExpand(n)
+
+    val t = System.currentTimeMillis - tstart
+    recordTime(n, t)
+  }
+
+  def debugInfoFor(n: Node) = timeSpent.get(n).map(_.toString).getOrElse("?")
+}
diff --git a/src/main/scala/leon/synthesis/strategies/WrappedStrategy.scala b/src/main/scala/leon/synthesis/strategies/WrappedStrategy.scala
new file mode 100644
index 0000000000000000000000000000000000000000..1b013392b4b5e6a8aea6bc57232815355ecbedb9
--- /dev/null
+++ b/src/main/scala/leon/synthesis/strategies/WrappedStrategy.scala
@@ -0,0 +1,34 @@
+/* Copyright 2009-2015 EPFL, Lausanne */
+
+package leon
+package synthesis
+package strategies
+
+import synthesis.graph._
+
+class WrappedStrategy(underlying: Strategy) extends Strategy {
+
+  override def init(root: RootNode) = underlying.init(root)
+
+  override def getNextToExpand(from: Node): Option[Node] = {
+    underlying.getNextToExpand(from)
+  }
+
+  override def bestAlternative(on: OrNode): Option[Node] = {
+    underlying.bestAlternative(on)
+  }
+
+  override def beforeExpand(n: Node) = {
+    underlying.beforeExpand(n)
+  }
+
+  override def afterExpand(n: Node) = {
+    underlying.afterExpand(n);
+  }
+
+  override def interrupt() = underlying.interrupt()
+
+  override def recoverInterrupt() = underlying.recoverInterrupt()
+
+  def debugInfoFor(n: Node) = underlying.debugInfoFor(n)
+}
diff --git a/src/test/scala/leon/regression/synthesis/SynthesisSuite.scala b/src/test/scala/leon/regression/synthesis/SynthesisSuite.scala
index ca2b4a3c98107c73c9244486200dd0a44a348cb2..a3e94094fe887f048eca984a68ccebb97ed73530 100644
--- a/src/test/scala/leon/regression/synthesis/SynthesisSuite.scala
+++ b/src/test/scala/leon/regression/synthesis/SynthesisSuite.scala
@@ -9,6 +9,7 @@ import leon.synthesis.graph._
 import leon.synthesis.utils._
 import leon.utils.PreprocessingPhase
 
+/*
 class SynthesisSuite extends LeonRegressionSuite {
   private var counter : Int = 0
   private def nextInt() : Int = {
@@ -345,3 +346,4 @@ object ChurchNumerals {
       Close("CEGIS")
   }
 }
+*/