mike-neckのブログ

JavaかJavaFXかJavaEE(なんかJava8が多め)

カイジの地下チンチロで班長大槻が勝つ確率をシミュレートするGroovyスクリプト

ざわ…
           ざわ…
     ざわ…

こんにちわ、みけです。

表題の件について、班長大槻が地下チンチロでカイジに勝つ確率を調べてみました。


…と、その前に地下チンチロについて、説明をば…

地下チンチロのルール

チンチロのルールは以下のとおり

  • サイコロを3つふる
  • 二つ以上同じ目が出た時に役が成立
  • 二つ以上同じ目が出た時に、残りの一つの目を出目とする
  • 例えば、1・3・1の目が出た時の出目は3
  • 三つ同じ目が出た時はゾロ目
  • 三つ同じ目が出た時にすべて1だった場合はピンゾロ
  • 全部違う目が出た時は次の場合を除いて振り直し
  • 1・2・3が出た時の出目はヒフミ
  • 4・5・6が出た時の出目はシゴロ
  • 振り直しは2回まで(計3回振れる)
  • 振り直しの結果、役がなかった場合は、出目なし
  • サイコロを振った時にお椀からサイコロが出た場合はションベン(=出目なしと同じ)
  • 親がシゴロ、ゾロ目、6の目を出した時も子はサイコロを振れる(地下チンチロ特別ルール)

役の強さは次の通り

  1. ピンゾロ
  2. ゾロ目
  3. シゴロ
  4. 出目の大きい順番
  5. 出目なし=ションベン
  6. ヒフミ

親と子の勝負については、割愛


と、ここまではよいのですが、

班長大槻はここぞという場面でイカサマをするわけです。

それが常勝シゴロ賽です。

常勝シゴロ賽

常勝シゴロ賽は、班長大槻が特別に用意したサイコロで、

  • 4の裏は4
  • 5の裏は5
  • 6の裏は6

というサイコロです。

このサイコロを使うと、

最低でも出目は4、同じ確率でシゴロが出るという優れもののサイコロとなっています。

f:id:mike_neck:20141014040532p:plain

ではいざ尋常に勝負

というわけで、シミュレートするスクリプトは次の通り。

ただし、いくつかどうにも出来ない事項があったので、

それらは考慮していません。

  • ションベンはどうしようにも計算出来ないので除外
  • 支払いレートについては面倒だったので除外(難しいわけではない)
IntRange.metaClass.define {
    random = {
        delegate.min() + new Random().nextInt(delegate.size())
    }
}

@groovy.transform.Canonical
class Dice {
    def dice = (1..6)
    int play() {
        dice.random()
    }
    static def jousyou = {
        def d = new Dice()
        d.dice = (4..6)
        return d
    }
}

enum Game {
    WIN, LOOSE, DRAW
}

enum Result {
    One(1), Two(2), Three(3), Four(4), Five(5), Six(6),
    Hifumi(-1), Shigoro(7), Zorome(8), Pinzoro(9), Menashi(0)

    def power    
    Result(int n) {
        this.power = n
    }
    static Result find(int value) {
        values().find {it.power == value}
    }
    static Result getResult(List list) {
        def map = list.groupBy{it}.collectEntries {
            [it.key, it.value.size()]
        }
        if (map.size() == 1) {
            return map.collect{it.key}[0] == 1?Pinzoro:Zorome
        } else if (map.size() == 2) {
            return find(map.find{it.value == 1}.key)
        } else if (map.collect{it.key}.sort() == [1,2,3]) {
            return Hifumi
        } else if (map.collect{it.key}.sort() == [4,5,6]) {
            return Shigoro
        } else {
            return Menashi
        }
    }
    def comp = {Result other ->
        if (power < other.power) return Game.LOOSE
        if (power > other.power) return Game.WIN
        return Game.DRAW
    }
}

@groovy.transform.Canonical
class Dices {
    def dices = [new Dice(), new Dice(), new Dice()]
    Result play() {
        def res = dices.collect{it.play()}
        Result.getResult(res)
    }
    static def jousyou = {
        def d = new Dices()
        d.dices = [Dice.jousyou(), Dice.jousyou(), Dice.jousyou()]
        return d
    }
}

@groovy.transform.Canonical
class Challenge {
    def dices = new Dices()
    Result play() {
        def result = Result.Menashi
        def count = 0
        while(result == Result.Menashi && count < 3) {
            result = dices.play()
            count++
        }
        return result
    }
}

final def player = ['normal', 'ootsuki'].collectEntries {
    if (it == 'normal') return [it, new Challenge()]
    def c = new Challenge()
    c.dices = Dices.jousyou()
    return [it, c]
}

(1..100000).collect {
    def normal = player.normal.play()
    def ootsuki = player.ootsuki.play()
    return normal.comp(ootsuki)
}.groupBy {
    it
}.collectEntries {
    def winner = it.key == Game.WIN? 'カイジ ': it.key == Game.LOOSE? '班長大槻': '引き分け'
    [winner, it.value.size()]
}.sort{it.key}.each {
    println "${it.key}[${String.format('%5d', it.value)}]"
}

結果

というわけで、試行回数10万回で行ったところ、

次のような結果になりました。

カイジ [15748]
引き分け[ 9865]
班長大槻[74387]

何度かやってみましたが、大体班長大槻が勝つ確率は75%弱くらいでした。

また、カイジが勝つ確率は15%強といったところでした。

意外なことに10%程度は引き分けになることがあるようです。


スクリプトが汚いのは、まあ目をつむっといてください。

以上