| 
     1 // 2 x 2 x 2 Rubik's Cube  | 
         | 
     2 //========================  | 
         | 
     3   | 
         | 
     4 // more memory for the JVM  | 
         | 
     5 //  | 
         | 
     6 // JAVA_OPTS="-Xmx2g" scala  | 
         | 
     7   | 
         | 
     8   | 
         | 
     9 // for measuring memory usage  | 
         | 
    10 val mb = 1024*1024  | 
         | 
    11 val runtime = Runtime.getRuntime  | 
         | 
    12   | 
         | 
    13 def memory() = { | 
         | 
    14   println(s"  ** Used Memory:  ${(runtime.totalMemory - runtime.freeMemory) / mb}") | 
         | 
    15   println(s"  ** Free Memory:  ${runtime.freeMemory / mb}") | 
         | 
    16   println(s"  ** Total Memory: ${runtime.totalMemory / mb}") | 
         | 
    17   println(s"  ** Max Memory:   ${runtime.maxMemory / mb}") | 
         | 
    18 }  | 
         | 
    19   | 
         | 
    20   | 
         | 
    21 abstract class Colour  | 
         | 
    22 case object White extends Colour  | 
         | 
    23 case object Yellow extends Colour  | 
         | 
    24 case object Orange extends Colour  | 
         | 
    25 case object Red extends Colour  | 
         | 
    26 case object Green extends Colour  | 
         | 
    27 case object Blue extends Colour  | 
         | 
    28   | 
         | 
    29 // Faces  | 
         | 
    30 //    -------  | 
         | 
    31 //   |c11 c12|  | 
         | 
    32 //   |c21 c22|  | 
         | 
    33 //    -------    | 
         | 
    34 case class Face(c11: Colour, c12: Colour,  | 
         | 
    35                 c21: Colour, c22: Colour)  | 
         | 
    36   | 
         | 
    37   | 
         | 
    38 // Cubes  | 
         | 
    39 //     +--+  | 
         | 
    40 //     |f2|  | 
         | 
    41 //  +--+  +--+  | 
         | 
    42 //  |f5 f1 f6|  | 
         | 
    43 //  +--+  +--+  | 
         | 
    44 //     |f3|  | 
         | 
    45 //     |f4|  | 
         | 
    46 //     +--+   | 
         | 
    47 case class Cube(f1: Face, f2: Face, f3: Face,  | 
         | 
    48                 f4: Face, f5: Face, f6: Face)  | 
         | 
    49   | 
         | 
    50   | 
         | 
    51 // starting cube  | 
         | 
    52 val init =   | 
         | 
    53     Cube(Face(White, Green, White, White),   | 
         | 
    54          Face(Blue, Yellow, Orange, Red),   | 
         | 
    55          Face(Red, Blue, Red, Blue),  | 
         | 
    56          Face(White, Yellow, Red, Orange),  | 
         | 
    57          Face(Yellow, Green, Blue, Green),  | 
         | 
    58          Face(Yellow, Green, Orange, Orange))  | 
         | 
    59   | 
         | 
    60 // solved cube  | 
         | 
    61 val solved =   | 
         | 
    62     Cube(Face(Yellow, Yellow, Yellow, Yellow),   | 
         | 
    63          Face(Orange, Orange, Orange, Orange),   | 
         | 
    64          Face(Red, Red, Red, Red),  | 
         | 
    65          Face(White, White, White, White),  | 
         | 
    66          Face(Blue, Blue, Blue, Blue),  | 
         | 
    67          Face(Green, Green, Green, Green))    | 
         | 
    68   | 
         | 
    69   | 
         | 
    70   | 
         | 
    71 // actions  | 
         | 
    72 def up(c: Cube) : Cube =  | 
         | 
    73     Cube(Face(c.f1.c11, c.f3.c12, c.f1.c21, c.f3.c22),  | 
         | 
    74          Face(c.f2.c11, c.f1.c12, c.f2.c21, c.f1.c22),  | 
         | 
    75          Face(c.f3.c11, c.f4.c12, c.f3.c21, c.f4.c22),  | 
         | 
    76          Face(c.f4.c11, c.f2.c12, c.f4.c21, c.f2.c22),  | 
         | 
    77          Face(c.f5.c11, c.f5.c12, c.f5.c21, c.f5.c22),  | 
         | 
    78          Face(c.f6.c21, c.f6.c11, c.f6.c22, c.f6.c12))  | 
         | 
    79   | 
         | 
    80 def clock(c: Cube) : Cube =  | 
         | 
    81     Cube(Face(c.f1.c21, c.f1.c11, c.f1.c22, c.f1.c12),  | 
         | 
    82          Face(c.f2.c11, c.f2.c12, c.f5.c22, c.f5.c12),  | 
         | 
    83          Face(c.f6.c21, c.f6.c11, c.f3.c21, c.f3.c22),  | 
         | 
    84          Face(c.f4.c11, c.f4.c12, c.f4.c21, c.f4.c22),  | 
         | 
    85          Face(c.f5.c11, c.f3.c11, c.f5.c21, c.f3.c12),  | 
         | 
    86          Face(c.f2.c21, c.f6.c12, c.f2.c22, c.f6.c22))  | 
         | 
    87   | 
         | 
    88 def right(c: Cube) : Cube =  | 
         | 
    89     Cube(Face(c.f5.c11, c.f5.c12, c.f1.c21, c.f1.c22),  | 
         | 
    90          Face(c.f2.c12, c.f2.c22, c.f2.c11, c.f2.c21),  | 
         | 
    91          Face(c.f3.c11, c.f3.c12, c.f3.c21, c.f3.c22),  | 
         | 
    92          Face(c.f4.c11, c.f4.c12, c.f6.c12, c.f6.c11),  | 
         | 
    93          Face(c.f4.c22, c.f4.c21, c.f5.c21, c.f5.c22),  | 
         | 
    94          Face(c.f1.c11, c.f1.c12, c.f6.c21, c.f6.c22))           | 
         | 
    95   | 
         | 
    96   | 
         | 
    97 // simple bf-search without solution, just true  | 
         | 
    98   | 
         | 
    99 def actions(c: Cube) : List[Cube] =   | 
         | 
   100   List(up(c), clock(c), right(c))   | 
         | 
   101   | 
         | 
   102 def search(cs: List[Cube], d: Int) : Boolean = {  | 
         | 
   103   println(s"Depth: $d    Cands: ${cs.length}")   | 
         | 
   104   //memory()  | 
         | 
   105   if (cs.exists(solved == _)) true  | 
         | 
   106   else search(cs.flatMap(actions), d + 1)  | 
         | 
   107 }    | 
         | 
   108   | 
         | 
   109 search(List(init), 0)  | 
         | 
   110   | 
         | 
   111 // Set version  | 
         | 
   112 def actions_set(c: Cube) : Set[Cube] =  | 
         | 
   113   Set(up(c), clock(c), right(c))  | 
         | 
   114   | 
         | 
   115 def search_set(cs: Set[Cube], d: Int) : Boolean = { | 
         | 
   116   println(s"Depth: $d candidates: ${cs.size}") | 
         | 
   117   //memory()  | 
         | 
   118   if (cs.contains(solved)) true  | 
         | 
   119   else search_set(cs.flatMap(actions_set), d + 1)  | 
         | 
   120 }    | 
         | 
   121   | 
         | 
   122 search_set(Set(init), 0)  | 
         | 
   123   | 
         | 
   124   | 
         | 
   125 // For generating list of actions  | 
         | 
   126 abstract class Action  | 
         | 
   127 case object Up extends Action  | 
         | 
   128 case object Right extends Action  | 
         | 
   129 case object Clock extends Action  | 
         | 
   130   | 
         | 
   131 type Actions = List[Action]  | 
         | 
   132   | 
         | 
   133 // List version generating a list of actions  | 
         | 
   134 def actions_list(c: Cube, act: Actions) : List[(Cube, Actions)] =   | 
         | 
   135   List((up(c), Up::act),   | 
         | 
   136        (clock(c), Clock::act),   | 
         | 
   137        (right(c), Right::act))   | 
         | 
   138   | 
         | 
   139   | 
         | 
   140 def search_list(cs: List[(Cube, Actions)], d: Int) : (Cube, Actions) = {  | 
         | 
   141   println(s"Depth: $d    Cands: ${cs.length}")   | 
         | 
   142   val res = cs.find(_._1 == solved)  | 
         | 
   143   if (res.isDefined) res.get  | 
         | 
   144   else search_list(cs.flatMap{case (c, as) => actions_list(c, as) }, d + 1) | 
         | 
   145 }    | 
         | 
   146   | 
         | 
   147 search_list(List((init, Nil)), 0)._2.reverse.mkString("\n")  | 
         | 
   148   | 
         | 
   149   | 
         | 
   150 // Map version generating a list of actions  | 
         | 
   151 def actions_map(c: Cube, as: Actions) : Map[Cube, Actions] =   | 
         | 
   152   Map(up(c) -> (Up::as),   | 
         | 
   153       clock(c) -> (Clock::as),   | 
         | 
   154       right(c) -> (Right::as))   | 
         | 
   155   | 
         | 
   156   | 
         | 
   157 def search_map(cs: Map[Cube, Actions], d: Int) : Actions = {  | 
         | 
   158   println(s"Depth: $d    Cands: ${cs.keySet.size}")   | 
         | 
   159   val res = cs.keySet.find(_ == solved)  | 
         | 
   160   if (res.isDefined) cs(res.get)  | 
         | 
   161   else search_map(cs.flatMap{case (c, as) => actions_list(c, as) }, d + 1) | 
         | 
   162 }    | 
         | 
   163   | 
         | 
   164   | 
         | 
   165 search_map(Map(init -> Nil), 0).reverse.mkString("\n") | 
         | 
   166   | 
         | 
   167   | 
         | 
   168   | 
         | 
   169 // Bidirectional breadth-first search  | 
         | 
   170 def bsearch(cs: Map[Cube, Actions],   | 
         | 
   171             bs: Map[Cube, Actions], d: Int) : Actions = {  | 
         | 
   172   println(s"Depth: $d    Cands: ${cs.keySet.size}/${bs.keySet.size}")  | 
         | 
   173   val res = cs.keySet intersect bs.keySet   | 
         | 
   174   if (!res.isEmpty) (cs(res.head).reverse ::: bs(res.head))  | 
         | 
   175   else bsearch(cs.flatMap{case (c, as) => actions_map(c, as) },  | 
         | 
   176                bs.flatMap{case (c, as) => actions_map(c, as) }, d + 1) | 
         | 
   177 }    | 
         | 
   178   | 
         | 
   179 bsearch(Map(init ->  Nil), Map(solved -> Nil), 0).mkString("\n") | 
         | 
   180   | 
         | 
   181   | 
         | 
   182   | 
         | 
   183   | 
         | 
   184   | 
         | 
   185   | 
         | 
   186   | 
         | 
   187   | 
         | 
   188   | 
         | 
   189   | 
         | 
   190   | 
         | 
   191   | 
         | 
   192   | 
         | 
   193   | 
         | 
   194   | 
         | 
   195   | 
         | 
   196   | 
         | 
   197   | 
         | 
   198   | 
         | 
   199   |