diff --git a/build.sbt b/build.sbt
index 8a217a837bc141045ea76cef19341b7a42cc5a63..2363514675c88cbb9850b3f17f952f1e1f47be0b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -18,6 +18,7 @@ ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports"
 
 
 
+
 val commonSettings = Seq(
   crossScalaVersions := Seq("2.12.13", "2.13.4", "3.0.1", "3.2.0"),
   run / fork := true
diff --git a/lisa-examples/src/main/scala/Example.scala b/lisa-examples/src/main/scala/Example.scala
index 17f272ab5ef77c947ff7beeb0eadc139ae1b43a7..28b78f657614e16d12f4ef651bbc70500be17582 100644
--- a/lisa-examples/src/main/scala/Example.scala
+++ b/lisa-examples/src/main/scala/Example.scala
@@ -2,8 +2,6 @@ import lisa.automation.Substitution.{ApplyRules as Substitute}
 
 object Example extends lisa.Main {
 
-
-
   val x = variable
   val y = variable
   val P = predicate[1]
diff --git a/lisa-sets/src/main/scala/lisa/automation/Tableau.scala b/lisa-sets/src/main/scala/lisa/automation/Tableau.scala
index f084af4e3513dca9195d4bfe1b831c543c719be2..1ab6e0673eefaa2a52b8d34e7505fffab66412ac 100644
--- a/lisa-sets/src/main/scala/lisa/automation/Tableau.scala
+++ b/lisa-sets/src/main/scala/lisa/automation/Tableau.scala
@@ -1,6 +1,7 @@
 package lisa.automation
 import lisa.fol.FOL as F
 import lisa.prooflib.Library
+import lisa.prooflib.OutputManager.*
 import lisa.prooflib.ProofTacticLib.*
 import lisa.utils.K
 import lisa.utils.K.{_, given}
@@ -73,7 +74,6 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
     val nf = reducedNNFForm(fnamed)
     val uv = VariableLabel(Identifier("§", nextId))
     val proof = decide(Branch.empty(nextId + 1, uv).prepended(nf))
-
     proof match
       case None => None
       case Some((p, _)) => Some(SCProof((Restate(sequent, p.length) :: Weakening(nf |- (), p.length - 1) :: p).reverse.toIndexedSeq, IndexedSeq.empty))
@@ -103,7 +103,9 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
       delta: List[BinderFormula], // Exists(...))
       gamma: List[BinderFormula], // Forall(...)
       atoms: (List[AtomicFormula], List[AtomicFormula]), // split into positive and negatives!
-      unifiable: Map[VariableLabel, BinderFormula], // map between metavariables and the original formula they came from
+      unifiable: Map[VariableLabel, (BinderFormula, Int)], // map between metavariables and the original formula they came from, with the penalty associated to the complexity of the formula.
+      numberInstantiated: Map[VariableLabel, Int], // map between variables and the number of times they have been instantiated
+
       skolemized: Set[VariableLabel], // set of variables that have been skolemized
       triedInstantiation: Map[VariableLabel, Set[Term]], // map between metavariables and the term they were already instantiated with
       maxIndex: Int, // the maximum index used for skolemization and metavariables
@@ -142,14 +144,20 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
 
     import Branch.*
     override def toString(): String =
-      val pretUnif = unifiable.map((x, f) => x.id + " -> " + prettyFormula(f)).mkString("Unif(", ", ", ")")
+      val pretUnif = unifiable.map((x, f) => x.id + " -> " + prettyFormula(f._1) + " : " + f._2).mkString("Unif(", ", ", ")")
       // val pretTried = triedInstantiation.map((x, t) => x.id + " -> " + prettyTerm(t, true)).mkString("Tried(", ", ", ")")
-      s"Branch(${prettyIte(alpha, "alpha")}, ${prettyIte(beta, "beta")}, ${prettyIte(delta, "delta")}, ${prettyIte(gamma, "gamma")}, ${prettyIte(atoms._1, "+")}, ${prettyIte(atoms._2, "-")}, $pretUnif, _, _)"
+      (s"Branch(" +
+        s"${RED(prettyIte(alpha, "alpha"))}, " +
+        s"${GREEN(prettyIte(beta, "beta"))}, " +
+        s"${BLUE(prettyIte(delta, "delta"))}, " +
+        s"${YELLOW(prettyIte(gamma, "gamma"))}, " +
+        s"${MAGENTA(prettyIte(atoms._1, "+"))}, ${CYAN(prettyIte(atoms._2, "-"))}, " +
+        s"$pretUnif, _, _)").split("'").mkString("").split("_").mkString("")
 
   }
   object Branch {
-    def empty = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Set.empty, Map.empty, 1, Map.empty, VariableLabel(Identifier("§uv", 0)))
-    def empty(n: Int, uv: VariableLabel) = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Set.empty, Map.empty, n, Map.empty, uv)
+    def empty = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, 1, Map.empty, VariableLabel(Identifier("§uv", 0)))
+    def empty(n: Int, uv: VariableLabel) = Branch(Nil, Nil, Nil, Nil, (Nil, Nil), Map.empty, Map.empty, Set.empty, Map.empty, n, Map.empty, uv)
     def prettyIte(l: Iterable[Formula], head: String): String = l match
       case Nil => "Nil"
       case _ => l.map(prettyFormula(_, true)).mkString(head + "(", ", ", ")")
@@ -275,8 +283,37 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
       )
     )
 
-    cr.sortBy(_._1.size).headOption
+    bestSubst(cr, branch)
+
+  }
+
+  def bestSubst(substs: List[(Substitution, Set[Formula])], branch: Branch): Option[(Substitution, Set[Formula])] = {
+    if substs.isEmpty then return None
+    val minSize = substs.minBy(_._1.size)
+    val smallSubst = substs.filter(_._1.size == minSize._1.size)
+    // Up to this, it is necessary for completeness. From this, it is heuristic.
 
+    val best = smallSubst.minBy(s => substitutionScore(s._1, branch))
+    Some(best)
+  }
+  def formulaPenalty(f: Formula, branch: Branch): Int = f match
+    case ConnectorFormula(And, args) => 10 + args.map(formulaPenalty(_, branch)).sum
+    case ConnectorFormula(Or, args) => 40 + args.map(formulaPenalty(_, branch)).sum
+    case BinderFormula(Exists, x, inner) => 30 + formulaPenalty(inner, branch)
+    case BinderFormula(Forall, x, inner) => 200 + formulaPenalty(inner, branch)
+    case AtomicFormula(id, args) => 0
+    case ConnectorFormula(Neg, List(AtomicFormula(id, args))) => 0
+    case _ => ???
+
+  def substitutionScore(subst: Substitution, branch: Branch): Int = {
+    def pairPenalty(v: VariableLabel, t: Term) = {
+      val variablePenalty = branch.unifiable(v)._2 + branch.numberInstantiated(v) * 20
+      def termPenalty(t: Term): Int = t match
+        case VariableTerm(x) => if branch.unifiable.contains(x) then branch.unifiable(x)._2 * 1 else 0
+        case Term(label, args) => 100 + args.map(termPenalty).sum
+      variablePenalty + termPenalty(t)
+    }
+    subst.map((v, t) => pairPenalty(v, t)).sum
   }
 
   /**
@@ -330,7 +367,13 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
         val newBound = VariableLabel(Identifier(f.bound.id.name, branch.maxIndex))
         val newInner = substituteVariablesInFormula(f.inner, Map(f.bound -> newBound), Seq())
         (newInner, newBound)
-    val b1 = branch.copy(gamma = branch.gamma.tail, unifiable = branch.unifiable + (nb -> f), maxIndex = branch.maxIndex + 1, varsOrder = branch.varsOrder + (nb -> branch.varsOrder.size))
+    val b1 = branch.copy(
+      gamma = branch.gamma.tail,
+      unifiable = branch.unifiable + (nb -> (f, formulaPenalty(f.inner, branch))),
+      numberInstantiated = branch.numberInstantiated + (nb -> (branch.numberInstantiated.getOrElse(f.bound, 0))),
+      maxIndex = branch.maxIndex + 1,
+      varsOrder = branch.varsOrder + (nb -> branch.varsOrder.size)
+    )
     (b1.prepended(ni), nb, ni)
   }
 
@@ -339,13 +382,18 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
    * This does not backtracking: The metavariable remains available if it needs further instantiation.
    */
   def applyInst(branch: Branch, x: VariableLabel, t: Term): (Branch, Formula) = {
-    val f = branch.unifiable(x)
+    val f = branch.unifiable(x)._1
     val newTried = branch.triedInstantiation.get(x) match
       case None => branch.triedInstantiation + (x -> Set(t))
       case Some(s) => branch.triedInstantiation + (x -> (s + t))
 
     val inst = instantiate(f.inner, f.bound, t)
-    val r = branch.prepended(inst).copy(triedInstantiation = newTried)
+    val r = branch
+      .prepended(inst)
+      .copy(
+        triedInstantiation = newTried,
+        numberInstantiated = branch.numberInstantiated + (x -> (branch.numberInstantiated(x) + 1))
+      )
     (r, inst)
   }
 
@@ -356,8 +404,7 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
    * The return integer is the size of the proof: Used to avoid computing the size every time in linear time.
    */
   def decide(branch: Branch): Option[(List[SCProofStep], Int)] = {
-    // println(" ")
-    // println("decide: " + branch)
+
     val closeSubst = close(branch)
     if (closeSubst.nonEmpty && closeSubst.get._1.isEmpty) // If branch can be closed without Instantiation (Hyp)
       Some((List(RestateTrue(Sequent(closeSubst.get._2, Set()))), 0))
@@ -421,8 +468,8 @@ object Tableau extends ProofTactic with ProofSequentTactic with ProofFactSequent
       val upperProof = decide(recBranch)
       upperProof.map((proof, step) =>
         if proof.head.bot.left.contains(instantiated) then
-          val sequent = (proof.head.bot -<< instantiated) +<< branch.unifiable(x)
-          (LeftForall(sequent, step, branch.unifiable(x).inner, branch.unifiable(x).bound, t) :: proof, step + 1)
+          val sequent = (proof.head.bot -<< instantiated) +<< branch.unifiable(x)._1
+          (LeftForall(sequent, step, branch.unifiable(x)._1.inner, branch.unifiable(x)._1.bound, t) :: proof, step + 1)
         else (proof, step)
       )
     else None
diff --git a/lisa-utils/src/main/scala/lisa/prooflib/OutputManager.scala b/lisa-utils/src/main/scala/lisa/prooflib/OutputManager.scala
index c147c3529f2efa34bfa374eea61090d5b6903083..74bf1592e73146c042488a2c48d8c0f4934671e2 100644
--- a/lisa-utils/src/main/scala/lisa/prooflib/OutputManager.scala
+++ b/lisa-utils/src/main/scala/lisa/prooflib/OutputManager.scala
@@ -38,4 +38,13 @@ abstract class OutputManager {
     e.printStackTrace(PrintWriter(stringWriter))
     output(Console.RESET)
   }
+
+}
+object OutputManager {
+  def RED(s: String): String = Console.RED + s + Console.RESET
+  def GREEN(s: String): String = Console.GREEN + s + Console.RESET
+  def BLUE(s: String): String = Console.BLUE + s + Console.RESET
+  def YELLOW(s: String): String = Console.YELLOW + s + Console.RESET
+  def CYAN(s: String): String = Console.CYAN + s + Console.RESET
+  def MAGENTA(s: String): String = Console.MAGENTA + s + Console.RESET
 }
diff --git a/lisa-utils/src/main/scala/lisa/utils/K.scala b/lisa-utils/src/main/scala/lisa/utils/K.scala
index 1980bc0f4ff379fd8ce60fa53b6ed9ce3232ffa2..2269cfed39bb481da9967fc77c5af445990f26fd 100644
--- a/lisa-utils/src/main/scala/lisa/utils/K.scala
+++ b/lisa-utils/src/main/scala/lisa/utils/K.scala
@@ -11,5 +11,6 @@ object K {
   export lisa.kernel.proof.RunningTheoryJudgement as Judgement
   export lisa.kernel.proof.RunningTheoryJudgement.*
   export lisa.utils.KernelHelpers.{*, given}
+  export lisa.utils.parsing.FOLPrinter.*
 
 }