badazim’s blog

素人がプログラミングを勉強しながら覚え書きを書きます

Python(numpy)のよくわからないメモ

ダーツの分析でよくハマるところをメモ

まず最初は空配列を作るとき。
一次元配列の空配列は要素数を指定しなくていい。※0.75の意味がわからないが。。

>>> import numpy as np
>>> arrays = np.empty((0,3),int)
>>> print(arrays)
[]
>>> array = np.empty(3)
>>> print(array)
[0.75 0.75 0.  ]
>>> array = np.empty(0)
>>> print(array)
[]

次に1次元の空配列への要素の追加

>>> array=np.append(array,1)
>>> array=np.append(array,2)
>>> array=np.append(array,3)
>>> print(array)
[1. 2. 3.]

最後に二次元配列に一次元配列を挿入する。
np.insertの場合は挿入位置の指定が必要。axis=0をしないと一次元配列になる。

>>> arrays = np.insert(arrays,array,axis=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<__array_function__ internals>", line 4, in insert
TypeError: _insert_dispatcher() missing 1 required positional argument: 'values'
>>> arrays = np.insert(arrays,0,array,axis=0)
>>> print(arrays)
[[1 2 3]]

二次元配列の最後に挿入するならnp.vstackも使えそう。理由はわからないが二重括弧"(())"にする必要がある模様。
np.appendも使えた。これは配列の次元が一致していないとダメなのかな。np.array([一次元配列])としてあげたら使えた。

>>> array2 = np.array([4,5,6])
>>> arrays = np.vstack(arrays,array2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<__array_function__ internals>", line 4, in vstack
TypeError: _vhstack_dispatcher() takes 1 positional argument but 2 were given
>>> arrays = np.vstack((arrays,array2))
>>> print(arrays)
[[1 2 3]
 [4 5 6]]
>>> array3 = np.array([7,8,9])
>>> arrays = np.append(arrays,np.array([array3]),axis=0)
>>> print(arrays)
[[1 2 3]
 [4 5 6]
 [7 8 9]]
>>>

ダーツ戦略論③:ゼロワンゲームで採用するべき戦略に関する考察(その1)

ダーツ戦略論③:ゼロワンゲームで採用するべき戦略に関する考察(その1)

1.はじめに

本記事ではダーツゲームで最もポピュラーなゼロワンゲームの分析を行っていく。分析の前提事項やモデルについては、以下の記事を参照

badazim.hatenablog.com

 

2.残点数40点以下で1ラウンドでクリアできる確率の算出

 

3.残点数60点の上がり方(ブルを使用するべきか否か)

3.残点数90点の上がり方(ブル+ダブルナンバーのケース)

3.残点数110点の上がり方(ブル+トリプルナンバーのケース)

ダーツ戦略論②:クリケットで狙うべき位置とは

ダーツ戦略論②:クリケットで狙うべき位置とは

1.はじめに

クリケットゲームにおいて、あるナンバーを狙うときに効果は大きいがターゲット面積が狭いトリプル部分を狙うべきか、効果は小さいがターゲット面積が広いシングル部分を狙うべきか、という議論がある。

 

当然、一定以上の腕前のプレイヤーは原則としてトリプルの中心を狙うのが最も優れた戦略になると考えられる。下記はある程度高レベルのプレイヤーが20のトリプル・シングルそれぞれを狙って100本スローした結果をシミュレーションしたものである。(赤いマーカーが狙っている位置、青いマーカーがスローした結果を示す)

20のトリプルを狙った場合においても、20以外のターゲットに外れることはほとんどないことが分かる。

f:id:badazim:20200912181858p:plain
f:id:badazim:20200912181854p:plain
プレイヤーが上手な場合のシミュレーション

一方、そこまで上手ではないプレイヤーの場合、面積の狭いトリプルを狙うと狙ったターゲットに入らない可能性が高くなるため、面積の広いシングルターゲットを狙った方が期待値が高くなる可能性があると考えられる。

f:id:badazim:20200912182347p:plain
f:id:badazim:20200912182351p:plain
プレイヤーの腕前がそこまで高くない場合のシミュレーション

今回は、このような観点からプレイヤーの腕前や状況毎にクリケットナンバーのどの部分を狙うのが最も優れた戦略となるのかを分析していきたい。 

 

なお、分析の前提事項やモデルについては、以下の記事を参照

badazim.hatenablog.com

2.もっとも高いスタッツを得るために狙うべき位置は

まず腕前の高いプレイヤーより順にシミュレーション結果を示す。なお各グラフでは、左側のダーツボード上の赤線の位置を狙った際の平均的なスタッツ(3投した際のポイント数)のシミュレーション結果を横棒グラフで表現している。

f:id:badazim:20200915224230p:plain
f:id:badazim:20200915224233p:plain
左:Rating16(σ=13.8)  右:Rating13(σ=17.6)

次に中程度の場合

f:id:badazim:20200915224433p:plain
f:id:badazim:20200915224436p:plain
左:Rating10(σ=21.4) 右:Rating8(σ=24.8)

最後に比較的腕前が低い場合

f:id:badazim:20200915224857p:plain
f:id:badazim:20200915224900p:plain
左:Rating6(σ=29.8) 右:Rating4(σ=39.6)

わかったこと

・BBフライト(Rating8)程度のプレイヤーはトリプルリング内を狙うというより、トリプルリング寄りのシングルを狙った方が期待値が高くなる。狙い方を変えるだけでスタッツは0.1程度上昇することが見込める。

・Aフライト(Rating10)程度のプレイヤーであっても、トリプルリングの中心というより、トリプルリング内の極めてシングルよりの箇所を狙った方が期待値が高い。この狙い方でスタッツに0.2程度の違いがでてくる。

・AAフライトレベル以上のプレイヤーは感覚的にはトリプルリングの中心を狙う意識でほとんど問題ないように思える。

・Bフライトレベル以下のプレイヤーは逆に明確にトリプルではなくシングルを狙った方が期待値は高くなる

 

3.カットに成功する確率を最大化するために狙うべき位置は

大きく2戦略で比較する。1スローでカットを狙う戦略と地道にシングルを積み重ねていく戦略それぞれが1ラウンド内でカットができる確率はどちらが高いかをシミュレーションによって比較する。

※筆者の経験上、当時のダーツ界では、比較的低いレベルのプレイヤーは戦略②を採用することが推奨されていたように感じる。

戦略①:1スローでカットを狙う戦略

f:id:badazim:20200916234451p:plain

戦略②:地道にシングルを積み重ねていく戦略 ※戦略①との違いを赤字

f:id:badazim:20200916234529p:plain

 各腕前(SA,AA,A,BB,B,CCフライト)のプレイヤーが戦略①(積極戦略)と戦略②(地道戦略)それぞれでカットに成功する確率は以下の通りとなった。

f:id:badazim:20200922142520p:plain
f:id:badazim:20200922142530p:plain
左:戦略①(積極戦略) 右:戦略②(地道戦略)

戦略の異なりによりカットに要する本数は両戦略で異なるものの、1ラウンド内であるターゲットのカットに成功する確率は両戦略間でほとんど変わらないという結果を得られた。結果的にカットに要する本数が少ない戦略①(積極戦略)の方がより有効な戦略といえると考えられる。

ダーツ戦略論①:カウントアップの最適戦略

ダーツ戦略論①:カウントアップの最適戦略

1.はじめに

まず、第一回目はカウントアップの最適戦略を分析する。

分析のモデルについては、以下の記事を参照

 

badazim.hatenablog.com

 

一般的なプレイヤーはファットブルのルール(アウターブルも50点とする)では、常にブルを狙うのが正しいと言われている。

 

一方、トッププロを超えるようなあるレベルに達した以降は、20トリプルを狙った方が期待値が高くなるはずであるし、もしかしたらあるレベルのプレイヤーは比較的高得点のナンバーが並ぶボード左下側を狙った方が期待値が高くなるかもしれない。

 

この観点で検証するため、ダーツボードに縦横5mm毎の格子点(下図右側の青い点)を設定し、格子点毎に100回カウントアップを行った平均値を算出することで、格子点毎の期待値を求め、可視化することとする。

f:id:badazim:20200910225156p:plain
f:id:badazim:20200910225131p:plain
左:分析対象とするボード 右:5mm毎に格子点を設定し、期待値を求める

2.ファットブルルールにおける分析結果

4段階のダーツの腕前(CCフライト/Rating4、Bフライト/Rating6、BBフライト/Rating8、AAフライト/Rating13)にて、シミュレーションを行った結果は下記の通りとなった。

※PCのスペック上、試行回数が少ないため等高線が綺麗に出ていない部分があります

f:id:badazim:20200916222655p:plain
f:id:badazim:20200916222706p:plain
左:Rating4(σ=39.8) 右:Rating6(σ=29.8)
f:id:badazim:20200916222703p:plain
f:id:badazim:20200916222700p:plain
左:Rating8(σ=24.8) 右:Rating13(σ=17.6)

 

本シミュレーションにより、カウントアップの戦略について、以下のような示唆が得られた

・Rating4レベルのプレイヤーは、19トリプルを狙うのが最も期待値が高くなる。これは、このレベルのプレイヤーに対して、19の周囲が比較的高得点のターゲットが配置されていることが有利に影響しているためと考えられる。

・Rating6レベルのプレイヤーになると、ブルと19トリプル付近のどちらを狙ってもほぼ同じ程度の得点が期待できる。これはプレイヤーの腕前がこのレベルになると一定程度ブルを狙ってブルに入る確率が高まってくるためと考えられる。

・Rating8レベル以上のプレイヤーになってくると、前述の傾向がさらに強まり、明確にブルを狙うのが最も適切な戦略となる。この傾向はRating13となっても変わらないことが確認できる。

 

高得点のターゲット3点(Bullの中心,19トリプルの中心,20トリプルの中心)について、プレイヤーの腕前(σ)毎に期待値を散布図でグラフ化すると以下の通りとなった。

f:id:badazim:20200916225041p:plain

 基本的にはBullを狙うのが安定的に効果的な戦略となるが、Bullを狙ったカウントアップの平均点が1,180点を超えるような異次元のプレイヤーになってくると、20Tを狙った方が期待値が高くなることがわかる。また、CC~Cフライト上位のレベルでは、一時的に19Tを狙った方が期待値が高くなるが、これより低いレベルの場合、再びBullを狙った方が優位になっていることが確認できる。これはこのレベルのプレイヤーの場合、トリプルを狙った場合にアウトボードする確率が無視できなくなるためと考えられる。

3.ブルセパレートルールにおける分析結果

ファットブルルールではBullが優位すぎるため、あまり興味深い結果を得られなかったため、クリケットや一部のプロトーナメントでは01でも採用されているブルセパルール(アウターブルは25点) でも同様に分析を行う。

 

 

(分析中)

 

 

ダーツ戦略に関する確率的な分析(モデルの話)

ダーツ戦略に関する確率的な分析(モデルの話)

1.はじめに

過去、著者の趣味はソフトダーツであった。著者がダーツバーに入り浸っていた10年前のソフトダーツの世界では、過去より常連客によって言い伝えられてきたある種の「戦略(セオリー)」が連綿と受け継がれていた。例えば

  • Criketゲームにおいて、点数が優位な状態でカットを行う場合、3本シングルキープを狙うべし
  • 01ゲームでは、ブル+あるナンバーのトリプルとなるような数字が残った場合、トリプルを先に狙うべし

といったもので、当時はその「戦略(セオリー)」がどの程度正しいものかと疑問を覚えながらも先人に従っていたことを記憶している。

 

そこで、本カテゴリでは、ダーツゲームを確率的な観点から分析を行うことで、ダーツゲームの戦略の事実関係を整理することを目指したいと思う。

 

2.モデルについて

分析対象となるダーツゲームモデルを示す。

 ① 一般的なソフトダーツのボードサイズ(≠ハードボード)を前提とする。

 ② 特に断りのない限り、一般的なソフトダーツのルール(01やCountUP時は

   ファットブルCriketはセパブル)を前提とする。

 ③ レーティング等の指標はダーツライブ社の指標(Ratingは最低1、最高18の

   18段階、フライトはN~SAの8段階)を利用する。

 

上記の前提事項に加え、最も重要な仮説として

 ④ ダーツはプレイヤーの狙ったダーツボード上のある定点から正規分布に従う

   乱数を加えた点に到達(着弾)するものと仮定するものとする

この仮説について、次章で説明する。

3.狙った定点から正規分布を従う乱数を加えた点に到達する とは?

通常ダーツゲームにおいて、プレイヤーは二次元のダーツボード上のある定点(例えば、インブルの中心等)に狙いを定め、その上でスロー(矢を投げる行為)を行っている。

 

スローした結果、当然狙ったその定点(先ほどの例で言えば、インブルど真ん中の1ビット)そのものにダーツが到達する可能性もあるが、多くの場合ではダーツは狙った定点から数~数十ビットの「ズレ」が生じたダーツボード上の別の定点に到達することとなる。

 

今回の分析ではこの「ズレ」が正規分布という確率分布に従った乱数で表現できるものと仮定して考える。正規分布の詳細な説明は別の詳しい方に譲ることとしますが、下記の数式によって定義され、その確率分布は以下のグラフのように図示される。(Wikipediaより引用)

 

確率密度関数  

  {\displaystyle {\frac {1}{\sqrt {2\pi \sigma ^{2}}}}\;\exp \left(-{\frac {\left(x-\mu \right)^{2}}{2\sigma ^{2}}}\right)}

12πσexp[12σ2(xμ)2]12πσexp[12σ2(xμ)2]

正規分布は上図の通り、その数式中に保有するパラメータ「σ(シグマ)」によって、確率分布の裾の広さが規定される。簡単に言えば、「σ(シグマ)」が小さいほど中心からの「ズレ」は小さくなる可能性が高く、「σ(シグマ)」が大きいほど中心からの「ズレ」が大きくなる可能性が高くなるという傾向を持つ。

 

今回の分析では、二次元のダーツボード面において、狙ったある定点からx軸方向、y軸方向それぞれに同じ「σ(シグマ)」を持つ正規分布に従った乱数(=「ズレ」)を加えた定点に到達するものと仮定して、分析を行っていく。

f:id:badazim:20200908220352p:plain

イメージ図(インブルの中心を狙って18シングルへ「ズレ」たケース)

 ※要するに相関係数 ρ=0とする2変量正規分布に従った乱数ということだと

  理解しています。勘違いしていたらごめんなさい。

 

より視覚的に理解できるようにσが小さいプレイヤー(σ=20)と大きいプレイヤー(σ=40)それぞれがインブルの中心を狙って、100投スローした結果をシミューレションすると下記の通りとなる。

f:id:badazim:20200908220717p:plain
f:id:badazim:20200908220720p:plain
左:σ=20、右:σ=40

σが小さいプレイヤーの方がダーツが中心に集まっており(ダーツ用語でいうところの“グルーピング”している状態)、σが大きいプレイヤーの方が中心から離れた位置に到達することが多いという傾向が把握できると思います。 

4.「ダーツの上手さ(Rating)」と「σ(シグマ)」の関係

「3.狙った定点から正規分布を従う乱数を加えた点に到達する とは?」に示した通り、このモデルにおいては、正規分布のパラメータ「σ(シグマ)」は狙った位置からのダーツのずれやすさを意味する指標となり、これはダーツの世界におけるプレイヤーのダーツの上手さ(Rating)の概念とそのまま紐づけることができるものと考えられる。

 

参考:インブルを狙った場合の確率分布

f:id:badazim:20200908222911p:plain
f:id:badazim:20200908222914p:plain
f:id:badazim:20200908222907p:plain

ダーツライブ社の指標(http://help.dartslive.jp/data/data-004.html)をベースにRatingと「σ(シグマ)」を紐づけていくことを考える。

 

ダーツライブ社の指標では「① 01ゲームの平均スタッツ」、「②Criketゲームの平均スタッツ」の2つの組み合わせで「Rating」を決定している。

 

Criketゲームでは、ゲームの状況によって、広さの異なる様々なターゲット(ブルやクリケットナンバーのトリプル?シングル?)を狙う必要があり、本モデルでのシミュレーションに適さない。

 

一方、01ゲームでは、ゲーム終盤で上がり目がでるまでは、ほとんどすべてのケースでブルの中心を狙って投げていると考えられるため、01ゲームの平均スタッツを「インブルの中心を狙った場合の平均得点」と仮定すれば、Ratingと「σ(シグマ)」を紐づけることができる。

 

 ※勿論、厳密には01ゲームの平均スタッツには上がり目が出た後のブル以外の

  ターゲットを狙ったスローの結果も反映される点で両者は異なりますが、

  ダーツライブ社の01ゲーム80%スタッツであるため、そこまで極端に

  非現実的な仮定とはならないと考える

 

「インブルの中心を狙った場合の平均得点」と仮定したので、よりわかりやすくするため、8ラウンド連続でインブルの中心を狙った場合の合計得点(=カウントアップの結果)で考えます。本モデルにおいて、パラメータ「σ(シグマ)」を0から0.2刻みに40まで変更し、各シグマにてカウントアップを1,000回シミュレーションした場合の平均得点をグラフ化したものは以下の通りとなる。


※分かりやすくするため、フライトの境目(CCフライト400点:、Bフライト:480点等)に点線を入れています。

f:id:badazim:20200916220338p:plain

結果、「σ」と「Rating」の紐づけ結果を下表のとおり得ることができました。
 

f:id:badazim:20200916220036p:plain

長くなりましたが、今回の分析のモデルについての説明はここまでなります。

 

次回以降はここまでのモデルを前提として、個別論点ごとの分析を行いたいと思います。

kotlinでstartActivityForResultで別画面からデータを受け渡して戻ってくる

<遷移元(MainActivity)側の実装>

MainActivityから別画面へ遷移する際にstartActivityForResultへ変更する。2つめの引数は後述するOnActivityResultで遷移元画面を判断するためのコード値(requestCode)になっている。

ToOrderChange.setOnClickListener {

 (省略)

val intent = Intent(this, OrderChange2::class.java)
intent.putExtra("PlayerOrder",PlayOrderArray)
intent.putExtra("PointOrder",PlayOrderPoint)
//startActivity(intent)
startActivityForResult(intent,9)
}

 

startActivityForResultをoverrideする。とりあえず"9"以外のrequestCodeはありえないため、処理をしない。その上で戻るボタンではない(resultCode == Activity.RESULT_OK)場合で、null許容のIntent型のdataがnull出ない場合だけ処理してみる。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if(requestCode != 9) { return}

if (resultCode == Activity.RESULT_OK && data != null) {
val resultArray:Array<String>? = data.getStringArrayExtra("playOrder")
val pointArray:Array<String>? = data.getStringArrayExtra("pointOrder")
if (resultArray != null && pointArray != null){

       (処理は省略)

}

} else if(resultCode == Activity.RESULT_CANCELED) {
textView0_A.text = "戻るボタン"
}
}

 

<遷移遷移先(2つめのActivity)側の実装>

もともとstartActivityだったところをsetResultとfinish()で置き換えて実装できた。

Restart.setOnClickListener {
val intent = Intent(this,MainActivity::class.java)
val update_resultArray = arrayOf(
省略 )
val update_pointArray= arrayOf( 省略 )
intent.putExtra(
"playOrder",update_resultArray)
intent.putExtra(
"pointOrder",update_pointArray)

setResult(Activity.
RESULT_OK, intent)
finish()
//startActivity(intent)
}

 

ちなみにここで配列がnullになってしまい1時間はまった。

val resultArray:Array<String>? = data.getStringArrayExtra("playOrder")
val pointArray:Array<String>? = data.getStringArrayExtra("pointOrder")

 

遷移先(ここではMainActivity側)のgetStringArrayExtraの使い方に問題があるものと思い込んだけど実際には遷移元(2つめのActivity)側でputExtraするときの配列の定義がarrayListofを使っていたので、arrayList型になっていただけだった。android studioがちゃんと型を表示してくれているので、ちゃんと確認するようにしよう。。。

val update_resultArray = arrayListOf( 省略 )
val update_pointArray= arrayListOf( 省略 )

 

 

kotlinでRecyclerViewを使ってみる(その2)

 ItemTouchHelperのonMove()で

notifyItemMoved(fromPostion,toPosition)

をしただけだと画面の表示しか変わらない。

 

実際に順番を入れ替えた配列を作るには

override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val fromPostion = viewHolder?.adapterPosition ?:0
val toPosition = target?.adapterPosition ?:0

(MyRecyclerView.adapter as MyRecyclerViewAdapter).notifyItemMoved(fromPostion,toPosition)
val moto:RowData = dataset[toPosition]
dataset[toPosition] = dataset[fromPostion]
dataset[fromPostion] = moto

return true
}

みたいなことをする必要がある。