Example #1
0
    def __init__(self, game):
        self.game = game
        self.turn_ordered_players = game.get_player_decks(
            sort_by_turn_order=True)
        self.supply = ConvertibleDefaultDict(value_type=int)
        num_players = len(game.get_player_decks())
        for cardinst in set(EVERY_SET_CARDS + game.get_supply()):
            self.supply[cardinst] = cardinst.num_copies_per_game(num_players)

        self.player_decks = ConvertibleDefaultDict(
            value_type=lambda: ConvertibleDefaultDict(int))
        self.player_start_decks = ConvertibleDefaultDict(
            value_type=lambda: ConvertibleDefaultDict(int))
        self.player_vp_tokens = collections.defaultdict(int)

        if game.player_start_decks is None:
            self.supply[dominioncards.Copper] = self.supply[dominioncards.Copper] - (len(self.turn_ordered_players) * 7)

            for player in self.turn_ordered_players:
                self.player_decks[player.name()][dominioncards.Copper] = 7
                self.player_decks[player.name()][dominioncards.Estate] = 3
        else:
            for start_deck in game.player_start_decks:
                for card in start_deck[START_DECK]:
                    self.supply[dominioncards.index_to_card(card)] -= 1
                    self.player_decks[start_deck[NAME]][dominioncards.index_to_card(card)] += 1

        self.turn_ind = 0
Example #2
0
    def __init__(self, game_dict):
        self.turns = []
        self.supply = [index_to_card(i) for i in game_dict[SUPPLY]]
        self.vetoes = game_dict.get(VETO, {})
        # pprint.pprint(game_dict)

        self.player_decks = [PlayerDeck(pd, self) for pd in game_dict[DECKS]]
        self.id = game_dict.get('_id', '')

        for raw_pd, pd in zip(game_dict[DECKS], self.player_decks):
            turn_ct = 0
            poss_ct = 0
            out_ct = 0
            for turn in raw_pd[TURNS]:
                if POSSESSION in turn:
                    poss_ct += 1
                elif OUTPOST in turn:
                    out_ct = 1
                else:
                    turn_ct += 1
                    poss_ct, out_ct = 0, 0
                self.turns.append(Turn(turn, game_dict, pd, turn_ct, poss_ct))
            pd.set_num_turns(turn_ct)

        self.turns.sort(key=lambda x: (x.get_turn_no(),
                                       x.get_player().TurnOrder(),
                                       x.get_poss_no()))
Example #3
0
    def __init__(self, game_dict):
        self.turns = []
        self.supply = [index_to_card(i) for i in game_dict[SUPPLY]]
        self.vetoes = game_dict.get(VETO, {})
        # pprint.pprint(game_dict)

        self.player_decks = [PlayerDeck(pd, self) for pd in game_dict[DECKS]]
        self.id = game_dict.get('_id', '')

        for raw_pd, pd in zip(game_dict[DECKS], self.player_decks):
            turn_ct = 0
            poss_ct = 0
            out_ct = 0
            for turn in raw_pd[TURNS]:
                if POSSESSION in turn:
                    poss_ct += 1
                elif OUTPOST in turn:
                    out_ct = 1
                else:
                    turn_ct += 1
                    poss_ct, out_ct = 0, 0
                self.turns.append(Turn(turn, game_dict, pd, turn_ct, poss_ct))
            pd.set_num_turns(turn_ct)

        self.turns.sort(key=lambda x: (x.get_turn_no(), x.get_player().
                                       TurnOrder(), x.get_poss_no()))
Example #4
0
def track_brokenness(log, parse_error_col, parsed_games):
    """Print some summary statistics about cards that cause bad parses."""
    failures = 0
    wrongness = collections.defaultdict(int)
    overall = collections.defaultdict(int)
    for raw_game in parsed_games:
        accurately_parsed = check_game_sanity(game.Game(raw_game), log)
        if not accurately_parsed:
            log.warning('Failed to accurately parse game %s', raw_game['_id'])
            save_parse_error(parse_error_col, log, raw_game, 'check_game_sanity failed')
            failures += 1
        for card in raw_game[SUPPLY]:
            if not accurately_parsed:
                wrongness[card] += 1
            overall[card] += 1

    ratios = []
    for card in overall:
        ratios.append(((float(wrongness[card]) / overall[card]), index_to_card(card)))
    ratios.sort()
    if ratios and ratios[-1][0] > 0:
        log.warning("Ratios for problem cards %s, %d failures out of %d games", ratios[-10:],
                    failures, len(parsed_games))
    else:
        log.debug('Perfect parsing, %d games!', len(parsed_games))
Example #5
0
 def __init__(self, player_deck_dict, game):
     self.raw_player = player_deck_dict
     self.game = game
     self.player_name = player_deck_dict[NAME]
     self.win_points = player_deck_dict[WIN_POINTS]
     self.points = player_deck_dict[POINTS]
     self.deck = {}
     for (index, count) in player_deck_dict[DECK].iteritems():
         self.deck[ index_to_card(int(index)) ] = count
     self.turn_order = player_deck_dict[ORDER]
     self.num_real_turns = 0
Example #6
0
 def __init__(self, player_deck_dict, game):
     self.raw_player = player_deck_dict
     self.game = game
     self.player_name = player_deck_dict[NAME]
     self.win_points = player_deck_dict[WIN_POINTS]
     self.points = player_deck_dict[POINTS]
     self.deck = {}
     for (index, count) in player_deck_dict[DECK].iteritems():
         self.deck[index_to_card(int(index))] = count
     self.turn_order = player_deck_dict[ORDER]
     self.num_real_turns = 0
Example #7
0
def parse_game(game_str, dubious_check = False):
    """ Parse game_str into game dictionary.

    game_str: Entire contents of a log file from goko.
    dubious_check: If true, raise a BogusGame exception if the game is
      suspicious.

    returns a dict with the following fields:
      decks: A list of player decks, as documented in parse_endgame.
      start_decks: A list of player starting decks. Usually 7c3e. 
      supply: A list of cards in the supply.
      players: A list of normalized player names.
      game_end: List of cards exhausted that caused the game to end.
      resigned: True iff some player in the game resigned..
    """

    # Goko logs are not split into sections by an obvious separator
    # So analyze sequentially, by lines
    log_lines = game_str.split('\n')

    game_dict = parse_header(log_lines)
    # start_decks, players, and supply are now set

    validate_names(game_dict, dubious_check)
    game_dict[VETO] = {}

    # So much work just to know when two piles are empty for cities! 
    # Here, need to account for number of coppers/zapped silvers in all start
    # decks. Can't get estates to work with zapped start decks and shelters... 
    removed_from_supply = collections.defaultdict(lambda: 0)
    for d in game_dict[START_DECKS]:
        for c in d[START_DECK]:
            card = index_to_card(c)
            if card != dominioncards.Estate and not card.is_shelter():
                removed_from_supply[card] += 1

    turns = parse_turns(log_lines, game_dict[PLAYERS], removed_from_supply)
    decks = parse_endgame(log_lines)
    game_dict[DECKS] = decks
    associate_turns_with_owner(game_dict, turns, dubious_check)
    return game_dict
Example #8
0
def turn_decode(turn_dict, field):
    return [index_to_card(i) for i in turn_dict.get(field, [])]
Example #9
0
def turn_decode(turn_dict, field):
    return [index_to_card(i) for i in turn_dict.get(field, [])]
Example #10
0
 def name_getter(ind_str):
     return dominioncards.index_to_card(int(ind_str)).singular
Example #11
0
    def GET(self):
        web.header("Content-Type", "text/html; charset=utf-8")  

        query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING']))
        target_player = query_dict['player'].decode('utf-8')

        db = utils.get_mongo_database()
        game_stats = db.game_stats
        norm_target_player = norm_name(target_player)
        games_coll = game_stats.find({compkey('_id', NAME): norm_target_player})

        leaderboard_history_result = db.leaderboard_history.find_one(
            {'_id': norm_target_player})
        leaderboard_history = None
        if leaderboard_history_result:
            leaderboard_history = leaderboard_history_result['history']

        game_list = []
        aliases = set()

        overall_record = RecordSummary()
        rec_by_game_size = collections.defaultdict(RecordSummary)
        rec_by_date = collections.defaultdict(RecordSummary)
        rec_by_turn_order =  collections.defaultdict(RecordSummary)

        expansion_dist = collections.defaultdict(float)
        expansion_win_points = collections.defaultdict(float)

        date_buckets = [1, 3, 5, 10]
        cutoffs = {}
        for delta in date_buckets:
            cutoff = datetime.datetime.now().date() + datetime.timedelta(days = -delta)
            cutoffs[delta] = cutoff.strftime("%Y%m%d")

        # NOTE: This assumes that game IDs can be lexically sorted
        # into temporal order
        for g in games_coll.sort('_id', pymongo.DESCENDING):
            g_id = g['_id']['game_id']
            game_list.append(g_id)

            name = g['_id'][NAME]

            # TODO: Turn this back. The concept of aliases only comes
            #into play when two different "real" player names both
            #normalize to the same "normalized" player name.
            # aliases.add(target_player_cur_name)

            wp = g[WIN_POINTS]
            res = g[RESULT]
            overall_record.record_result(res, wp)
            game_len = len( g[PLAYERS] ) + 1
            rec_by_game_size[game_len].record_result(res, wp)

            _ord = g[ORDER]
            rec_by_turn_order[_ord].record_result(res, wp)
            for delta in date_buckets:
                if g['game_date'] >= cutoffs[delta]:
                    rec_by_date[delta].record_result(res, wp)
            supply = [dominioncards.index_to_card(i) for i in g[SUPPLY]]

            for (ex, wt) in dominioncards.get_expansion_weight(supply).items():
                expansion_dist[ex] += wt
                expansion_win_points[ex] += wt * wp

        #TODO: a good choice for a template like jinja2
        ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % 
                               target_player)

        ret += '<form action="/player" method="get">'
        ret += '<span class="subhead">Profile for %s</span>' % target_player

        leaderboard_history_most_recent = (leaderboard_history[-1] if 
                                           leaderboard_history else None)
        if leaderboard_history_most_recent:
            level = (leaderboard_history_most_recent[1] - 
                     leaderboard_history_most_recent[2])
            level = int(max(math.floor(level), 0))
            ret += '<span class="level">Level ' + str(level) + '</span>'

        ret += '<span class="search2">'
        ret += """
               Search for another player:
               <input type="text" name="player" style="width:100px;" />
               <input type="submit" value="View Stats!" />
               </span></form><br><br>
               """

        if len(aliases) > 1:
            ret += 'Aliases: ' + ', '.join(aliases) + '\n'


        ret += render_record_table('Record by game size', overall_record,
                                   rec_by_game_size,
                                   lambda game_size: '%d players' % game_size)
        ret += render_record_table('Recent Record', overall_record,
                                   rec_by_date,
                                   lambda num_days: 'Last %d days' % num_days)
        ret += render_record_table('Record by turn order', overall_record,
                                   rec_by_turn_order,
                                   lambda pos: 'Table position %d' % pos)

        ret += '<div style="clear: both;">&nbsp;</div>'
        ret += '<div class="cardborder yellow"><h3>Expansion Data</h3><table class="stats">'
        ret += '<tr><th>Card Set<th>Avg. Cards<br/> Per Kingdom<th>Weighted<br/> Win Points<th>Favor'

        for (ex, weight) in sorted(expansion_dist.iteritems(), 
                      key=operator.itemgetter(1), reverse=True):

            if ex == 'Fan':
                continue

            wp = expansion_win_points[ex] / weight
            average = overall_record.average_win_points()

            ret += '<tr><th>%s</th>'%ex
            ret += '<td>%.2f</td>'% (weight * 10. / len(game_list))
            ret += '<td>%.2f<td>' % wp
            if average > 0:
                ret += '<td>%.2f%%</td>'% ( (wp - average) * 100. / average )
            else:
                ret += '<td>0</td>' 
        ret += '</table></div>'

        ret += '<div style="clear: both;">&nbsp;</div>'

        ret += goals.MaybeRenderGoals(db, norm_target_player)

        ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A>\n' % target_player
        ret += '<A HREF="/games_by_opponent?player=%s"><h2>Record by opponent</h2></A>\n' % target_player

        if leaderboard_history:
            render = web.template.render('')
            ret += str(render.player_page_leaderboard_history_template(
                    json.dumps(leaderboard_history)))

        ret += '<h2>Most recent games</h2>\n'
        qm = query_matcher.QueryMatcher(p1_name=target_player)
        goko_games = [g for g in game_list if '.txt' in game_list]
        if len(goko_games) > 2:
            goko_games.sort(reverse=True)
            most_recent = goko_games[:3]
        else:
            most_recent = game_list[:3]
        for g_id in most_recent:
            g = db.games.find_one({'_id': g_id})
            game_val = game.Game(g)
            ret += (query_matcher.GameMatcher(game_val, qm).display_game_snippet() +
                    '<br>')

        ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % 
                target_player)
        ret += '</body></html>'

        return ret
Example #12
0
 def name_getter(ind_str):
     return dominioncards.index_to_card(int(ind_str)).singular
Example #13
0
    def GET(self):
        web.header("Content-Type", "text/html; charset=utf-8")  

        query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING']))
        target_player = query_dict['player'].decode('utf-8')

        db = utils.get_mongo_database()
        game_stats = db.game_stats
        norm_target_player = norm_name(target_player)
        games_coll = game_stats.find({compkey('_id', NAME): norm_target_player})

        leaderboard_history_result = db.leaderboard_history.find_one(
            {'_id': norm_target_player})
        leaderboard_history = None
        if leaderboard_history_result:
            leaderboard_history = leaderboard_history_result['history']

        game_list = []
        aliases = set()

        overall_record = RecordSummary()
        rec_by_game_size = collections.defaultdict(RecordSummary)
        rec_by_date = collections.defaultdict(RecordSummary)
        rec_by_turn_order =  collections.defaultdict(RecordSummary)

        expansion_dist = collections.defaultdict(float)
        expansion_win_points = collections.defaultdict(float)

        date_buckets = [1, 3, 5, 10]
        cutoffs = {}
        for delta in date_buckets:
            cutoff = datetime.datetime.now().date() + datetime.timedelta(days = -delta)
            cutoffs[delta] = cutoff.strftime("%Y%m%d")

        # NOTE: This assumes that game IDs can be lexically sorted
        # into temporal order
        for g in games_coll.sort('_id', pymongo.DESCENDING):
            g_id = g['_id']['game_id']
            game_list.append(g_id)

            name = g['_id'][NAME]

            # TODO: Turn this back. The concept of aliases only comes
            #into play when two different "real" player names both
            #normalize to the same "normalized" player name.
            # aliases.add(target_player_cur_name)

            wp = g[WIN_POINTS]
            res = g[RESULT]
            overall_record.record_result(res, wp)
            game_len = len( g[PLAYERS] ) + 1
            rec_by_game_size[game_len].record_result(res, wp)

            _ord = g[ORDER]
            rec_by_turn_order[_ord].record_result(res, wp)
            for delta in date_buckets:
                if g['game_date'] >= cutoffs[delta]:
                    rec_by_date[delta].record_result(res, wp)
            supply = [dominioncards.index_to_card(i) for i in g[SUPPLY]]

            for (ex, wt) in dominioncards.get_expansion_weight(supply).items():
                expansion_dist[ex] += wt
                expansion_win_points[ex] += wt * wp


        #TODO: a good choice for a template like jinja2
        ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % 
                               target_player)

        ret += '<form action="/player" method="get">'
        ret += '<span class="subhead">Profile for %s</span>' % target_player

        leaderboard_history_most_recent = (leaderboard_history[-1] if 
                                           leaderboard_history else None)
        if leaderboard_history_most_recent:
            level = (leaderboard_history_most_recent[1] - 
                     leaderboard_history_most_recent[2])
            level = int(max(math.floor(level), 0))
            ret += '<span class="level">Level ' + str(level) + '</span>'

        ret += '<span class="search2">'
        ret += """
               Search for another player:
               <input type="text" name="player" style="width:100px;" />
               <input type="submit" value="View Stats!" />
               </span></form><br><br>
               """

        if len(aliases) > 1:
            ret += 'Aliases: ' + ', '.join(aliases) + '\n'


        ret += render_record_table('Record by game size', overall_record,
                                   rec_by_game_size,
                                   lambda game_size: '%d players' % game_size)
        ret += render_record_table('Recent Record', overall_record,
                                   rec_by_date,
                                   lambda num_days: 'Last %d days' % num_days)
        ret += render_record_table('Record by turn order', overall_record,
                                   rec_by_turn_order,
                                   lambda pos: 'Table position %d' % pos)

        ret += '<div style="clear: both;">&nbsp;</div>'
        ret += '<div class="cardborder yellow"><h3>Expansion Data</h3><table class="stats">'
        ret += '<tr><th>Card Set<th>Avg. Cards<br/> Per Kingdom<th>Weighted<br/> Win Points<th>Favor'

        for (ex, weight) in sorted(expansion_dist.iteritems(), 
                      key=operator.itemgetter(1), reverse=True):

            if ex == 'Fan':
                continue

            wp = expansion_win_points[ex] / weight
            average = overall_record.average_win_points()

            ret += '<tr><th>%s</th>'%ex
            ret += '<td>%.2f</td>'% (weight * 10. / len(game_list))
            ret += '<td>%.2f<td>' % wp
            if average > 0:
                ret += '<td>%.2f%%</td>'% ( (wp - average) * 100. / average )
            else:
                ret += '<td>0</td>' 
        ret += '</table></div>'

        ret += '<div style="clear: both;">&nbsp;</div>'

        ret += goals.MaybeRenderGoals(db, norm_target_player)

        ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A>\n' % target_player
        ret += '<A HREF="/games_by_opponent?player=%s"><h2>Record by opponent</h2></A>\n' % target_player

        if leaderboard_history:
            render = web.template.render('')
            ret += str(render.player_page_leaderboard_history_template(
                    json.dumps(leaderboard_history)))

        ret += '<h2>Most recent games</h2>\n'
        qm = query_matcher.QueryMatcher(p1_name=target_player)
        for g_id in game_list[:3]:
            g = db.games.find_one({'_id': g_id})
            game_val = game.Game(g)
            ret += (query_matcher.GameMatcher(game_val, qm).display_game_snippet() +
                    '<br>')

        ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % 
                target_player)
        ret += '</body></html>'

        return ret