Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What is going on with this article?
@Fuyutsubaki

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

動機

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遊ぶ際、ゲームが終了するのは運や長期的な収束の結果ではなくプレイヤーの力量が原因である可能性が高い
  • 練習しろ
9
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
dwango
Born in the net, Connected by the net.

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
9
Help us understand the problem. What is going on with this article?