自己紹介
はじめまして、NCT48といいます。
ボードゲーム好きの変態(他称)です。
理系(非情報系)を卒業して3年半、だらだらと働いています。
普段のお仕事は組み込み系(C言語)です。
この記事について
今まで「情報のアウトプット」を行なった事がありませんでした。(自堕落マン)
そろそろ何か1つくらい書いてみようかと思っているまま時は過ぎ...(半年)
で、ついに重い腰を上げ一番好きなボードゲーム「ドミニオン」で書いてみる事にします。
Pythonの練習も兼ねてやってみました。
2018/11/08追記
改めて見返してあまりにもボロボロだったので、その2として色々書き直しました。
[Pythonでドミニオン戦略解析 〜鍛冶屋ステロ編 その2〜][1]
[1]:https://qiita.com/NCT48/items/c2b6dce2a23074f137fe
ドミニオンって?
ドミニオン (Dominion) は、アメリカのボードゲーム(カードゲーム)。作者はドナルド・X・ヴァッカリーノ。2008年秋にアメリカのリオグランデゲームズ(英語版)社より発売され、日本では2009年にホビージャパンが完全日本語版を発売している。(wikipedia ドミニオンより)
詳しく説明すると長くなるので、解説サイトやレビューサイトをご確認下さい。
個人的に最高のボードゲームだと思っています。さあ、みんなでレッツドミニオン!
本題...の前に
タイトルにある通り「鍛冶屋ステロ」に関して...の前に、最低限のルールだけ説明しようかなと。
そもそもドミニオン知らない人がこの記事を見に来てくれるのか?
・カードを買ってデッキを強くするゲーム
・最初のデッキは「銅貨7枚(1金)」と「屋敷3枚(1点)」から
・毎ターンの終了時、手札を全て捨て5枚補充する
・デッキが無くなったら、捨て札をシャッフルしデッキにする。
・アクションカードや財宝カードを買ってデッキを強化していく
・ゲーム終了時に勝利点が多い人が勝ち
・一般的に「属州4枚(6点*4枚=24点)」を集めれば負けはないと言われている
簡易的な説明のため一部語弊や誤解を招く表現がありますが、だいたいこんな感じです。
ステロイド、略してステロ
ドミニオンには様々な戦法があります。
その中の一つがステロイド(ステロ)と呼ばれるものです。
最低限(1〜2枚)のアクションカードと大量の財宝カードを買い、属州4枚の獲得を目指す。
今回は「鍛冶屋」を主体にした「鍛冶屋ステロ」を解析してみました。
解析データ
解析対象のデータはcsvで、50000回一人回しを行なった結果です。
(自作のシミュレータを使用して、結果を出力しています。)
出力内容は
・ID
・属州4枚買うまでのターン(Turn)
・各ターンの手札内容(Hund)
・各ターン使用しなかった財宝(UselessMoney)
となっています。
今回の解析ではID,Turn,Hundを使用しました。
(UselessMoneyはうまく活用できなかったです...)
戦略詳細
1.手札に8金以上あり、デッキに金貨が1枚以上ある→属州購入
2.手札に8金以上あり、デッキに金貨が1枚もない→金貨購入
3.手札に6金以上ある→金貨購入
4.手札に4金以上あり、デッキに鍛冶屋が1枚以上ある→銀貨購入
5.手札に4金以上あり、デッキに鍛冶屋が1枚もない→鍛冶屋購入
6.手札に3金以上ある→銀貨購入
実践ではもう少し色々考えるんですが、簡易化のためにこの戦術でいきます。
環境
Windows10
Jupyter Notebook
Python3
NumPy
Pandas
Matplotlib
(自作シミュレータはC#です。)
戦略解析
ここからはソースと出力結果を合わせて解説していきます。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
ここは特に言うことはないと思います。
smith_data = pd.read_csv('./TestProgramSmith.csv', header=0, index_col=0)
smith_data.head()
Turn | Hund | UselessMoney | |
---|---|---|---|
0 | 14 | 屋屋銅銅銅/屋銅銅銅銅/屋屋銅銅銅/屋銀銅銅銅銅銅鍛/屋屋銀銅銅/屋銀銅銅銅銅銅鍛/屋金銀銅... | 0/0/0/1/1/1/1/3/1/0/2/3/0/1 |
1 | 12 | 屋銅銅銅銅/屋屋銅銅銅/屋屋銀銅銅銅銅鍛/屋銅銅銅銅/屋屋金銀銅銅銅鍛/屋屋金銀銅銅銅鍛/屋... | 0/0/0/1/2/0/1/1/2/1/2/0 |
2 | 17 | 屋銅銅銅銅/屋屋銅銅銅/屋銅銅銅銅/屋屋銀銅銅銅銅鍛/屋銀銅銅銅/屋銀銀銅銅/屋屋銀銅銅銅銅... | 0/0/1/0/2/0/0/0/3/1/3/1/1/3/0/0/7 |
3 | 13 | 屋銅銅銅銅/屋屋銅銅銅/銀銅銅銅銅/屋屋屋金銅銅銅鍛/銀銅銅銅銅/金金銀銅銅/屋屋屋金銅銅銅... | 0/0/0/0/0/2/0/3/2/1/2/0/1 |
4 | 13 | 屋屋銅銅銅/屋銅銅銅銅/屋屋銀銅銅銅銅鍛/屋銅銅銅銅/屋屋金銀銅銅銅鍛/屋屋銅銅銅/金銀銅銅... | 0/0/0/1/2/0/0/1/1/0/0/1/1 |
ここでHundの詳細な説明を。
今回登場するカードは
・屋敷(1点,2円)
・銅貨(1円,0円)
・銀貨(2円,3円)
・金貨(3円,6円)
・鍛冶屋(カードを3枚引く,4円)
・属州(6点,8円)
です。
ここで(X,Y)の[X=カードの効果]、[Y=カードを買うのに必要なお金]です。
「屋屋銅銅銅」となっていた場合、手札に「屋敷2枚、銅貨3枚」があると言うことです。
「屋銀銅銅銅銅銅鍛」となっていた場合、鍛冶屋を発動した結果、手札に「屋敷1枚、銀貨1枚、銅貨5枚」があると言うことです。
ターン毎に/が入っているので、「屋屋銅銅銅/屋銅銅銅銅」となっていた場合、「1ターン目/2ターン目」となっています。
smith_data34 = smith_data[smith_data["Hund"].str.startswith("屋屋銅銅銅/", "屋銅銅銅銅/")]
smith_data25 = smith_data[smith_data["Hund"].str.startswith("銅銅銅銅銅/", "屋屋屋銅銅/")]
最初のデッキは銅貨7枚と屋敷3枚で手札は5枚なので、
1.銅貨2枚-銅貨5枚
2.銅貨3枚-銅貨4枚
3.銅貨4枚-銅貨3枚
4.銅貨5枚-銅貨2枚
の4パターンです。
このうち、1-4と2-3は同じ結果なのでそれぞれのパターンにDatasetを分けました。
smith_data34_des = smith_data34["Turn"].describe()
smith_data25_des = smith_data25["Turn"].describe()
smith_data25_des, smith_data34_des
(count 4149.000000
mean 15.847674
std 1.701418
min 11.000000
25% 15.000000
50% 16.000000
75% 17.000000
max 23.000000
Name: Turn, dtype: float64,
count 20974.000000
mean 14.833031
std 1.676777
min 9.000000
25% 14.000000
50% 15.000000
75% 16.000000
max 23.000000
Name: Turn, dtype: float64)
平均ターン、最速ターン共に3-4の入りの方が上です。
この理由としては、2-5の入りだとデッキ2週目に銀貨がいない(鍛冶屋を買っている)→デッキの金量が低いからではないかと思います。
(銀貨を買った方が良いのかは次回以降に試したいと思います。)
plt.hist([smith_data25["Turn"],smith_data34["Turn"]],bins=10, range=(9.5,19.5), density=True, rwidth=0.5)
青色が2-5,オレンジが3-4です。
やはり山のピークが左に寄っているのは3-4ですね。
グラフのスケールが違いすぎるので正規化しています。
smith_data_sizumi = smith_data[smith_data["Hund"].str.find("鍛") == 31]
smith_data_sizumazu = smith_data[smith_data["Hund"].str.find("鍛") != 31]
次に「底沈み」と呼ばれる現象について解析したいと思います。
底沈みとは、「1〜2ターン目に買ったアクションカードを5ターン目に引く」ことを指します。
一般的に発生して欲しくない(デッキの進みが遅くなる)と言われています。
詳細はこれまた長くなるので「ドミニオン 底沈み」でググってください。
果たして本当に遅くなるのかを解析したいと思います。
smith_data_sizumi_des = smith_data_sizumi["Turn"].describe()
smith_data_sizumazu_des = smith_data_sizumazu["Turn"].describe()
smith_data_sizumi_des, smith_data_sizumazu_des
(count 7715.000000
mean 15.357874
std 1.675631
min 10.000000
25% 14.000000
50% 15.000000
75% 16.000000
max 22.000000
Name: Turn, dtype: float64,
count 42285.000000
mean 14.937519
std 1.722500
min 9.000000
25% 14.000000
50% 15.000000
75% 16.000000
max 23.000000
Name: Turn, dtype: float64)
約15%の確率で発生してます。
平均で0.4ターン、最速、最遅ターン共に1ターン遅くなっています。
plt.hist([smith_data_sizumi["Turn"],smith_data_sizumazu["Turn"]],bins=10, range=(9.5,19.5), density=True, rwidth=0.5)
青色が沈んだ時,オレンジが沈まなかった時です。
沈んだ時はバラツキも大きく、確かに発生しない方が良いですね。
やはり、グラフのスケールが違いすぎるので正規化しています。
まとめ
今まで体系的に言われきた「ドミニオンあるある」は本当なんだなぁと改めて思いました。
至極当たり前のことしか言ってない気もしますが、改めてデータで見るのも大事だなと言ったところです。
今後は「使い切れなかったお金(今回無駄にしたデータ)」や「アクションを発動した回数(シミュレータの改良が必要)」あたりを解析に生かしていきたいです。
最後に
今回初めてPythonでデータ解析を行いました。
かなりツッコミどころが多いと思います。
コメント等にてガンガン突っ込んでもらえると嬉しいです。
もしドミニオンに詳しい人がいたら「こんな戦術試してくれ!」的なリクエストがあるともっと嬉しいです。コンボ系はやめてくださいしんでしまいます
(次回は「船着場ステロ」を予定してます。実はGitHubにはソースとか置いてあります。)
今回使用したソースおよびCSVです。
https://github.com/NCT48/Dominion