17
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NKODICEシミュレーターを実装してみた

Last updated at Posted at 2021-06-08

動機

NKODICEというゲームがある
どういったゲームなのかはこの記事を読んで感じ取ってほしい。

分かったと思うので、Rubyで簡易的なシミュレータを実装してみる。

実装方針

  • 小便は考慮しない(プロのNKODICEプレイヤーは小便しないので)
  • NUDGEはリロール相当の効果があるものとする(プロのNKODICEプレイヤーは少なくともNUDGEを無駄遣いしないので)
  • wordが完成しない場合、NUDGEする(ライフで受ける戦略や、完成wordが期待よりしょぼかった場合もNUDGEする戦略もあるが、今回はこの実装で行く)

Qiitaの規約の回避

NKODICEで用いられる東洋の神秘的な文字列は、何故かQiitaの規約に抵触してしまう可能性がある
この問題は出力にsortをかけることで回避できる。

> 5.times.map{%w(ん こ う ま ち お).sample}.sort.join
=> "おちちんん"

また、sortすることにより役判定の高速化が望める。

実装

nkodice.rb
def solve(kotodama)
    face_list = kotodama.chars

    word_list = []
    if (kotodama.match(/う.*ち.*ん/))
        word_list<<"うちん"
    end
    if (kotodama.match(/う.*こ.*ん/))
        word_list << "うこん"
    end
    if (kotodama.match(/お.*こ.*ま.*ん/))
        word_list << "おこまん"
    elsif (kotodama.match(/こ.*ま.*ん/))
        word_list << "こまん"
    end
    if (kotodama.match(/こ.*ち.*ん/))
        word_list << "こちん"
    end
    if (kotodama.match(/お.*ちち.*んん/))
        word_list << "OCHICHINN"
    elsif (kotodama.match(/ちち.*んん/))
        word_list << "ちちんん"
    end
    
    tripile_list = %w(ん こ う ま ち お).each_with_object([]) do|c,memo|
         m = kotodama.match(/#{c}{3,}/)
         memo << [c, m[0].size] if m
    end
    [face_list, word_list, tripile_list]
end

life = 3
dice_num = 5
nudge_num = 5
score = 0
roll = 0
word_combo = {}

loop do
    break if life == 0
    life -= 1
    roll += 1
    print "ROLL:#{roll} life: #{life} nudge:#{nudge_num} dice:#{dice_num}\n"
    (face_list, word_list, tripile_list) = [nil,nil,nil]

    loop do
        kotodama = dice_num.times.map{%w(ん こ う ま ち お).sample}.sort.join
    
        (face_list, word_list, tripile_list) = solve(kotodama)

        if word_list.size == 0 && nudge_num > 0
            nudge_num-=1
            print "nudge!(left: #{nudge_num})\n"
            next
        end
        break
    end

    life += 1 if word_list.size >= 1
    dice_num = 5
    dice_num += word_list.size - 1 if word_list.size >= 2
    nudge_num += word_list.size

    score += face_list.sum{|c|  {"ん"=>50,"こ"=>100,"う"=>500,"ま"=>500,"ち"=>500,"お"=>300}[c]  }
    word_list.each{|w|
        base_point = {"うちん"=>1000,"うこん"=>1000,"おこまん"=>5000,"こまん"=>1000,"こちん"=>1000,"OCHICHINN"=>10000,"ちちんん"=>3000}[w]
        score += base_point * 2**[word_combo[w]||0, 3].min
        dice_num = 10 if w == "OCHICHINN" 
    }
    word_combo = word_list.map{|w| [w, (word_combo[w] || 0) + 1] }.to_h
    
    tripile_list.each{|c,n|  
        base = {"ん"=>-3,"こ"=>1.5,"う"=>2,"ま"=>2,"ち"=>2,"お"=>1.5}[c]
        score *= (base + (base > 0 ? 1 : -1) * (n-3)).clamp(-4,4)
        score = score.abs if c == "ん"
    }

    print "score: #{score}\twords: #{word_list}\n"
end

print "gameover\n"

print "score: #{score}\n"

動作結果

実行してみる。

out.gif

停止しないんだが??

考察

操作を繰り返した際、長期的に見て 「消費するNUDGE」=<「得られるNUDGE」が成り立つならば、
終わりなくダイスを振り続けることができると思われる。

  • 5個のダイスを振ったとき、wordが完成する確率は約41%である
  • 1個以上のwordが完成した際得られるNUDGEの数は平均は約1.58個である

このとき、「1個以上のwordが完成するまでNUDGEし続けた場合に失うNUDGEの数の期待値」は約1.43個になり、
「消費するNUDGE」=<「得られるNUDGE」が成り立つ。

したがって、wordが1つ以上完成するまでNUDGEし続けるという戦略をとった場合、
十分な数のNUDGEが残っているという仮定の下ではNUDGEは長期的には増えていくことが予想され、ゲームは停止しなくなる。

実際はダイスの数が増える要素もあるので上の計算より高速にNUDGE数は増えるだろう。

> a=%w(ん こ う ま ち お).repeated_permutation(5).map{|kotodama|solve(kotodama.sort.join)[1].size}
# wordが完成する確率
> a.select{|x|x!=0}.size.to_f/a.size
=> 0.411522633744856

# 役が完成した際得られるNUDGEの数の平均
> b = a.select{|x|x!=0}
> b.sum(0.0)/b.size
=> 1.58125

# 1個以上のwordが完成するまでNUDGEし続けた場合に失うNUDGEの数の期待値
> (0..1000).sum{|i| i*(0.59**(i)*0.41) }
=> 1.4390243902439022

結論

  • プロではない人間がNKODICE遊ぶ際、ゲームが終了するのは運や長期的な収束の結果ではなくプレイヤーの力量が原因である可能性が高い
  • 練習しろ
17
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?