LoginSignup
5
5

More than 5 years have passed since last update.

Pythonでドミニオン戦略解析 〜鍛冶屋ステロ編〜

Last updated at Posted at 2018-11-07

自己紹介

はじめまして、NCT48といいます。
ボードゲーム好きの変態(他称)です。
理系(非情報系)を卒業して3年半、だらだらと働いています。
普段のお仕事は組み込み系(C言語)です。

この記事について

今まで「情報のアウトプット」を行なった事がありませんでした。(自堕落マン)
そろそろ何か1つくらい書いてみようかと思っているまま時は過ぎ...(半年)
で、ついに重い腰を上げ一番好きなボードゲーム「ドミニオン」で書いてみる事にします。
Pythonの練習も兼ねてやってみました。

2018/11/08追記
改めて見返してあまりにもボロボロだったので、その2として色々書き直しました。

Pythonでドミニオン戦略解析 〜鍛冶屋ステロ編 その2〜

ドミニオンって?

ドミニオン (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#です。)

戦略解析

ここからはソースと出力結果を合わせて解説していきます。

smith.ipynb
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

ここは特に言うことはないと思います。


smith.ipynb
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.ipynb
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.ipynb
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週目に銀貨がいない(鍛冶屋を買っている)→デッキの金量が低いからではないかと思います。
(銀貨を買った方が良いのかは次回以降に試したいと思います。)


smith.ipynb
plt.hist([smith_data25["Turn"],smith_data34["Turn"]],bins=10, range=(9.5,19.5), density=True, rwidth=0.5)

Unknown.png

青色が2-5,オレンジが3-4です。
やはり山のピークが左に寄っているのは3-4ですね。
グラフのスケールが違いすぎるので正規化しています。


smith.ipynb
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.ipynb
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ターン遅くなっています。


smith.ipynb
plt.hist([smith_data_sizumi["Turn"],smith_data_sizumazu["Turn"]],bins=10, range=(9.5,19.5), density=True, rwidth=0.5)

Unknown-1.png

青色が沈んだ時,オレンジが沈まなかった時です。
沈んだ時はバラツキも大きく、確かに発生しない方が良いですね。
やはり、グラフのスケールが違いすぎるので正規化しています。

まとめ

今まで体系的に言われきた「ドミニオンあるある」は本当なんだなぁと改めて思いました。
至極当たり前のことしか言ってない気もしますが、改めてデータで見るのも大事だなと言ったところです。
今後は「使い切れなかったお金(今回無駄にしたデータ)」や「アクションを発動した回数(シミュレータの改良が必要)」あたりを解析に生かしていきたいです。

最後に

今回初めてPythonでデータ解析を行いました。
かなりツッコミどころが多いと思います。
コメント等にてガンガン突っ込んでもらえると嬉しいです。
もしドミニオンに詳しい人がいたら「こんな戦術試してくれ!」的なリクエストがあるともっと嬉しいです。コンボ系はやめてくださいしんでしまいます
(次回は「船着場ステロ」を予定してます。実はGitHubにはソースとか置いてあります。)

今回使用したソースおよびCSVです。
https://github.com/NCT48/Dominion

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5