ScalaでLuaスクリプトを動かして遊ぶ

これはScala Advent Calendar 2015(Adventar版)10日目です。9日目はShinpeimさんの既存のクラスをScalazで定義されている型クラスのインスタンスにするの巻でした。

さて、ScalaでLuaです。みんなScala上でLua動かしたいですよね!?1

luaj 2を入れましょう

libraryDependencies += "org.luaj" % "luaj-jse" % "3.0.1"

ただ実行するだけならJsePlatform.standardGlobals().loadを使います。またloadFileメソッドでファイルも指定できるようです。

import org.luaj.vm2.lib.jse._

val globals = JsePlatform.standardGlobals()
val chunk = globals.load("print 'hello, world'")
chunk.call
$ sbt run
[info] Set current project to (...)
[info] Compiling 1 Scala source to ...
[info] Running Main
hello, world

ScalaからLuaに値を渡したり、逆にLuaから値を受けとりたくなりますよね。もちろん可能です。

import javax.script.ScriptEngineManager

val manager = new ScriptEngineManager()
val engine  = manager.getEngineByName("luaj")

engine.put("x", 10)
engine.put("y", 20)
engine.eval("z = x + y")

val result = engine.get("z")
println(result) // => 30

調子に乗ってLuaスクリプトを非同期で実行してみました。

import javax.script.ScriptEngineManager
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global

object Main {
  def executeLua[T](script: String, returnVariable: String, binds: Map[String, Any]): Future[T] = Future {
    val manager = new ScriptEngineManager()
    val engine  = manager.getEngineByName("luaj")

    binds.foreach { bind =>
      engine.put(bind._1, bind._2)
    }

    engine.eval(script)
    engine.get(returnVariable).asInstanceOf[T]
  }

  def main(args: Array[String]): Unit = {
    val luaScript =
      """
        |function fib(n)
        |   if n < 2 then return n end
        |   return fib(n - 2) + fib(n - 1)
        |end
        |
        |result = fib(x)
      """.stripMargin

    val f1 = executeLua[Int](luaScript, "result", Map("x" -> 10))
    val f2 = executeLua[Int](luaScript, "result", Map("x" -> 38))
    val f3 = executeLua[Int](luaScript, "result", Map("x" -> 10))

    f1.onSuccess { case r: Int => println(r) }
    f2.onSuccess { case r: Int => println(r) }
    f3.onSuccess { case r: Int => println(r) }

    Await.ready(f1, Duration.Inf)
    Await.ready(f2, Duration.Inf)
    Await.ready(f3, Duration.Inf)
  }
}
$ ./sbt run
[info] Set current project to (...)
[info] Compiling 1 Scala source to ...
[info] Running Main
55
55
39088169
[success] Total time: 26 s, completed 2015/12/09 3:14:23
  1. ネタっぽい感じですが、ゲーム業界だとLuaの需要がそれなりにあるので、現場によっては意外と使えるかも?ゲームバランスに影響するロジックをLuaに切り出しておくと開発終盤にプランナーが幸せになれるかもしれない 

  2. JavaライブラリなのでScala以外でも使えます