2011年12月3日土曜日

Fantom でサンタクロース問題

前回のせんだみつおゲームで Fantom の Actor の使い方が何となく分かったので、サンタクロース問題でもやってみようと思う。クリスマスも近いしな。まあ、本物のプログラマには、そんなの関係ないけどな。

====

SantaClausProblem.main()は、サンタ、トナカイ、小人を生成して起動する

using concurrent

class SantaClausProblem {
  Void main() {
    ActorPool pool := ActorPool()

    Santa santa  := Santa(pool)
    Deer[] deers := (1..9).map |Int n->Deer| { Deer(pool, n, santa) }
    Elf[] elves  := (1..10).map |Int n->Elf| { Elf(pool, n, santa) }

    deers.each |Deer deer| { deer.send("vacation") }
    elves.each |Elf elf| { elf.send("home") }

    while (!pool.isStopped) { Actor.sleep(1sec) }
  }
}

Player は登場人物共通のコード。ActorUnderSanta はトナカイと小人の共通コード。

Actor
 └ Player
     ├ Santa
     └ ActorUnderSanta
         ├ Deer
         └ Elf
const class Player: Actor {
  new make(ActorPool pool) : super(pool) {}

  Void sleepRandom(Range r) { Actor.sleep(1sec* Int.random(r)) }
}
const class ActorUnderSanta: Player {
  const Int number
  const Santa santa

  new make(ActorPool pool, Int number, Santa santa) : super(pool) {
    this.number = number
    this.santa = santa }

  Str name() { this.typeof().name + number  }
}

トナカイと小人はこんな感じ

const class Deer: ActorUnderSanta {
  new make(ActorPool pool, Int number, Santa santa)
    : super(pool, number, santa) {}

  override Obj? receive(Obj? msg) {
    if ("vacation" == msg) { sleepRandom(1..10); santa.send(this) }
    return msg
  }
}

const class Elf: ActorUnderSanta {
  new make(ActorPool pool, Int number, Santa santa)
    : super(pool, number, santa) {}

  override Obj? receive(Obj? msg) {
    if ("home" == msg) { sleepRandom(2..15); santa.send(this) }
    return msg
  }
}

サンタはこんな感じ

const class Santa: Player {
  new make(ActorPool pool) : super(pool) {}

  override Obj? receive(Obj? msg) {
    echo (((ActorUnderSanta)msg).name + " が来た")
    
    if (msg is Deer) addDeer((Deer)msg)
    else if (msg is Elf) addElf((Elf)msg)
    
    return msg
  }
  Void addDeer(Deer deer) {
    Deer[] deers := get("deers", (Deer[])[,])
    deers.add(deer)
    wakeUpIfConditionIsMet
  }
  Void addElf(Elf elf) {
    Elf[] elves := get("elves", (Elf[])[,])
    elves.add(elf)
    wakeUpIfConditionIsMet
  }
  Void wakeUpIfConditionIsMet() {
    if (9 == getDeers().size) deliverToys
    else if (3 <= getElves().size) meetInStudy
  }
  Void deliverToys() {
    echo("おもちゃを配る")
    sleepRandom(1..2)
    getDeers().each|Deer deer| { deer.send("vacation") }
    Actor.locals["deers"] = (Deer[])[,]
  }
  Void meetInStudy() {
    echo("小人と打ち合わせ")
    sleepRandom(1..2)
    Elf[] elves := getElves()
    elves.eachRange(0..2, |Elf elf| { elf.send("home") })
    Actor.locals["elves"] = elves.removeRange(0..2)
  }
  Deer[] getDeers() { (Deer[])Actor.locals["deers"] }

  Elf[] getElves() { (Elf[])Actor.locals["elves"] }

  Obj get(Str key, Obj defaultValue) {
    Obj? result := Actor.locals[key]
    if (null == result) {
      result = defaultValue
      Actor.locals[key] = result
    }
    return result
  }
}
こんな結果が得られる
$ fan santaClausProblem.fan
Deer6 が来た
Deer1 が来た
Elf2 が来た
Elf9 が来た
Deer9 が来た
Deer2 が来た
Deer8 が来た
Deer3 が来た
Elf3 が来た
小人と打ち合わせ
Elf5 が来た
Elf8 が来た
Deer4 が来た
Deer7 が来た
Elf1 が来た
小人と打ち合わせ
Deer5 が来た
おもちゃを配る
Elf2 が来た
Elf7 が来た
Elf9 が来た
小人と打ち合わせ
Deer9 が来た
Deer5 が来た
Elf4 が来た
Elf6 が来た
Elf3 が来た
小人と打ち合わせ

0 件のコメント:

コメントを投稿