Problem:
Problem G: Dice

サイコロ３兄弟は三つ子であり一心同体、３つでひとつとも言ってもよい。三つ子であるため見分けがつかないほど似ている。そんなサイコロ３兄弟はA君のお気に入りのおもちゃだ。A君はいつも３つ１セットでサイコロを転がして遊んでいる。このようにA君が楽しく遊ぶおもちゃだが、実は大きな秘密があった。彼らは実は生きていて、話したり自由に動いたりできるのだ。子ども部屋は r × c の大きさのグリッドで表すことができ、とても大きいものが散乱している。

サイコロ３兄弟は室内を東西南北の４方向へ進行方向に向かって転がりながら移動する。その形ゆえに斜め移動ができないのが、少し気の毒である。進行方向にとても大きいものがある場合、サイコロは進行方向に進むことができない。
サイコロ３兄弟は３つで１セットであるため、一度にひとつのサイコロしか移動することができない。また、サイコロたちはその場で回転するなどという器用な能力は持っていない。

彼らが話したり動いたりしている事実は人間には知られてはいけないというのが「おもちゃのルール」である。このルールを破ったおもちゃはこの世から消滅してしまう。A君が出かけているときに動いたり話したりしているおもちゃたちだが、A君が帰宅して子ども部屋に入ってきたときにもとの位置に戻っていなければならない。A君が帰宅した直後は緊急事態であり、A君が子ども部屋にたどり着く前にあらゆるおもちゃは即座にもとの場所へ戻らなければならない。
A君は床を見下ろすかたちになるため、サイコロの場所と上を向いている数字が正しければ、移動したことに気づくことはない。また、サイコロたちは見分けがつかないほど似ているので、どの収納場所にどのサイコロが向かっても支障はない。

そんなある日、A君のおもちゃ箱の中で最も優秀なプログラマーであるあなたに、与えられたサイコロの位置情報と収納場所から最短何手でサイコロ達が収納場所に戻れるかを計算する任務が与えられた。
サイコロ３兄弟を溺愛しているA君の手からサイコロ３兄弟が消えてしまったら、A君は泣いてしまうので、とても重要な任務である。

Input

入力は以下のフォーマットで与えられる。

r c
grid1
.
.
.
gridr

gridi は長さ c の文字列で、1から6までの数字か"."か”x”か”o”からなる。

数字は収納場所かつ、到着時のサイコロの上の値を示している。

“.”は普通の床を示している。

“x”はとてもおおきなものを示している。

“o”はサイコロを示している。

入力は以下の制約を満たす

1 ≤ r,c ≤ 20

初期状態でのサイコロの向きは以下の図に従う。

Output

それぞれの入力に対する、各サイコロがすべて収納できるまでの最短手数を求めよ。

Sample Input 1

3 3
4o.
4o.
4o.

Sample Output 1

３

各サイコロが西方向に移動すればよい。

Sample Input 2

4 4
4o..
.o3.
.o..
.5..

Sample Output2

3

1行目のにあるサイコロが西方向

2行目のにあるサイコロが東方向

3行目のにあるサイコロが南方向に移動すればよい。

Sample Input 3

6 10
1xxxxxxxxx
o.......o.
xxxx.xxxx4
xoxx.xxxx.
x....3xx..
x..xxxxx..

Sample Output 3

34

Solution in Scala (judge verdict: Runtime Error)

import scala.collection.mutable

object Main extends App {
  import scala.io.StdIn.{readLine}
  import scala.collection.mutable._

  class Dice private (private val top:Int = 1, private val south:Int = 2, private val east:Int = 4){
    def turnUp:Dice = new Dice(south, 7 - top, east)
    def turnDown:Dice = new Dice(7 - south, top, east)
    def turnRight:Dice = new Dice(7 - east, south, top)
    def turnLeft:Dice = new Dice(east, south, 7 - top)
    def toIndex:Int = (top - 1) * 4 + (if (south > 3) 2 else 0) + (if (east > 3) 1 else 0) //0 until 24
    def topNumber:Int = top
  }
  case class Point(x:Int, y:Int){
    def up:Point = Point(x, y - 1)
    def right:Point = Point(x + 1, y)
    def down:Point = Point(x, y + 1)
    def left:Point = Point(x - 1, y)
  }
  class Saikorokun private (val position:Point, val dice:Dice) {
    def moveUp:Saikorokun = new Saikorokun(position.up, dice.turnUp)
    def moveRight:Saikorokun = new Saikorokun(position.right, dice.turnRight)
    def moveDown:Saikorokun = new Saikorokun(position.down, dice.turnDown)
    def moveLeft:Saikorokun = new Saikorokun(position.left, dice.turnLeft)
    def neighbors():List[Saikorokun] = List(moveUp, moveRight, moveDown, moveLeft)
  }
  object Saikorokun{
    def apply(start:Point):Saikorokun = new Saikorokun(start, Dice())
  }
  object Dice{
    def apply():Dice = new Dice()
  }
  class Room private (private val mArray:Array[Array[Array[Int]]], private val mGoals:List[(Point, Int)]){
    def isEmpty(saikorokun: Saikorokun):Boolean = (0 until mArray.length).contains(saikorokun.position.y) && (0 until mArray(0).length).contains(saikorokun.position.x) && mArray(saikorokun.position.y)(saikorokun.position.x)(saikorokun.dice.toIndex) == Room.unset
    def update(saikorokun:Saikorokun, value:Int):Unit = mArray(saikorokun.position.y)(saikorokun.position.x)(saikorokun.dice.toIndex) = value
    def isAllGoalReached():Boolean = mGoals.forall{case (p, d) ⇒ (0 until 4).exists{i ⇒ mArray(p.y)(p.x)((d - 1) * 4 + i) != Room.unset}}
    def minCost(point: Point, top:Int):Int = (0 until 4).map{i ⇒ mArray(point.y)(point.x)((top - 1) * 4 + i)}.filter{n ⇒ n != Room.unset}.min
  }
  object Room{
    def apply(strings:Array[String]):Room = {
      var goals :List[(Point, Int)] = Nil
      val array = Array.tabulate(strings.length){
        y ⇒ strings(y).zipWithIndex.map{
          case ('x', _) ⇒ Array.tabulate(24){_ ⇒ blocked}
          case (d, x) if d.isDigit ⇒ goals = (Point(x, y), d.asDigit)::goals
            Array.tabulate(24){_ ⇒ unset}
          case _ ⇒ Array.tabulate(24){_ ⇒ unset}
        }.toArray
      }
      new Room(array, goals)
    }
    private val blocked:Int = -1
    private val unset:Int = -2
  }
  def setShortestCount(room: Room, start:Point):Room = {
    var searching:mutable.Traversable[Saikorokun] = mutable.Queue(Saikorokun(start))
    var count = 0
    room(searching.head) = 0
    while (!room.isAllGoalReached()){
      count += 1
      searching = searching.flatMap{
        case saikoro ⇒ saikoro.neighbors().filter{case s ⇒ room.isEmpty(s)}
      }
      searching.foreach(s ⇒ room(s) = count)
    }
    return room
  }
  val Array(r, c) = readLine().split(' ').map(_.toInt)
  val strings = Array.tabulate(r){_ ⇒ readLine()}
  val start = (0 until r).flatMap{y ⇒ (0 until c).filter{x ⇒ strings(y)(x) == 'o'}.map{x ⇒ Point(x, y)}}
  val goal = (0 until r).flatMap{y ⇒ (0 until c).filter{x ⇒ strings(y)(x).isDigit}.map{x ⇒ Point(x, y)}}
  val minCost = start.map(p ⇒ setShortestCount(Room(strings), p))
  val min = goal.permutations.map{
    case goalPermutation ⇒ goalPermutation.zip(minCost).map{
      case (g, r) ⇒ r.minCost(g, strings(g.y)(g.x).asDigit)
    }.sum
  }.min
  println(min)
}