読者です 読者をやめる 読者になる 読者になる

Mukku John Blog

取り組んでいること を つらつら と

Rプログラミング入門 14回目

これからの数回は、スロットマシンのシミュレーターをRで作成する過程を通じて、
これらを学んでいきます。

  1. プログラムの設計方法
  2. 分岐処理の書き方
  3. 反復処理の書き方
  4. ルックアップテーブルの作り方、使い方
  5. S3メソッドの使い方

ちなみに、スロットマシンのモデルはこれ。
Journal of Statistics Education, V3N2: Braun
(本にも記載してありますが、1ドルに対しての払い戻し率に色々とあった模様)

ゴール

play関数を呼ぶと、下の2つが表示される事を目指します。

  • シンボル
  • 賞金
> play()
B 0 BBB
$0

プログラムの設計方法

どんなプログラミング言語でも同じですが、ルールや目的など、
プログラミングをする前に、決めないといけません。

スロットマシンが提供するシンボルはこちらです。

> symbols
[1] "DD"  "7"   "BBB" "BB"  "B"   "C"   "0"  

スロットマシンは、これらに該当する時に賞金が得られます。

  1. 同じシンボルが3つ揃った時(ゼロ3つは除く)
  2. 3つのバー(さまざまな種類のバーが混ざっていても良い)
  3. 1個以上のチェリー

この時点で、分岐使わないとできない空気を出してきます。
さらに、()内にHogeHoge書いてあるのも嫌なところです。

そして、賞金はシンボルの組み合わせによって決まります。

           combos prize
1        DD DD DD   100
2          7 7 7     80
3     BBB BBB BBB    40
4        BB BB BB    20
5           B B B    10
6           C C C    10
7  any Bar Combos     5
8           C C *     5
9           C * C     5
10          * C C     5
11          C * *     5
12          * C *     5
13          * * C     5

また、組み合わせ中にダイヤ(=DD)が含まれていると、賞金が倍になります。
例として、"7 7 7"は80ですが、"7 7 DD"は160、"7 DD DD"は320のような形です。
全て"DD"だと、100の倍、倍、倍なので800です。

これらをプログラミングしていくのですが、Rを用いる場合には
この2種類に分割して考えを進めていけば良さそうです。

  1. 順次的なステップ
  2. 並列するケース
順次的なステップ

まず、プログラムの骨子を行わせたい事の順序に沿って決めます。
それから、その骨子部分をより単純な問題に分割することを考えていきます。。

スロットマシンの骨子はこちらです。

  1. シンボルの生成
  2. シンボルの表示
  3. シンボルのスコア計算

それをブログラムにしたのがこちら。

play <- function(){
  # ステップ1: シンボルの生成
  symbols <- get_symbols()
  
  # ステップ2:シンボルの表示
  print(symbols)
  
  # ステップ3:シンボルのスコア計算
  score(symbols)
}

play関数内で、呼んでいるget_symbols関数score関数はまだ作成していません。
print関数は、ジェネリック関数と呼び、base環境に存在する関数です。

> print
function (x, ...) 
UseMethod("print")
<bytecode: 0x00000000126da340>
<environment: namespace:base>

get_symbols関数を作成します。
この関数は、シンボルの中からランダムに3種類抽出すれば良いです。

get_symbols <- function(){
  wheel <- c("DD","7","BBB","BB","B","C","0")
  sample(wheel, size = 3, replace = TRUE,
         prob = c(0.03, 0.03, 0.06,0.1,0.25,0.01,0.52)
  )
}

sample関数は、過去記事でも触れていますので割愛します。
それぞれのシンボルの確立ですが、実際のスロットマシンと同じ確率が用いられています。

次に、複雑に条件分岐が発生しそうなscore関数に踏み込んでいきます。

並列するケース

score関数は、上に書かれているルールに沿って、賞金を計算します。
ルールをまとめると、こうなります。(それぞれが独立している条件です。)

  • 同じ種類のシンボルが3つ:賞金のルックアップ
  • すべてバー:5ドル
  • 上記以外:チェリーの数を数える

それから上の結果を受けて、ダイヤによる調整を行います。

if文 else文

上の並列するケースを記載するには、if~else文を利用します。
使い方は、C#と同じように、このような形です。

もっぱら、C#ではif文は極力記載しないように逃げます。こいつですね。
Specification pattern in C# - CodeProject

sampleIf <- function(a,b){
  if(a > b){
    print("A wins!")
  }else if(a < b){
    print("B Wins!")
  }else{
    print("Tie.")
  }
}
> sampleIf(1,1)
[1] "Tie."
> sampleIf(2,1)
[1] "A wins!"
> sampleIf(1,2)
[1] "B Wins!"

また、Rのif~else文は、ショートサーキットです。
条件式に複数の論理式が結合されている場合、1番目の条件式から評価を行い
条件に該当しない場合は、2番目の条件式を評価しません。

このような関数があったとします。

sampleIf <- function(a,b){
  if(a > 10 && a > b){
    print("A Wins!")
  }else{
    print("B Wins!")
  }
}


これは、a > b条件を評価することなく、else側に流れます。

> sampleIf(2 , 1)
[1] "B Wins!"


これは、a > 10の条件を満たし、a > bを評価しましたがFALSEのため、
else側に流れます。

> sampleIf(11 , 12)
[1] "B Wins!"

if~else文の使い方が分かったので、score関数の作成に取り掛かりますが、
それは、次回の記事にします。