指導教員:豊泉 洋 教授
クラスター分析を用いた麻雀における行動分析
基幹理工学研究科 数学応用数理専攻 豊泉研究室
5115A010-5
鵜野 秀隆
目 次
1 はじめに 3
1.1 研究背景. . . 3
1.2 研究目的. . . 3
2 麻雀のルール 4 2.1 ゲームの進行 . . . 4
2.2 あがるとは . . . 4
2.2.1 あがり方. . . 4
2.3 プレイスタイル . . . 4
3 オンラインサイトでのデータ収集 5 3.1 牌譜データ . . . 6
3.2 使用牌譜データ . . . 6
3.2.1 データ概要 . . . 6
3.2.2 データ構成 . . . 7
3.2.3 牌譜出力データ . . . 7
4 分析手法 8 4.1 クラスター分析とは . . . 8
4.1.1 ボトムップ . . . 8
4.1.2 トップダウン . . . 8
4.2 分析条件. . . 8
4.3 主成分分析とは . . . 8
5 分析結果 9 5.1 クラスター分析 . . . 9
5.2 各クラスターの主成分分析 . . . 11
5.2.1 クラスター1 . . . 11
5.2.2 クラスター2 . . . 13
5.2.3 クラスター3 . . . 15
5.2.4 クラスター4 . . . 17
6 考察 19
7 結論 19
概要
1
はじめに
1.1
研究背景
麻雀は不完全情報型のゲームであるためその時々により状況が常に変化し、この打ち方をすれば必ず勝 てるという方法が確立されていない。そのため、各プレイヤは独自のプレイスタイルを持ち、勝率が良いプ レイヤと勝率が悪いプレイヤが2極化する。プレイスタイルにはさまざまな行動パターンがあり、ゲームの 勝敗にも大きく関係してくる。積極的にリーチを行うか行わないか、面前聴牌(めんぜんてんぱい)を重視 するか鳴きを重視するかなど、さまざまな行動パターンがある。また、近年インターネットによる麻雀ゲー ムの普及に伴い、プレイヤの行動パターンを分析する研究が盛んになってきている[1][2]。プレイヤの行動 パターンを分析することは麻雀の勝率を上げるためにはとても重要なことである。
1.2
研究目的
2
麻雀のルール
2.1
ゲームの進行
麻雀の牌は萬子(マンズ)・筒子(ピンズ)・索子(ソウズ)・字牌(ツーパイ)の4種類存在し、牌の合 計枚数は全部で136枚である。ゲームを行う人数は4人で、席決めをして自分が座る席を決定する。ゲー ムを一番最初に開始する席を東家(トンチャ)と呼び、東家のプレイヤがその局の親番となり反時計回りに ゲームが進行する。親番の人は必ず最初のツモを取らなくてはならない。各々の席にも名前がついており、 東家を基準に反時計回りに南家(ナンチャ)・西家(シューチャ)・北家(ペーチャ)と席の名前が決められ ている。局の親番が流れると、南家に座っていたプレイヤに親番が回り、南家だったプレイヤは東家に、西 家だったプレイヤは南家に、北家だったプレイヤは西家に、東家だったプレイヤは北家に代わる。ゲームは 前半戦4回、後半戦4回合わせて8回行われ、各親回ずつ親番が与えられる[3]。
2.2
あがるとは
手牌13枚の牌に加えた1枚の牌、合計14枚の牌で決められた役を一番最初に作ることによってあが ることができる。あがるための基本的な形は、「2枚で1組になる ”雀頭(じゃんとう)”を1つ+3枚で1 組になる ”面子(メンツ)”を4つ」である。雀頭(じゃんとう)とは同じ牌を2つ(發・發や2筒・2筒 など)集めることである。面子(メンツ)には2種類あり、”順子(シュンツ)”と ”刻子(コウツ)”が存 在する。順子(シュンツ)とは同じ種類の牌の連番(1萬・2萬・3萬や7索・8索・9索など)のことを 言い、この連番の形があがるための1つのグループになる。ただし、8・9・1のように数字が1周する組 み合わせは順子(シュンツ)にはならない。刻子(コウツ)とは同じ牌を3つ(中・中・中や1萬・1萬・ 1萬など)集めることを言い、この同じ牌の形があがるための1つグループとなる。
2.2.1 あがり方
麻雀の上がり方には ”ツモあがり ”をするか ”ロンあがり ”をするかの2パターンが存在する。ツモあが りとは自分の手番のときに自分が引いてきた牌で上がることを指す。一方ロンあがりとは他家(自分以外の プレイヤ)が捨てた牌であがることを指す。あるプレイヤが捨てた牌で同時に複数のプレイヤがロンをした 場合、放銃(振り込んだ、ロンされた)したプレイヤから次のツモ番が最も近いプレイヤのロンあがりのみ をあがりとみなす。この状態を上家取りと言う。子(南家、西家、北家)がツモあがりもしくはロンあがり をしたらその場でゲームは終了し次の局に移り親は流れてしまう。ただし、親がツモあがりもしくはロンあ がりをした際は親は流れずその局を続行する。
2.3
プレイスタイル
3
オンラインサイトでのデータ収集
”天鳳 ”とは、累計登録者数400万人を超えるインターネット上で無料オンライン麻雀ができるサイト である[4]。各プレイヤには段位が与えられており、初期は新人から始まり1級、2級、…8級、9級、初 段、二段、…、九段、十段、鳳凰位と麻雀の勝敗によって段位が変化する。段位を昇格させるためには各段 位に設けられている昇格ポイントを満たす必要があり、ゲームの順位によってポイントが付与され、必要な 昇格ポイント数を満たした場合段位を進ませることができる。また、段位が進むと一般卓、上級卓、特上 卓、鳳凰卓と麻雀をプレイする卓の位が上がり、自分と同じくらいの力量のプレイヤと対戦することがで きる。
3.1
牌譜データ
麻雀の自摸(ツモ)や打牌などの動作、点数の得失などを記録したものである。野球のスコアや囲碁・将 棋の棋譜などに相当する。図2は本研究で使用する実際の天鳳の牌譜データとなる。
*以降の各文字の先頭にある数字はプレイヤ番号を表しており、プレイヤ番号の後に記載されているアル ファベットがそのプレイヤが取った行動を示している。Gはツモ、Dは打牌、Rはリーチ、Aはあがり、N はポン、Cはチー、Kはカンの行動をそれぞれ表している。
本研究では、プレイヤのプレイスタイルをクラスターに分け、どのプレイスタイルが一番効率的に自分 の持ち点を増やせるか分析を行っていく。そのため、東1局∼南4局までの各プレイヤの「リーチ、ポン、 チー、カン」の4つの行動回数を各プレイヤそれぞれ求め、最終的なゲームの獲得点数との比較を行いクラ スター分析を行う。図2の青い四角は「リーチ」、赤い四角は「あがり」、緑の四角は「ポン」、オレンジの 四角は「チー」を表している。
図2: 牌譜データ
3.2
使用牌譜データ
3.2.1 データ概要
鳳凰卓の東南戦赤有りで4人対戦をしているプレイヤの中からサンプルを抽出しデータを取得した。デー タ取得期間及びプレイヤ数、プレイヤの基本属性は下記のとおりである。
・データ取得期間:2017年12月11日(月) ・プレイヤ数:164人(41試合分)
・プレイヤの基本属性:段位(7段:93人 8段:51人 9段:13人 10段:7人)
鳳凰卓は7段以上の高段位のプレイヤのみ参加ができる卓であるため、各プレイヤが独自のプレイスタ イルを持っていることが推測できる。また、有料会員でないと鳳凰卓に参加することができないため、より 質の高いデータを取得することができるのではないかと考えた。
3.2.2 データ構成
プレイスタイルの違いによる最も効率的に自分の持ち点を増やすことができる行動パターンを分析する ために必要となる取得データを下記に示す。
・1局中のリーチの回数 ・1局中のあがりの回数 ・1局中のポンの回数 ・1局中のチーの回数 ・1局中のカンの回数
上記のデータを牌譜データから抽出するためにC言語でプログラムを作成しデータ抽出を行った。プロ グラムコードは論文の末にある付録に記載しておく。
3.2.3 牌譜出力データ
表1: 各プレイヤの1局あたりの行動パターン回数
番号 段位 リーチ回数 あがり回数 順位 獲得点数 ポン回数 チー回数 カン回数 鳴きの合計 リーチと鳴きの合計
1 7 3 4 3 -24 5 3 0 8 11
2 8 1 2 4 -34 5 1 0 6 7
3 7 3 4 1 52 1 2 0 3 6
4 7 2 3 2 6 5 3 0 8 10
5 7 0 1 2 10 1 0 0 1 1
6 8 3 0 4 -52 0 0 0 0 3
7 7 1 2 1 55 0 0 0 0 1
8 8 0 1 3 -13 0 1 1 2 2
9 10 3 1 4 -47 0 1 0 1 4
10 8 3 3 2 14 1 5 1 7 10
11 7 5 4 1 59 3 2 0 5 10
12 7 4 2 3 -26 4 5 1 10 14
13 8 2 3 4 -36 3 3 0 6 8
∼ ∼ ∼ ∼ ∼ ∼ ∼ ∼ ∼ ∼ ∼
152 8 2 3 4 -39 3 2 0 5 7
153 7 3 3 2 14 2 3 0 5 8
154 7 0 4 1 50 3 1 0 4 4
155 8 1 0 4 -46 1 5 0 6 7
156 7 1 1 3 -18 3 2 0 5 6
157 8 2 3 3 -20 3 1 0 4 6
158 8 4 1 4 -40 1 0 0 1 5
159 7 1 1 2 13 4 1 0 5 6
160 9 2 4 1 47 1 1 0 2 4
161 8 3 2 1 51 1 2 0 3 6
162 7 4 2 2 1 0 3 1 4 8
163 8 4 2 4 -33 1 0 0 1 5
164 8 1 3 3 -19 1 1 0 2 3
4
分析手法
4.1
クラスター分析とは
クラスター分析とは、異なる性質のもの同士が混ざり合っている集団の中から、ある方針の下で類似して いるいくつかのかたまり(クラスター)に分類する手法である。分類の目的や用途に応じてさまざまな分類 方法が提唱されているが、ボトムアップとトップダウンの2つに大別される[5]。
4.1.1 ボトムップ
分析者が距離(データ間の非類似度を数値的に表したもの)の測定方法や結合(クラスター化)の方法を 指定し、距離の小さいものから分類していく方法である。分類過程は樹形図(デントログラム)で描かれ ているため、データを分類するだけではなく、クラスター間の関係性や分類過程を明らかにできる。また、 クラスター数をあらかじめ指定するのではなく、樹形図を見て決めることができる。距離の測定方法として は、ユークリッド距離法が一般的であり、結合(クラスター化)の方法としては、Ward法や最短距離法、 最長距離法、群平均法、メディアン法、重心法などが挙げられる。
4.1.2 トップダウン
分析者があらかじめいくつかのクラスターにデータを分類するかを指定した上で、クラスター内のばら つきは小さく、クラスター間のばらつきは大きくなるよう、最適な分割を求める方法である。分類過程は樹 形図で描かれず、どのクラスターに分類されたかを示すのみであるが、明確なクラスターが得られる。
4.2
分析条件
どちらの手法を適用するかは分析者の判断によるが、データ数が比較的に少なく、かつ、分類過程や類似 度も考慮したい場合にはボトムアップを用い、データ数が非常に多く、計算量が膨大になる場合にはトップ ダウンを用いる傾向にある。本研究では、データ数は164と比較的少なく、また類似度を考慮したいとい う観点からクラスター数を指定しないボトムアップによるクラスター分析を適用する。
4.3
主成分分析とは
主成分分析とは、量的データを要約する分析手法である。複数の変数を持うデータから情報はなるべく損 失しないようにしつつ、より少ない変数でデータの特徴がつかめるような新しい軸を作り出すことができ、 データの関係性を視覚的に捉えることができるようになる[6]。
5
分析結果
5.1
クラスター分析
図 3: 各手法によるクラスター分析
164人分の牌譜データを元にクラスター分析を行った。左上から右に向かって、最短距離法(single)、 最長距離法(complete)、群平均法(average)、重心法(centroid)、メディアン法(median)、ウォード法 (ward)となっている。各手法で指定したクラスター分析方法で計算されるクラスター間の距離行列をコー
フェン距離と言い、その距離行列のことをコーフェン行列と呼ぶ。
クラスター分析の樹形図はコーフェン行列に基づいている。コーフェン行列は用いる手法によって結果が 異なるため、どの結果を用いるべきか1つの問題となる。クラスター分析の妥当性の評価に、距離の行列と コーフェン行列との相関係数が用いられている。その係数がコーフェン相関係数である。コーフェン相関係 数の値が大きいほど、距離行列と用いた方法のコーフェン行列との歪みが少ないと判断でき、妥当性の高い 結果であることが分かる[7]。
この6種類の手法の中で、最も妥当性が高い手法を選ぶためにコーフェン相関係数を用いた。下記の表が 各手法のコーフェン相関係数をまとめたものである。コーフェン相関係数の値が大きいほど、歪みが少なく 妥当性が高い手法であると分かる。
表2: 各プレイヤの1局あたりの行動パターン回数
最短距離法
(single
) 最長距離法(complete)
群平均法(averag)
重心法(centroid)
メディアン法(median)
ウォード法(ward)
コーフェン相関係数
0.7877527
0.8042893
0.7636091
0.8147767
0.7688269
0.7492072
以下に、重心法だけの結果を示す。
図 4: 重心法によるクラスター分析
クラスター分析によって作られた樹形図の縦軸は距離を表している。クラスターどうしが併合された場 合、樹形図では横棒がそれらクラスターが併合されたことを示しており、そのときの横棒の縦軸の目盛りが クラスター間の距離を表している。また、樹形図の縦軸のある位置でグラフをカットすることにより、目的 の数のクラスター数を得ることができる。
縦軸の目盛りが小さければ小さいほど、クラスター間の距離が近く似た者どうしがクラスターを形成し ていることになる。分析結果から図4のように樹形図を縦軸の13辺りでカットし、クラスターを4つに分 け各々のクラスターに対して分析を行った。
表3: 各クラスターの平均値
クラスター番号 リーチ回数 あがり回数 順位 獲得点数 ポン回数 チー回数 カン回数 鳴きの合計 リーチと鳴きの合計 1
1.951219512 3.512195122
1
51.80487805
2.56097561 1.414634146 0.268292683 4.243902439
6.195121951
2
1.658536585 2.268292683
2
10.34146341
3
2.292682927 0.219512195 5.512195122
7.170731707
3
1.810810811 1.891891892
3
-17.48648649
3
2.216216216 0.216216216 5.432432432
7.243243243
4
1.955555556 0.933333333 3.911111111 -42.24444444 2.533333333 1.622222222
0.2
4.355555556
6.311111111
まず、各クラスターの平均値をとり各クラスターの特徴を調べる。「あがり回数」、「順位」、「獲得点数」 の3つの項目が平均値の差が大きく、その他の項目は平均値の差が小さいことが分かる。クラスター分析の 結果より、順位が一番強く寄与していると考えられる。
5.2
各クラスターの主成分分析
5.2.1 クラスター1
表 4: クラスター1の固有ベクトル
第1主成分 第2主成分 第3主成分 第4主成分 第5主成分 第6主成分 第7主成分 第8主成分 リーチ回数 -0.006 0.691 -0.242 0.270 -0.191 0.380 -0.107 0.445 あがり回数 0.194 0.378 0.571 -0.163 -0.506 -0.459 0.000 0.000 獲得点数 -0.101 0.359 0.035 -0.824 0.396 0.153 0.000 0.000 ポン回数 0.433 -0.229 0.443 -0.001 0.116 0.418 0.460 0.406 チー回数 0.402 0.171 -0.357 0.090 0.402 -0.608 0.283 0.250 カン回数 0.232 -0.263 -0.527 -0.445 -0.601 0.048 0.148 0.131 鳴きの合計 0.544 -0.150 0.038 -0.064 0.122 0.043 -0.803 0.124 リーチ、鳴きの合計 0.504 0.278 -0.111 0.104 -0.002 0.270 0.176 -0.736 固有値 1.791 1.269 1.054 0.980 0.776 0.715 0.000 0.000 寄与率 0.401 0.201 0.139 0.120 0.075 0.064 0.000 0.000 累計寄与率 0.401 0.602 0.741 0.861 0.936 1.000 1.000 1.000
表4より主成分を選択する。主成分の選択をする際に2つの方法が存在する。1つ目は「固有値が1以 上」の主成分を選択する方法、2つ目は「累計寄与率が80%以上」の主成分を選択する方法である。表8 より、主成分3までの固有値を見ると固有値が1以上の値であり、かつ累積寄与率は約80%である。よっ て、主成分3までのデータを見れば全体の約74%の情報を占めていることになり、次元を3次元に縮約し ても十分にデータの特徴を表すことができる。
次に、主成分1から主成分3が何を表しているのかを理解するために因子負荷量を求める。因子負荷量 とは主成分と変数との相関関係を表しており、‐1以上1以下の値をとり、因子負荷量の絶対値が大きいほ ど主成分と強く相関していることが分かる。「リーチ回数、あがり回数、獲得点数、ポン回数、チー回数、 カン回数、鳴きの合計、リーチと鳴きの合計」を、それぞれ「リ、あ、点、ポ、チ、カ、鳴、合」で図に表 し、主成分と強く相関している変数を赤丸で示す。図5は主成分1から主成分3までの因子負荷量を計算し プロットしたものである。
[1]主成分1の因子負荷量 [2]主成分2の因子負荷量 [3]主成分3の因子負荷量
図5より、第1主成分は「ポン回数、チー回数、鳴きの合計、リーチと鳴きの合計」の4つのデータが強 く寄与しているので、鳴く傾向を示している。第2成分は「リーチ回数」の1つのデータが強く寄与してい るので、リーチをする傾向を示している。第3成分は「上がり回数」の1つのデータが強く寄与しているの で、上がりやすい手作りをする傾向を示している。
表 5: クラスター1における各主成分の特徴
主成分(PC) 特徴
1 鳴く傾向
2 リーチをする傾向
3 上がりやすい手作りをする傾向
図 6: クラスター1の主成分得点
5.2.2 クラスター2
以下、クラスター1と同様に行う。
表 6: クラスター2の固有ベクトル
第1主成分 第2主成分 第3主成分 第4主成分 第5主成分 第6主成分 第7主成分 第8主成分 リーチ回数 -0.172 0.295 -0.835 0.011 0.148 -0.294 -0.052 0.275 あがり回数 -0.396 0.365 -0.086 0.057 -0.013 0.837 0.000 0.000 獲得点数 -0.123 0.672 0.276 0.034 -0.587 -0.334 0.000 0.000 ポン回数 -0.401 -0.177 0.191 0.607 0.081 -0.133 0.470 0.397 チー回数 -0.339 0.065 0.238 -0.754 0.277 -0.109 0.312 0.264 カン回数 -0.217 -0.506 -0.276 -0.236 -0.731 0.094 0.112 0.095 鳴きの合計 -0.480 -0.181 0.200 0.036 0.064 -0.129 -0.802 0.176 リーチ、鳴きの合計 -0.495 -0.064 -0.101 0.036 0.108 -0.217 0.153 -0.811 固有値 1.981 1.158 1.022 0.870 0.806 0.531 0.000 0.000 寄与率 0.491 0.168 0.131 0.095 0.081 0.035 0.000 0.000 累計寄与率 0.491 0.658 0.789 0.883 0.965 1.000 1.000 1.000
表6より、主成分は第3主成分までを選択する。
[1]主成分1の因子負荷量 [2]主成分2の因子負荷量 [3]主成分3の因子負荷量
図 7: クラスター2の各主成分の因子負荷量
表 7: クラスター2における各主成分の特徴
主成分(PC) 特徴
1 上がりやすい鳴きをする傾向
2 持ち点を増やす傾向
3 リーチをする傾向
図 8: クラスター2の主成分得点
5.2.3 クラスター3
表 8: クラスター3の固有ベクトル
第1主成分 第2主成分 第3主成分 第4主成分 第5主成分 第6主成分 第7主成分 第8主成分 リーチ回数 -0.204 -0.547 0.370 0.027 -0.602 -0.259 0.169 0.252 あがり回数 -0.159 -0.595 0.250 0.179 0.557 0.464 0.000 0.000 獲得点数 0.156 -0.318 -0.563 0.624 0.091 -0.401 0.000 0.000 ポン回数 -0.455 0.167 0.130 -0.069 0.430 -0.502 0.551 -0.047 チー回数 -0.380 -0.027 -0.549 -0.058 -0.280 0.490 0.479 -0.041 カン回数 -0.110 0.434 0.370 0.754 -0.166 0.228 0.121 -0.010 鳴きの合計 -0.515 0.152 -0.169 0.029 0.096 -0.016 -0.460 0.679 リーチ、鳴きの合計 -0.529 -0.067 -0.013 0.035 -0.137 -0.110 -0.461 -0.686 固有値 1.867 1.240 1.074 0.912 0.759 0.644 0.000 0.000 寄与率 0.436 0.192 0.144 0.104 0.072 0.052 0.000 0.000 累計寄与率 0.436 0.628 0.772 0.876 0.948 1.000 1.000 1.000
表8より、主成分は第3主成分までを選択する。
[1]主成分1の因子負荷量 [2]主成分2の因子負荷量 [3]主成分3の因子負荷量
図 9: クラスター3の各主成分の因子負荷量
表 9: クラスター3における各主成分の特徴
主成分(PC) 特徴
1 鳴く傾向
2 上がりやすいリーチをする傾向
3 チーを用いて持ち点を増やす傾向
図10: クラスター3の主成分得点
5.2.4 クラスター4
表 10: クラスター4の固有ベクトル
第1主成分 第2主成分 第3主成分 第4主成分 第5主成分 第6主成分 第7主成分 第8主成分 リーチ回数 0.162 0.427 -0.643 0.278 -0.112 -0.380 -0.377 0.050 あがり回数 -0.150 0.607 0.057 -0.007 -0.436 0.645 0.000 0.000 獲得点数 -0.097 0.404 0.676 0.164 -0.155 -0.565 0.000 0.000 ポン回数 -0.480 0.170 0.038 0.029 0.560 0.105 -0.395 -0.508 チー回数 -0.356 -0.318 -0.095 -0.362 -0.636 -0.210 -0.263 -0.338 カン回数 -0.109 -0.366 0.084 0.865 -0.224 0.169 -0.085 -0.109 鳴きの合計 -0.559 -0.091 -0.005 -0.019 0.067 0.001 -0.266 0.777 リーチ、鳴きの合計 -0.511 0.120 -0.331 0.120 0.014 -0.191 0.744 -0.098 固有値 1.775 1.298 1.068 0.954 0.836 0.645 0.000 0.000 寄与率 0.394 0.211 0.143 0.114 0.087 0.052 0.000 0.000 累計寄与率 0.394 0.604 0.747 0.861 0.948 1.000 1.000 1.000
表10より、主成分は第3主成分までを選択する。
[1]主成分1の因子負荷量 [2]主成分2の因子負荷量 [3]主成分3の因子負荷量
図11: クラスター4の各主成分の因子負荷量
表11: クラスター4における各主成分の特徴
主成分(PC) 特徴
1 鳴く傾向
2 上がりやすいリーチをして持ち点を増やす傾向
3 持ち点を増やす傾向
図12: クラスター4の主成分得点
6
考察
表 12: 各クラスターの特徴
クラスター 順位 特徴
1 1 高い手を狙い、リーチを多く行う
2 2 リーチを多く行う
3 3 鳴く傾向が高く、チーを用いて持ち点を増やす
4 4 鳴きなら鳴き、リーチならリーチと打ち方が一辺倒
各クラスターの特徴を表12にまとめた。順位の高いクラスターに属するプレイヤほど面前を重視する傾 向があり順位の低いクラスターに属するプレイヤほど鳴きを重視する傾向があることが分かった。鳴きは 自分の手を進める有効な手段となる反面、防御面が薄くなり相手に放銃してしまうリスクが高くなってし まう。面前の場合、手が進むのは遅いがその分防御力が高いため、危険な状況においても臨機応変に対応す ることができる。この結果から、順位の高いプレイヤはリスクを常に意識しつつ柔軟な対応ができるよう に心掛けているのだと考えられる。
また、順位の高いクラスターに属するプレイヤほどリーチを上がる手段としている。リーチは行動を制限 してしまう一見リスクの高い行動だが、一発や裏ドラといったリスク以上の恩恵がとても大きい。また、自 分の手牌が相手に一枚も見えないため他のプレイヤは手の内を読みずらく、一発や裏ドラを考えると真っす ぐ手を進めることを躊躇してしまう。一方、鳴きの場合は相手に自分の手牌をさらすことになるので他のプ レイヤから手の内が読まれやすく、一発や裏ドラがないので真っすぐ手を進めやすくなる。この結果から、 順位の高いプレイヤは相手の手を進ませないようにすると同時に自分が上がったときの点数をなるべく高 くしようと意識していると考えられる。
7
結論
麻雀の勝率を上げるためには、
1.面前聴牌(めんぜんてんぱい)を目指す 2.高い手を狙い、積極的にリーチを掛ける
参考文献
[1] 山田大智:麻雀プロの牌譜に関する統計的分析
[2] みーにん:「統計学」のマージャン戦術、竹書房[2017]
[3] 麻雀ルール・麻雀入門 http://www2.odn.ne.jp/ cbm15900/html/n0.html
[4] 天鳳 https://tenhou.net/
[5] 新納浩幸:Rで学ぶクラスタ解析、オーム社[2015]
[6] 末吉正成、里洋平、酒巻隆治、小林雄一郎、 大城信晃:Rではじめるビジネス統計分析、翔泳社[2014]
8
付録 プログラム
# i n c l u d e < stdio .h > # i n c l u d e < stdl ib .h > # i n c l u d e < stri ng .h > # define S T R _ M A X 256
int main ( void ) {
FILE * fp ; char fname [80];
char buff [ S T R _ M A X ] , srch [ S T R _ M A X ]; char * ss tart ;
int count = 0; int count1 = 0; int count2 = 0; int count3 = 0; int Acount = 0; int A c o u n t 1 = 0; int A c o u n t 2 = 0; int A c o u n t 3 = 0;
printfファイル名(" : "); gets ( fname );
printf段位検索 段(" : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ) if ( strstr ( buff , srch ) != NULL )
printf ("% s " , buff ); printfリーチ検索R("[1] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ count ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfリーチ回数R("[1]= %回d \ n " , count ); printfリーチ検索R("[2] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ coun t1 ;
break ; } else
break ; }
}
printfリーチ回数R("[2]= %回d \ n " , count 1 ); printfリーチ検索R("[3] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ coun t2 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfリーチ回数R("[3]= %回d \ n " , count 2 ); printfリーチ検索R("[4] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ coun t3 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfリーチ回数R("[4]= %回d \ n " , count 3 );
printfあがり検索A("[1] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ Acou nt ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfあがり回数A("[1]= %回d \ n " , Acoun t );
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ A c o u n t 1 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfあがり回数A("[2]= %回d \ n " , A c o u n t 1 );
printfあがり検索A("[3] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ A c o u n t 2 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfあがり回数A("[3]= %回d \ n " , A c o u n t 2 ); printfあがり検索A("[4] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ A c o u n t 3 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfあがり回数A("[4]= %回d \ n " , A c o u n t 3 ); printf順位検索 位(" : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
printf ("% s " , buff ); int Pcount = 0;
int P c o u n t 1 = 0; int P c o u n t 2 = 0; int P c o u n t 3 = 0; int Ccount = 0; int C c o u n t 1 = 0; int C c o u n t 2 = 0; int C c o u n t 3 = 0; int Kcount = 0; int K c o u n t 1 = 0; int K c o u n t 2 = 0; int K c o u n t 3 = 0;
printfポン検索("[1] P : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ Pcou nt ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfポン回数("[1] P = %回d \ n " , Ncou nt );
printfポン検索("[2] P : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ P c o u n t 1 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfポン回数("[2] P = %回d \ n " , N c o u n t 1 ); printfポン検索("[3] P : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ P c o u n t 2 ;
if (* sstart == ’\0 ’) break ; } else break ; } }
printfポン回数("[3] P = %回d \ n " , N c o u n t 2 ); printfポン検索("[4] P : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ P c o u n t 3 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfポン回数("[4] P = %回d \ n " , N c o u n t 3 );
printfチー検索C("[1] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ Ccou nt ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfチー回数C("[1]= %回d \ n " , Ccoun t );
printfチー検索C("[2] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ C c o u n t 1 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfチー検索C("[3] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ C c o u n t 2 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfチー回数C("[3]%回d \ n " , C c o u n t 2 ); printfチー検索C("[4] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ C c o u n t 3 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfチー回数C("[4]= %回d \ n " , C c o u n t 3 ); printfカン検索K("[1] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ Kcou nt ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfカン回数K("[1]= %回d \ n " , Kcoun t ); printfカン検索K("[2] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ K c o u n t 1 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfカン回数K("[2]= %回d \ n " , K c o u n t 1 ); printfカン検索K("[3] : ");
gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ K c o u n t 2 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfカン回数K("[3]= %回d \ n " , K c o u n t 2 );
printfカン検索K("[4] : "); gets ( srch );
if (( fp = fopen ( fname , " r ")) == NULL ){ printfファイルがオープンできません("\ n "); exit (1);
}
while ( fgets ( buff , STR_MAX , fp ) != NULL ){ sstart = buff ;
while (1){
sstart = str str ( sstart , srch ); if ( sstart != NULL ){
++ K c o u n t 3 ;
sstart = sst art + strle n ( srch ); if (* sstart == ’\0 ’)
break ; } else
break ; }
}
printfカン回数K("[4]= %回d \ n " , K c o u n t 3 );