はじめに
ここでは、Pythonのデータ分析用ライブラリであるPandasを利用して、civilization 4のプレイを定量的に評価分析する方法を紹介します。
本記事は
- civ4というゲームのシステムを知っている
- プログラミング言語は1つ2つ触ったことがある
という読者が、見よう見まねで自身のプレイを分析できるようになることを目標としています。
プレイデータの出力
プレイデータ分析を行うためには、まずはデータがなければ始まりません。というわけで、データの出力方法から解説します。civ4にはデータを出力するような機能は存在しないため、Modで解決します。
使用しているModのCvEventManager.py の onEndPlayerTurn関数に、ファイルにデータ出力するためのコードを追加します。openからcloseまでがデータ出力用の追加処理です。
def onEndPlayerTurn(self, argsList):
'Called at the end of a players turn'
iGameTurn, iPlayer = argsList
if (gc.getGame().getElapsedGameTurns() == 1):
if (gc.getPlayer(iPlayer).isHuman()):
if (gc.getPlayer(iPlayer).canRevolution(0)):
popupInfo = CyPopupInfo()
popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_CHANGECIVIC)
popupInfo.addPopup(iPlayer)
f = open('D:/Temporary/civ4log.csv', 'a')
player = gc.getPlayer(iPlayer)
(loopCity, iter) = player.firstCity(false)
while(loopCity):
f.write('%s,%s,%s,%f,%f,%f,%f,%f,%f\n'
% (gc.getGame().getElapsedGameTurns(),
player.getNameKey(),
loopCity.getNameKey(),
loopCity.getBaseCommerceRate(CommerceTypes.COMMERCE_GOLD) * loopCity.getTotalCommerceRateModifier(CommerceTypes.COMMERCE_GOLD) / 100,
loopCity.getBaseCommerceRate(CommerceTypes.COMMERCE_RESEARCH) * loopCity.getTotalCommerceRateModifier(CommerceTypes.COMMERCE_RESEARCH) / 100,
loopCity.getBaseCommerceRate(CommerceTypes.COMMERCE_CULTURE) * loopCity.getTotalCommerceRateModifier(CommerceTypes.COMMERCE_CULTURE) / 100,
loopCity.getBaseCommerceRate(CommerceTypes.COMMERCE_ESPIONAGE) * loopCity.getTotalCommerceRateModifier(CommerceTypes.COMMERCE_ESPIONAGE) / 100,
loopCity.getCurrentProductionDifference(true, false),
loopCity.getMaintenance()
))
(loopCity, iter) = player.nextCity(iter, false)
f.close()
CvAdvisorUtils.resetAdvisorNags()
CvAdvisorUtils.endTurnFeats(iPlayer)
この変更を行った状態でゲームをプレイすると、D:/Temporary/civ4log.csv に、毎ターン全指導者の都市毎の情報が出力されます。出力される情報は以下のとおりです。
- ターン数
- 指導者名
- 都市名
- 金銭
- 研究
- 文化出力
- スパイポイント
- 都市の維持費
なお、ゲームをロードすると同じターンの情報が重複して出力されてしまいますので、重複分は手動で削除する等してください。
今からゲームプレイするのはちょっと面倒…という方は、こちらに私のプレイデータを配置してありますので利用してください。
このデータは
- 難易度: 国王
- パンゲア
- 指導者: ペリクレス
- 351T非戦宇宙勝利
- 小屋都市6、製材所型生産都市1、国立公園&民族叙事詩都市1
- 建てた代表的な世界遺産: ファロス灯台, マウソロス霊廟, 電気系遺産
- シド寿司 & マインニング社あり
という比較的オーソドックスな非戦勝利スタイルのものです。
立地はこんな感じ。
Pandas環境の準備
Pandasを利用するため、以下のURLからAnaconda3をダウンロードしインストールします。
https://www.continuum.io/downloads
また、デフォルト状態ではグラフ中で日本語を表示することができないため、以下の手順に従い、フォントのインストールを行います。
http://kaisk.hatenadiary.com/entry/2015/02/15/215831
分析の実施
Pandas実行環境の起動
スタートメニューからAnaconda3 - Jupyter notebookを選択してください。
Pandasの実行環境であるJupyter notebookが起動し、ブラウザ上にJupyterの画面が表示されたら成功です。
CSVヘッダの追加
CSVファイルの最初の行に以下のヘッダを追加してください。この行は、Pandasが列名を認識するために必要となります。
ターン数,指導者,都市名,金銭,研究,文化,スパイ,生産,維持費
CSVファイルの読み込みとデータの表示
Jupyterに以下のコードを入力して実行してみてください。コードを入力後、Ctrl+Enterキーを押すことで実行できます。
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
# とりあえず生データを全部出力
data = pd.read_csv('d:/Temporary/civ4log.csv')
data.head(6)
以下のような結果が出力されたはずです。記事スペースの関係上6行しか表示していませんが、head(6)の箇所の数値を書き換えると、任意の行数の結果が表示できます。また、.head(6) を丸ごと削除することですべての行を表示することが可能です。
ターン数 | 指導者 | 都市名 | 金銭 | 研究 | 文化 | スパイ | 生産 | 維持費 | |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 |
1 | 0 | TXT_KEY_LEADER_WANGKON | TXT_KEY_CITY_NAME_SEOUL | 0.0 | 9.0 | 2.0 | 4.0 | 2.0 | 0.0 |
2 | 0 | TXT_KEY_LEADER_HAMMURABI | TXT_KEY_CITY_NAME_BABYLON | 0.0 | 9.0 | 2.0 | 4.0 | 1.0 | 0.0 |
3 | 0 | TXT_KEY_LEADER_PACAL | TXT_KEY_CITY_NAME_MUTAL | 0.0 | 9.0 | 2.0 | 4.0 | 1.0 | 0.0 |
4 | 0 | TXT_KEY_LEADER_SITTING_BULL | TXT_KEY_CITY_NAME_CAHOKIA | 0.0 | 11.0 | 2.0 | 4.0 | 1.0 | 0.0 |
5 | 0 | TXT_KEY_LEADER_GILGAMESH | TXT_KEY_CITY_NAME_URUK | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 |
分析方針について
このあたりで分析の目標を定めましょう。とりあえず今回は、自国の各都市が建てられてからゲーム終了時である351Tめまでに、どれだけの研究力および生産力を生み出したか算出してみることにします。
情報の絞込みと追加
先ほど表示した情報のうち、指導者の列がkojimでない行はAIの都市に関する情報です。これらは今回の分析では不要であるため、除去してしまいましょう。
まずはJupyterのメニューから Insert - Insert Cell Belowを選び、コード入力用のセルを1つ追加します。追加されたセルに以下のコードを入力し、実行してみてください。
# AIのデータは邪魔なので除外
data2 = data.query("指導者 == 'kojim'")
data2
ターン数 | 指導者 | 都市名 | 金銭 | 研究 | 文化 | スパイ | 生産 | 維持費 | |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 |
7 | 1 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 |
14 | 2 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 |
21 | 3 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 |
28 | 4 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 |
35 | 5 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 |
見事自都市のデータだけに絞り込まれました。
続いて、都市の出力情報を大まかに表す列を追加してみましょう。このゲームにおいて都市の出力は、金銭出力, 研究出力, スパイポイント出力, 生産力を足し、そこから維持費を引くことでざっくりと表すことができます。
文化力も足したほうが良い、スパイポイントは不要ではないか、と思う方は各自アレンジしてみてください。
偉人ポイント?忘れてください。
それではセルをもう1つ追加した上で、以下のコードを入力&実行してみてください。
data2['出力'] = data2['金銭'] + data2['研究'] + data2['スパイ'] + data2['生産'] - data2['維持費']
data2
ターン数 | 指導者 | 都市名 | 金銭 | 研究 | 文化 | スパイ | 生産 | 維持費 | 出力 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 | 15.0 |
7 | 1 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 | 15.0 |
14 | 2 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 10.0 | 4.0 | 4.0 | 1.0 | 0.0 | 15.0 |
21 | 3 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 | 16.0 |
28 | 4 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 | 16.0 |
35 | 5 | kojim | TXT_KEY_CITY_NAME_ATHENS | 0.0 | 11.0 | 4.0 | 4.0 | 1.0 | 0.0 | 16.0 |
新しい列が追加されましたね。
都市毎の集約
次は都市毎のデータの集約です。
データを集約するにはgroupbyを使用します。groupbyでは集約のキーにしたい項目名をパラメタに渡してやる必要があります。今回は都市ごとの集計値を算出したいので都市名をパラメタに渡してやりましょう。
gdata = data2.groupby('都市名').sum()
gdata
ターン数 | 金銭 | 研究 | 文化 | スパイ | 生産 | 維持費 | 出力 | |
---|---|---|---|---|---|---|---|---|
都市名 | ||||||||
TXT_KEY_CITY_NAME_ARGOS | 57498 | 2514.0 | 20014.0 | 5771.0 | 3105.0 | 6294.0 | 1577.0 | 30350.0 |
TXT_KEY_CITY_NAME_ATHENS | 61776 | 3323.0 | 98223.0 | 15597.0 | 4112.0 | 20076.0 | 1473.0 | 124261.0 |
TXT_KEY_CITY_NAME_CORINTH | 60398 | 893.0 | 19968.0 | 24386.0 | 4604.0 | 10552.0 | 1815.0 | 34202.0 |
TXT_KEY_CITY_NAME_KNOSSOS | 57120 | 1117.0 | 20258.0 | 6893.0 | 5789.0 | 9265.0 | 1282.0 | 35147.0 |
TXT_KEY_CITY_NAME_MYCENAE | 56316 | 731.0 | 18085.0 | 7778.0 | 4036.0 | 8277.0 | 1358.0 | 29771.0 |
TXT_KEY_CITY_NAME_PHARSALOS | 56105 | 167.0 | 4570.0 | 10231.0 | 2729.0 | 22216.0 | 1747.0 | 27935.0 |
TXT_KEY_CITY_NAME_SPARTA | 60741 | 3521.0 | 12459.0 | 15164.0 | 4042.0 | 8050.0 | 2113.0 | 25959.0 |
TXT_KEY_CITY_NAME_THEBES | 59885 | 20647.0 | 29434.0 | 8364.0 | 3550.0 | 10785.0 | 2144.0 | 62272.0 |
集計方法にsum、つまり合計値を指定したので各列は都市毎の合計値が表示されています。さてこのデータを眺めていて気がつくことがあると思います。
- 研究力と生産がずば抜けて高いのは首都であるATHENS
- 生産都市であるPHARSALOSの生産力は首都のそれを上回る
- 文化力トップは国境最前線で王権の首都の文化力と押し合っていたCORINTH
- 小屋を張っていなかった偉人都市のSPARTAの研究力はお察し
- 企業本社のあったTHEBESは金銭収入が1桁違う
続きの記事
いまかいてます