テツポンドのブログ

テツポンドのブログ

テツポンド(Twitter : https://twitter.com/tetspond)のブログです。

【ポケモン剣盾】ランクバトル・マスターボール級に上がるための勝率について、ポイントとランクの仕組みを踏まえてさらに調べてみる【数学・プログラミング】

マスターボール昇格の瞬間(今期ダブル・レパルブリムツボラキの禁伝なし4匹構築で達成)

 

はじめに

 

 ポケットモンスター ソード・シールドのランクバトル(通称:ランクマ)のシステムに関して、数学・プログラミングの視点から考える記事の第2弾です。

 先週書いた第1弾より、さらに実際のランクバトルのシステムに合わせた内容となっています。

 

先週公開した第1弾

tetspond.hatenablog.com

 

 

 

 

前回記事の振り返りとその後

 

 前回の記事では、構造を単純化して考えていました。

 ランクの上下は無視して、両方向ともに終わりのない数直線上をポイントの上下をもとに移動していくという構造で考えていたのです。

 

 

 記事公開後も、記事内に登場した数学が得意な方(以下、「先輩」と呼称します)とまだこの話をしていました。その中で、ランクバトルの構造について話題になりました。

 

 その結果、既に分かっていた

・勝ったときは1ポイント ただし連勝であるなら1ポイントではなく3ポイント

・負けたときは-1ポイント

・ハイパーボール級では9ポイント貯まるとランクが上がる

・スーパーボール級では8ポイント貯まるとランクが上がる

・ビギナー級では負けてもポイントが減らない

・ゲージが0の状態で負けるとランクが下がり、一つ下のランクの「ランク昇格まであと1ポイント」という状態になる ただし、〜〜級の中で一番低いランク・ゲージが0の状態で負けても、ランクが落ちることはない

の他に、

・ランクアップ時には1ポイントがボーナスとして付与される(なお、付与されるのは1試合につき1回だけ)

モンスターボール級では6ポイント貯まるとランクが上がる

・ビギナー級では3ポイント貯まるとランクが上がる

が分かりました。

(以上のことは、全て結果の解釈をしているだけです。ただ、起きていることを全てこの解釈で説明できるので、問題なさそうです。)

 

<根拠>

 この動画で先輩が調べてくださりました。

【ポケモン剣盾】3カ月放置したサブロムがビギナー級になってて涙 とりまスパボくらいまで上げます - YouTube

 ランクバトルの1試合目からの様子が収められていました。

勝ち ビギナー級ランク1ゲージ1/3

勝ち ビギナー級ランク2ゲージ2/3

勝ち モンスターボール級ランク4ゲージ0

負け モンスターボール級ランク4ゲージ0

勝ち モンスターボール級ランク4ゲージ1/6

勝ち モンスターボール級ランク4ゲージ4/6

勝ち モンスターボール級ランク5ゲージ2/6

 この後、勝つとゲージ+1/6,連勝で+3/6,負けると-1/6でモンスターボール級をうろうろしました。

 序盤のポイントの増減は、+1, +4, +4, ±0, +1, +3, +4 となっています。3試合目勝利のとき、連勝時の3ポイントに加えてランク3に上がったことによる1ポイントでランク4まで一気に上がっていますが、ランク4に上がったことによる1ポイントはついていません。このことから、ランクアップボーナスは1試合につき1度きりだということが分かります。

 

 

 

ランクバトル・階級・ランクの全体像

整理します。

 

 

勝ったとき 連勝なら3ポイント獲得 それ以外なら1ポイント獲得

負けたとき 1ポイント減少

ランクが上がったとき 追加で1ポイント獲得(1試合につき1度まで)

 

5つの階級と11のランク(マスターボール級を除くと、4つの階級と10のランク)がある。

<マスターボール級>

ランクMAX

<ハイパーボール級> 9ポイント貯まるとランク上昇

ランク10

<スーパーボール級> 8ポイント貯まるとランク上昇

ランク7,8,9

モンスターボール級> 6ポイント貯まるとランク上昇

ランク4,5,6

<ビギナー級> 3ポイント貯まるとランク上昇

ランク1,2,3 負けてもポイント減少なし

 

階級は落ちることがない

ランクは落ちることがある(階級の中で一番低いランクではないランクの中で一番ポイントが少ない状態で負けると、1つ下のランクに落ちその中で一番ポイントが多い状態になる)

 

 

 ランクが上がったときに1ポイント追加されることを、この記事では「ランクアップボーナス」と呼ぶことにします。

 また、階級が落ちることはないという仕組みを、この記事では「打ち止め」と呼ぶことにします。

 ゲージという言葉が出てくるとき、それは、「現在のそのランクでの獲得ポイント / 一つ上のランクに上がるためのポイント」を指します。これが満タン=1になると、ランクアップです。

 

 

 

今回の記事でしたいこと

 

前回の記事では、単純化した構造でポイントが上昇していくための条件を考えていました。

今回は、ランクの上下・ランクアップボーナス・打ち止めも考慮して、マスターボール級に上がるための条件について調べてみます。

 

それをするにあたり

前回は、数直線上でポイントが上昇していくための勝率を数学的に導いていましたが、

今回は、数学的に導くのは複雑さにより難しそうなので、それは諦める方針にします。

先輩も「さすがにこれらを加味した勝率計算は酷ですね
大人しくプログラミングに任せるべきと思いました」と言っていました。

 

 

 

プログラミング

 

 後で最終的なコードを載せますが、先輩が作ってくださりました。乱数によって勝敗を決め、ポイントの計算をし、ランクの上下を行うというやり方になっています。

 

<実行結果の例> 勝率0.16 100万試合実施

勝率 0.16 で 3 シーズン戦ってみるね。
マスターボール級に到達!!到達までの試合数は 293997 だよ!
マスターボール級に到達!!到達までの試合数は 37418 だよ!
残念!試合数が  1000000  で,今のランクは 7 だよ!

勝率 0.16 で 3 シーズン戦ってみるね。
マスターボール級に到達!!到達までの試合数は 162841 だよ!
マスターボール級に到達!!到達までの試合数は 103707 だよ!
マスターボール級に到達!!到達までの試合数は 255177 だよ!

勝率 0.16 で 3 シーズン戦ってみるね。
マスターボール級に到達!!到達までの試合数は 361520 だよ!
マスターボール級に到達!!到達までの試合数は 444345 だよ!
マスターボール級に到達!!到達までの試合数は 60903 だよ!

勝率 0.16 で 3 シーズン戦ってみるね。
マスターボール級に到達!!到達までの試合数は 152578 だよ!
残念!試合数が  1000000  で,今のランクは 7 だよ!
マスターボール級に到達!!到達までの試合数は 506438 だよ!

 

 12シーズン分だけしか載せていませんが、勝率16%でも100万試合を行うならほぼマスターボール級に到達できそうだという、驚きの結果が得られました。前回記事では36.6%を超えればポイントが溜まっていくという話だったので、全然違う結果です。

 

筆者「底の打ち止め(とランクアップボーナス)でこんなに違うのですね。」

先輩「打ち止めを考慮すると,期待値うんぬんよりも「試行回数を増やして上振れを狙う」が優先されますね」

先輩「いくらか検証していましたが,やはり鬼門はスーパーボール級のように思えました。」

筆者「モンボ級なら18上振れればいけますが、スパボ級だと24上振れなければいけないので、ということですね。」

(試合数の問題で、モンボは間に合うけどスパボまでは間に合わないことがある、というほうが大きい理由かもしれません)

 

 

二人で話しているうちに、簡単なコードをオンラインで実行するのに使っていた、paiza.ioの仕組みもついでに推測できました。

・何かコードを書くと、それがURLとともに保存される

cookieはその人が作成したという情報を保存していて、作成者だけが大元のコードを編集できる

・作成者以外がそのコードのURLを開いた場合、作成者以外がコードを開いているということが分かっていて、何か編集をすると、新しいコードとして新しいURLが生成される。

 

 

ちなみに、勝ち続ける最短ルートを出すとこうなります。

勝率 0.99 で 1 シーズン戦ってみるね。
1 試合目は勝利!(現在のランク: 1 ,ゲージ: 1 /3
2 試合目は勝利!(現在のランク: 2 ,ゲージ: 2 /3
3 試合目は勝利!(現在のランク: 4 ,ゲージ: 0 /6
4 試合目は勝利!(現在のランク: 4 ,ゲージ: 3 /6
5 試合目は勝利!(現在のランク: 5 ,ゲージ: 1 /6
6 試合目は勝利!(現在のランク: 5 ,ゲージ: 4 /6
7 試合目は勝利!(現在のランク: 6 ,ゲージ: 2 /6
8 試合目は勝利!(現在のランク: 6 ,ゲージ: 5 /6
9 試合目は勝利!(現在のランク: 7 ,ゲージ: 3 /8
10 試合目は勝利!(現在のランク: 7 ,ゲージ: 6 /8
11 試合目は勝利!(現在のランク: 8 ,ゲージ: 2 /8
12 試合目は勝利!(現在のランク: 8 ,ゲージ: 5 /8
13 試合目は勝利!(現在のランク: 9 ,ゲージ: 1 /8
14 試合目は勝利!(現在のランク: 9 ,ゲージ: 4 /8
15 試合目は勝利!(現在のランク: 9 ,ゲージ: 7 /8
16 試合目は勝利!(現在のランク: 10 ,ゲージ: 3 /9
17 試合目は勝利!(現在のランク: 10 ,ゲージ: 6 /9
18 試合目は勝利!(現在のランク:MAX)
マスターボール級に到達!!到達までの試合数は 18 だよ!

 

 

 

グラフで観察

 

 勝率を変えながらシーズンを100万回行って、ヒストグラムにしていただきました(実行には結構時間がかかります 特に勝率が低くて1シーズンあたりの試合数が多くなればなるほどに)。横軸が、ビギナー級からマスターボール級に到達するまでにかかった試合数です。

 毎試合の勝敗判定は乱数でやっているので、実行するごとに多少は変化がありますが、試合数もシーズン数も多いので、ほぼ同じ結果になるはずです。

 

(追記)

 以下の観察ではその当時のコードのミスにより99万9999シーズンと1シーズン欠けてしまっていたことが判明しました。結果にほぼ影響がないのですが、Season=100万はSeason=99万9999に読み替えてください。

 

 

 

勝率 50%

 

勝率p=0.5,Season=100万

ヒストグラムの階級の幅 1

グラフ1

平均値 : 86.185558
最大値 : 360 最小値 : 18 最頻値 : 73

(最大値はグラフの画像の外に飛び出ています)

 

(追記:中央値をここだけ出してなかったので、再度試行を行い、出しました。

平均値 : 86.116533
最大値 : 401 最小値 : 18 中央値 : 82.0 最頻値 : 70)

 

 

 

筆者「上昇させるべきゲージが60であるのに対して、平均86、なるほどです」

(補足: ビギナーで3*3,モンスターで6*3,スーパーで8*3, ハイパーで9, この合計が60)

先輩「いつぞや計算した期待値Enでは、勝率1/2の場合に
En=n/2-1/2
であるため、60ポイント稼ぐためには121試合程度行うことが期待されますが、86程度でマスターボール級に行けてしまっているので、"底の打ち止め"の威力を感じますね」

 

 

 

勝率 約36.6%

 

勝率p=0.3660254037844386,100万シーズン,
ヒストグラムの階級幅 1

この勝率は、前回記事にて、単純化した構造においてポイントが上昇していくために超える必要がある勝率として求められた勝率です。小数表記になっていますが、(√3-1)/2のことです。

グラフ2

平均値 : 294.097442
最大値 : 1920 最小値 : 22 中央値 : 262.0 最頻値 :196

(最大値はグラフの画像の外に飛び出ています)

 

筆者「体感の必要勝率が3割後半だったのは、対戦できる回数の限界が200あたりだったからというのもあるようです」

 

 

 

勝率 約25%

 

勝率p=0.25,100万シーズン,

 

 ミスがある状態(マスター到達難易度が下がる方向に働くミス)で実行したときは、先輩のノートパソコンで3時間かかって結果が出ていたのですが、ミス修正後に実行して8時間近く経っても結果が出ないので、ここはあとで追記というかたちにします。

 

(追記)

 12時間かけて、できました

グラフ3

平均値 : 6248.75898
最大値 : 78752 最小値 : 65 中央値 : 4785.0 
最頻値 :1711,2212

 

 勝率約0.366のときに比べて、平均値がだいたい300→6000と20倍に増えています。とてもマスターボール到達が難しくなるという比較結果が得られました。

 

 

 

勝率はどんなに低くてもいいのか

勝率1割台でも、めちゃくちゃ多い試合数をこなしてマスターボール級に上がれることがあるという実行結果を見て思ったことです。

 

会話

 

筆者「正の勝率であればどんなに小さい勝率でも有限回の試合数でマスターに上がれるのでしょうか」

 

先輩「勝率が正である限り,あきらめなければ必ず上振れは引けると思いますよ、あきらめなければ」

先輩「有限回という言葉がどこまで許すかは分かりません(例えば10月シーズン寝ずに24時間やったところで,31日=744時間なので,1試合10分と仮定しても4464試合しかできないわけですし)が,
勝率が正の定数である限り(天文学的数字になろうとも)有限回でなんとかなるはずです」

 

筆者「なんとかなる の証明は可能でしょうか」

 

先輩「例えば,ハイパーボール級ゲージ0で4連勝すればマスターボール級に到達できるわけですが,
こうなる確率は,勝率をpとすればp^4になります。

n回試合を行ったとき,4連勝する回数の期待値は(n-3)*p^4ですので,n-3が1/p^4よりも大きければ,期待値的にはマスターにたどり着くことが期待されます」

先輩「例えばp=10^{-10}だったとしたら,
n=10^{40}(10正)
程度試合を行えば,“期待値的には”マスターに行けるはずです」

 

 

 シーズンが一ヶ月であるということを踏まえた、物理的な制約が存在するのが面白いです。不眠不休で試合をして、かなりサクサク試合が進んでも5000試合くらいが限度ということでしょう。

 

 

証明

ここでは物理的な時間の制約は無視します。

 

 試合数が多くなると、どんな正の勝率であってもマスターボール級に到達する可能性は1に限りなく近くなるということの証明の一部を先輩が書いてくださりました。勝率pは正の定数です。

 

=====

8連勝する確率はp^8
逆に,(8k-7)試合目から(8k)試合目まで8連勝"しない"確率は1-p^8となります。
kが1以上n以下のとき,そのすべてで8連勝しない確率は
(1-p^8)^n
となります。
1-p^8<1なので,
lim{n→∞}(1-p^8)^n=0
となるため,8連勝しない確率は0に収束します。

したがって,8連勝する確率は1に限りなく近づくことになります。

=====

(ここから先は、自力で考えつつ先輩に教えてもらいながら続きを書きました。)

 

 ここまでで、8連勝する確率は1に限りなく近づくことが示されたので、今度は「8連勝する⇒ハイパーボール級にあがる」を示します。

 

 昇格から一番遠い「スーパーボール級ランク7、ゲージ0(=ランク7の中で一番ポイントがひくい)」「前の試合は負けている」というケースを考えます。

このときでさえ、

1試合目 勝ち +1 ランク7ポイント1

2試合目 勝ち +3 ランク7ポイント4

3試合目 勝ち +3 ランク7ポイント7

4試合目 勝ち +3 ランク8ポイント3

5試合目 勝ち +3 ランク8ポイント6

6試合目 勝ち +4 ランク9ポイント2

7試合目 勝ち +3 ランク9ポイント5

8試合目 勝ち +3 ランク10

と8連勝すればハイパーボール級に上がれます。

 一番悪いところからでも8連勝すれば上がれるということは、スパボ級をうろうろしている間にどこかのタイミングで8連勝が発生すれば、どのような状態からその8連勝が始まったとしてもハイパーに上がれます(ここの説明はざっくりしすぎていてもしかしたらちゃんとした証明ではないかもしれない、と思っていましたが、「ざっくりとしているけれども,ちゃんと根拠を示した正しい推論です」とのことなので大丈夫そうです)

 よって「8連勝する⇒ハイパーボール級にあがる」が成り立ちます。

 

 「8連勝する⇒ハイパーボール級にあがる」が成り立つとき、「8連勝する確率<ハイパーボール級にあがる確率」が成り立ちます。そして8連勝する確率が1に限りなく近づくのだから,はさみうちの原理からハイパーボール級に上がる確率も1に近づきます。

 

 

 他の階級昇格についてもこれと同様に昇格する確率が1に限りなく近づくので、ビギナー級からマスターボール級に昇格する確率は勝率pに依らず1に限りなく近くといえます。

 

 つまり、試合を本当にいくらでも行えるなら、勝率は正でありさえすれば良いということです。

 

 

 

現実的な試合数なら、どのくらいの勝率が必要か考える

ざっくりとした思考です。

 

月50時間戦う場合

 

 1シーズンの30日のうち、平日20日は一日1時間、休日10日は一日3時間、ランクバトルをするとします。「よく対戦する社会人」くらいです。このとき、月の合計は 1*20+3*10=50、50時間=3000分になります。

 1試合10分とすると、月に300試合です。

 

 ここで、先ほど載せたグラフ2を再び載せます。勝率約36.6%で100万シーズン戦ったときの、階級幅1のヒストグラムです。

グラフ2 再掲

平均値 : 294.097442
最大値 : 1920 最小値 : 22 中央値 : 262.0 最頻値 :196

 

 中央値が262であることから、だいたい100万のうち半分の50万シーズンくらいは262試合以下でマスターに到達できています。これはもう一度プログラムを実行しても大きくは動かないはずです。

 よって(厳密には示せていませんが)、勝率約36.6%で300試合を戦えば、マスターに上がれることのほうが多いと言ってよいでしょう。

 

 

 

月300時間戦う場合

 

 月30日、1日あたり10時間、よって月に300時間対戦をし続ける、文字どおりのポケモン廃人を考えます。300時間は18000分です。

 1試合にかかる時間を10分とします(Youtubeポケモン動画は、だいたい2試合なら20分程度、3試合なら30分程度となっている感覚があります)。

 1シーズンは1ヶ月間とします。

 

 すると、1ヶ月に戦える試合の数は、18000/10で1800試合になります。

 

 先ほどのグラフを見ると、ほぼ全てのシーズンで1800試合以内にマスターボール級に到達しているので、勝率36.6%程度ならほぼ確実にマスターに行けるといえます。

 

 本当は勝率0.25のグラフを出して比較したかったのですが、100万シーズン実行にめちゃくちゃ時間がかかっているので、ここは後で追記することにします。

(追記)

グラフ3 再掲

 グラフを見ると、マスター到達まで1800試合以上かかっている試合が多いです。勝率0.25で1800試合を戦う場合、マスターに到達できないシーズンのほうがかなり多いといえます。

 

 

 

コード

 

 先輩作成のコードをここに掲載します。

 先輩には画像、コードなどなどブログに載せることを快諾していただきました。ありがとうございます。

 

お手軽実験ver

https://paiza.io/projects/o_MeteG8SWCxuGn4kHyukg?theme=xcode

に飛ぶと、コードが貼り付けられていて実行できるはずです(仕組みをよく分かっていないので、もしかしたらできないかもしれません)。設定を変えることもできるはずです。

 

import random
####
#### 初期設定(変えるならここ)
Rank = 1    # 初期ランク
Gauge = 0   # 初期ゲージ(普通は0だが)
p = 0.60    # 勝率
Season = 3  # 戦うシーズン数
n = 10000   # 1シーズンあたりの試合数 

#### タイムアウトの都合上,
#### Season*n を2500000以下にすることが望ましいです。


####ここから定義
a = False # ex-turn:Lose (for win streak bonus)
rank = Rank   
gauge = Gauge 

## 前提
## ビギナークラス:ランク1からランク3まで:1/3刻み:負け点なし
## ノーマルボール:ランク4からランク6まで:1/6刻み
## スーパーボール:ランク7からランク9まで:1/8刻み
## ハイパーボール:ランク10のみ:1/9刻み
## 各クラス間の降格は無し
## 引き分けは考えない
## ポイントの上がり方は "https://tetspond.hatenablog.com/entry/2022/10/23/175601#ポイントの上がり方" を参照。

## 試合処理(ビギナー以外)
def match(gauge , a , p) : 
    x = random.random()
    if x < p :
        if a :
            gauge = gauge + 3
        else:
            gauge = gauge + 1
            a = True
    else :
        gauge = gauge - 1
        a = False
    
    return gauge , a , p

## 試合処理(ビギナー)
def matchB(gauge , a , p) : 
    x = random.random()
    if x < p :
        if a :
            gauge = gauge + 3
        else:
            gauge = gauge + 1
            a = True
    else :
        #gauge = gauge - 1 (Beginner)
        a = False

    return gauge , a , p



# ビギナークラス
def MatchBeginner( rank , gauge , a , p ) :
    gauge , a , p = matchB(gauge , a , p)
    if gauge >= 3 :
        rank = rank + 1
        gauge = gauge - 2   # ランクアップボーナスを加味
    
    if gauge >= 3 :
        rank = rank + 1
        gauge = gauge - 3   # 2ランクアップはボーナス無し
    
    return rank , gauge , a , p

# モンスターボールクラス
def MatchMonster( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    if gauge >= 6 :         # モンボ級は1/6刻み
        rank = rank + 1
        gauge = gauge - 5   # ランクアップボーナスを加味
    
    if gauge < 0 :          # ランクダウンに関する処理
        if rank == 4 :
            gauge = 0
        else : 
            rank = rank - 1
            gauge = 5
    
    return rank , gauge , a , p


# スーパーボールクラス
def MatchSuper( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    if gauge >= 8 :         # スパボ級は1/8刻み
        rank = rank + 1
        gauge = gauge - 7   # ランクアップボーナスを加味
    
    if gauge < 0 :          # ランクダウンに関する処理
        if rank == 7 :
            gauge = 0
        else : 
            rank = rank - 1
            gauge = 7    
    
    return rank , gauge , a , p

# ハイパーボールクラス
def MatchHyper( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    gauge = max(gauge , 0)
    if gauge >= 9 :         # ハイボ級は1/9刻み
        rank = rank + 1
        gauge = 0

    return rank , gauge , a , p
               

# rankに応じて,どのクラスでの処理を与えるか場合分け
def RankMatch( rank , gauge , a , p ) : 
    if   rank < 4 :
        rank , gauge , a , p = MatchBeginner( rank , gauge , a , p )
    elif rank < 7 :
        rank , gauge , a , p = MatchMonster( rank , gauge , a , p )
    elif rank < 10 :
        rank , gauge , a , p = MatchSuper( rank , gauge , a , p )
    else :
        rank , gauge , a , p = MatchHyper( rank , gauge , a , p )

    return rank , gauge , a , p

# 動作確認用 ; 勝敗の推移をOutputに表示
def MatchResult( rank , gauge , a , n):
    if   rank < 4 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/3")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/3")
    elif rank < 7 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/6")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/6")
    elif rank < 10 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/8")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/8")
    else :
        if a :
            if rank == 11 :
                print( n , "試合目は勝利!(現在のランク:MAX)" )
            else :
                print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/9")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/9")



##### ここまで定義
##### ここから実行

print("勝率",p,"で" , Season ,"シーズン戦ってみるね。" )
for j in range(Season) :    
    for i in range(n) : 
        rank , gauge , a , p = RankMatch( rank , gauge , a , p )

        ## 勝敗の推移をOutputしたければ,次の行のコメントアウトを外してください。
        MatchResult( rank , gauge , a , i + 1)
        ##
        
        if rank == 11 :
            print("マスターボール級に到達!!到達までの試合数は" , i + 1 ,"だよ!")
            break
    
    if rank < 11 :
        print("残念!試合数が " , n ," で,今のランクは" , rank , "だよ!")
    
    rank = Rank   
    gauge = Gauge
    a = False

 

 

グラフ描画ver

https://paiza.io/projects/qS5bDOWU9e4zwnEidgF3AA?language=python3

 

import random
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd


#### 初期設定(変えるならここ)
Rank = 1        # 初期ランク
Gauge = 0       # 初期ゲージ(普通は0)
p = (np.sqrt(3)-1)/2         # 勝率
Season = 1000000  # 戦うシーズン数
n = 1000000       # 1シーズンあたりの試合数
## ヒストグラムの出力用
Haba = 1        # ヒストグラムの階級幅
MaxHist = 1200   # ヒストグラムの横軸の最大値

array = []

####ここから定義
a = False # ex-turn:Lose (for win streak bonus)
rank = Rank   
gauge = Gauge 

## 前提
## ビギナークラス:ランク1からランク3まで:1/3刻み:負け点なし
## ノーマルボール:ランク4からランク6まで:1/6刻み
## スーパーボール:ランク7からランク9まで:1/8刻み
## ハイパーボール:ランク10のみ:1/9刻み
## 各クラス間の降格は無し
## 引き分けは考えない
## ポイントの上がり方は "https://tetspond.hatenablog.com/entry/2022/10/23/175601#ポイントの上がり方" を参照。

## 試合処理(ビギナー以外)
def match(gauge , a , p) : 
    x = random.random()
    if x < p :
        if a :
            gauge = gauge + 3
        else:
            gauge = gauge + 1
            a = True
    else :
        gauge = gauge - 1
        a = False
    
    return gauge , a , p

## 試合処理(ビギナー)
def matchB(gauge , a , p) : 
    x = random.random()
    if x < p :
        if a :
            gauge = gauge + 3
        else:
            gauge = gauge + 1
            a = True
    else :
        #gauge = gauge - 1 (Beginner)
        a = False

    return gauge , a , p



# ビギナークラスのランクアップ処理
def MatchBeginner( rank , gauge , a , p ) :
    gauge , a , p = matchB(gauge , a , p)
    if gauge >= 3 :
        rank = rank + 1
        gauge = gauge - 2   # ランクアップボーナスを加味
    
    if gauge >= 3 :
        rank = rank + 1
        gauge = gauge - 3   # 2ランクアップはボーナス無し
    
    return rank , gauge , a , p

# モンスターボールクラスのランク処理
def MatchMonster( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    if gauge >= 6 :         # モンボ級は1/6刻み
        rank = rank + 1
        gauge = gauge - 5   # ランクアップボーナスを加味
    
    if gauge < 0 :          # ランクダウンに関する処理
        if rank == 4 :
            gauge = 0
        else : 
            rank = rank - 1
            gauge = 5
    
    return rank , gauge , a , p


# スーパーボールクラスのランク処理
def MatchSuper( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    if gauge >= 8 :         # スパボ級は1/8刻み
        rank = rank + 1
        gauge = gauge - 7   # ランクアップボーナスを加味
    
    if gauge < 0 :          # ランクダウンに関する処理
        if rank == 7 :
            gauge = 0
        else : 
            rank = rank - 1
            gauge = 7    
    
    return rank , gauge , a , p

# ハイパーボールクラスのランク処理
def MatchHyper( rank , gauge , a , p ) :
    gauge , a , p = match(gauge , a , p)
    gauge = max(gauge , 0)
    if gauge >= 9 :         # ハイボ級は1/9刻み
        rank = rank + 1
        gauge = 0

    return rank , gauge , a , p
               

# rankに応じて,どのクラスでの処理を与えるか場合分け
def RankMatch( rank , gauge , a , p ) : 
    if   rank < 4 :
        rank , gauge , a , p = MatchBeginner( rank , gauge , a , p )
    elif rank < 7 :
        rank , gauge , a , p = MatchMonster( rank , gauge , a , p )
    elif rank < 10 :
        rank , gauge , a , p = MatchSuper( rank , gauge , a , p )
    else :
        rank , gauge , a , p = MatchHyper( rank , gauge , a , p )

    return rank , gauge , a , p

# 動作確認用 ; 勝敗の推移をOutputに表示
def MatchResult( rank , gauge , a , n):
    if   rank < 4 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/3")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/3")
    elif rank < 7 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/6")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/6")
    elif rank < 10 :
        if a :
            print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/8")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/8")
    else :
        if a :
            if rank == 11 :
                print( n , "試合目は勝利!(現在のランク:MAX)" )
            else :
                print( n , "試合目は勝利!(現在のランク:" , rank , ",ゲージ:" , gauge , "/9")
        else :
            print( n , "試合目は負け。(現在のランク:" , rank , ",ゲージ:" , gauge , "/9")



##### ここまで定義
##### ここから実行

print("勝率",p,"で" , Season ,"シーズン戦ってみるね。" )
for j in range(Season) :    
    for i in range(n) : 
        rank , gauge , a , p = RankMatch( rank , gauge , a , p )

        ## 勝敗の推移をOutputしたければ,次の行のコメントアウトを外してください。
        #MatchResult( rank , gauge , a , i + 1)
        ####################################################################
        
        if rank == 11 :
            #print("マスターボール級に到達!!到達までの試合数は" , i + 1 ,"だよ!")
            break
    
    #if rank < 11 :
    #    print("残念!試合数が " , n ," で,今のランクは" , rank , "だよ!")
    
    rank = Rank   
    gauge = Gauge
    a = False
    array.append( i + 1 )

#print(array)

# グラフの装飾
plt.xlim(0, MaxHist)
plt.grid(True)                              
plt.tick_params(labelsize = 12)             

sns.histplot(array ,binwidth=Haba )
plt.savefig("histgram.png")
print("平均値 :", sum(array) / len(array))
print("最大値 :", max(array),"最小値 :", min(array),"中央値 :",np.median(array),"最頻値 :",pd.Series(array, name='values').mode())

plt.show()

 

 

 

おわりに

 

 SV発売までの記事公開予定はもう立ててあったのですが、急遽予定を変更して今回の記事を書きました。面白い発見が多かったことや、前回の単純化した構造だけでおわりにするのは誠実ではないと思ったことなどから、書くことにしました。

 

 

 2022/10/25に本ブログの通算アクセス数が8万を突破しました!

 読んでくださる方々、ありがとうございます。

 

 最近の一番アクセスが多いのはGoogle経由のこの記事です。8月に公開したばかりなのに、テラスタルの影響でしょうか。

【ポケモン】3つ目のタイプがあるとき最も弱点・耐性が多いのはそれぞれどんなタイプかプログラミングで調べてみる - テツポンドのブログ

 

 

宣伝 来週仲間大会を開催します。ご参加お待ちしています!

tetspond.hatenablog.com

 

 

検証 カテゴリーの記事一覧 - テツポンドのブログ

実戦(構築記事など) カテゴリーの記事一覧 - テツポンドのブログ

 

Twitterアカウント

テツポンド (@tetspond) | Twitter 

 

16161字