forked from rspeer/dominionstats
-
Notifications
You must be signed in to change notification settings - Fork 17
/
optimal_card_ratios.py
191 lines (154 loc) · 6.5 KB
/
optimal_card_ratios.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/python
# -*- coding: utf-8 -*-
import utils
import pymongo
import incremental_scanner
from primitive_util import PrimitiveConversion, ConvertibleDefaultDict
from stats import MeanVarStat
from game import Game
class DBCardRatioTracker(PrimitiveConversion):
""" This keeps track of every final and progressive card ratio for one
pair of cards.
"""
def __init__(self):
self.final = ConvertibleDefaultDict(MeanVarStat)
self.progressive = ConvertibleDefaultDict(MeanVarStat)
def add_outcome(self, tracker_type, ratio, win_points):
if tracker_type == 'final':
tracker = self.final
elif tracker_type == 'progressive':
tracker = self.progressive
else:
raise
if ratio not in tracker:
tracker[ratio] = MeanVarStat()
tracker[ratio].add_outcome(win_points)
class DBCardRatioTrackerManager:
""" This keeps track of every final and progressive card ratio for all
pairs of cards. This manages DBCardRatioTracker instances.
"""
def __init__(self, collection, incremental=True):
self.collection = collection
self.incremental = incremental
self.trackers = {}
if self.incremental:
for entry in collection.find():
tracker = DBCardRatioTracker()
tracker.from_primitive_object(entry)
self.trackers[entry['_id']] = tracker
def integrate_results(self, tracker_type, ratio_dict, win_points):
for key in ratio_dict:
if key not in self.trackers:
self.trackers[key] = DBCardRatioTracker()
tracker = self.trackers[key]
for ratio in ratio_dict[key]:
ratio = str(ratio[0]) + ':' + str(ratio[1])
tracker.add_outcome(tracker_type, ratio, win_points)
def save(self):
if not self.incremental:
self.collection.drop()
for key, tracker in self.trackers.iteritems():
utils.write_object_to_db(tracker, self.collection, key)
class CardRatioTracker:
""" Base class for the final and progressive card ratio trackers.
"""
def __init__(self, supply):
self.card_counts = {}
for card in [u'Estate', u'Duchy', u'Province', u'Curse', u'Copper', u'Silver', u'Gold'] + supply:
self.card_counts[card] = 0
def get_card_ratios(self):
ratios = {}
for card1 in self.card_counts.iterkeys():
for card2 in self.card_counts.iterkeys():
if card1 < card2:
ratios[card1 + ':' + card2] = set([(self.card_counts[card1], self.card_counts[card2])])
return ratios
class FinalCardRatioTracker(CardRatioTracker):
""" This is used to get the ratios between all of the cards in the supply
that a player has at the end of the game.
"""
def __init__(self, supply):
CardRatioTracker.__init__(self, supply)
def adjust_card_count(self, card, adjustment):
if card not in self.card_counts:
return
self.card_counts[card] += adjustment
def get_ratio_dict(self):
return CardRatioTracker.get_card_ratios(self)
class ProgressiveCardRatioTracker(CardRatioTracker):
""" This tracks all of the ratios between all of the cards in the supply
that a player has at any point throughout the whole game.
"""
def __init__(self, supply):
CardRatioTracker.__init__(self, supply)
self.card_counts[u'Estate'] = 3
self.card_counts[u'Copper'] = 7
self.ratios = self.get_card_ratios()
def adjust_card_count(self, card, adjustment):
if card not in self.card_counts:
return
self.card_counts[card] += adjustment
for card2 in self.card_counts.iterkeys():
if card != card2:
c1, c2 = sorted([card, card2])
self.ratios[c1 + ':' + c2].add((self.card_counts[c1], self.card_counts[c2]))
def get_ratio_dict(self):
return self.ratios
def process_game(game):
names = game.all_player_names()
supply = game.get_supply()
name_to_final_tracker = dict((name, FinalCardRatioTracker(supply)) for name in names)
name_to_progressive_tracker = dict((name, ProgressiveCardRatioTracker(supply)) for name in names)
name_to_win_points = dict((player_deck.name(), player_deck.WinPoints()) for player_deck in game.get_player_decks())
for player_deck in game.get_player_decks():
tracker = name_to_final_tracker[player_deck.name()]
for card, count in player_deck.Deck().iteritems():
tracker.adjust_card_count(card, count)
for turn in game.get_turns():
for deck_change in turn.deck_changes():
tracker = name_to_progressive_tracker[deck_change.name]
for card in deck_change.buys:
tracker.adjust_card_count(card, 1)
for card in deck_change.gains:
tracker.adjust_card_count(card, 1)
for card in deck_change.returns:
tracker.adjust_card_count(card, -1)
for card in deck_change.trashes:
tracker.adjust_card_count(card, -1)
retval = []
for name in names:
retval.append([name_to_final_tracker[name].get_ratio_dict(),
name_to_progressive_tracker[name].get_ratio_dict(),
name_to_win_points[name]])
return retval
def main():
parser = utils.incremental_max_parser()
args = parser.parse_args()
conn = pymongo.Connection()
database = conn.test
games = database.games
collection = database.optimal_card_ratios
db_tracker = None
scanner = incremental_scanner.IncrementalScanner('optimal_card_ratios', database)
if not args.incremental:
scanner.reset()
print scanner.status_msg()
total_checked = 0
for game in scanner.scan(games, {}):
if not db_tracker:
db_tracker = DBCardRatioTrackerManager(collection, args.incremental)
total_checked += 1
result = process_game(Game(game))
for final_ratio_dict, progressive_ratio_dict, win_points in result:
db_tracker.integrate_results('final', final_ratio_dict, win_points)
db_tracker.integrate_results('progressive', progressive_ratio_dict, win_points)
if total_checked % 1000 == 0:
print total_checked
if args.max_games >= 0 and total_checked >= args.max_games:
break
print scanner.status_msg()
if db_tracker:
db_tracker.save()
scanner.save()
if __name__ == '__main__':
main()