Example #1
0
def admin_fetchgames():
    """ Return a JSON representation of all finished games """
    q = GameModel.query(GameModel.over == True).order(GameModel.ts_last_move)
    gamelist = []
    for gm in q.fetch():
        gamelist.append(dict(
            id = gm.key.id(),
            ts = Alphabet.format_timestamp(gm.timestamp),
            lm = Alphabet.format_timestamp(gm.ts_last_move or gm.timestamp),
            p0 = None if gm.player0 is None else gm.player0.id(),
            p1 = None if gm.player1 is None else gm.player1.id(),
            rl = gm.robot_level,
            s0 = gm.score0,
            s1 = gm.score1,
            pr = gm.prefs
        ))
    return jsonify(gamelist = gamelist)
Example #2
0
def admin_fetchgames():
    """ Return a JSON representation of all finished games """
    # noinspection PyPep8
    q = GameModel.query(GameModel.over == True).order(GameModel.ts_last_move)
    gamelist = []
    for gm in q.fetch():
        gamelist.append(
            dict(id=gm.key.id(),
                 ts=Alphabet.format_timestamp(gm.timestamp),
                 lm=Alphabet.format_timestamp(gm.ts_last_move or gm.timestamp),
                 p0=None if gm.player0 is None else gm.player0.id(),
                 p1=None if gm.player1 is None else gm.player1.id(),
                 rl=gm.robot_level,
                 s0=gm.score0,
                 s1=gm.score1,
                 pr=gm.prefs))
    return jsonify(gamelist=gamelist)
Example #3
0
def _run_stats(from_time, to_time):
    """ Runs a process to update user statistics and Elo ratings """

    logging.info(u"Generating stats from {0} to {1}".format(from_time, to_time))

    if from_time is None or to_time is None:
        # Time range must be specified
        return

    if from_time >= to_time:
        # Null time range
        return

    # Iterate over all finished games within the time span in temporal order
    q = GameModel.query(GameModel.over == True).order(GameModel.ts_last_move) \
        .filter(GameModel.ts_last_move > from_time) \
        .filter(GameModel.ts_last_move <= to_time)

    # The accumulated user statistics
    users = dict()

    def _init_stat(user_id, robot_level):
        """ Returns the newest StatsModel instance available for the given user """
        return StatsModel.newest_before(from_time, user_id, robot_level)

    cnt = 0
    ts_last_processed = None

    try:
        # Use i as a progress counter
        for i, gm in enumerate(q):
            ts = Alphabet.format_timestamp(gm.timestamp)
            lm = Alphabet.format_timestamp(gm.ts_last_move or gm.timestamp)
            p0 = None if gm.player0 is None else gm.player0.id()
            p1 = None if gm.player1 is None else gm.player1.id()
            robot_game = (p0 is None) or (p1 is None)
            if robot_game:
                rl = gm.robot_level
            else:
                rl = 0
            s0 = gm.score0
            s1 = gm.score1

            if (s0 == 0) and (s1 == 0):
                # When a game ends by resigning immediately,
                # make sure that the weaker player
                # doesn't get Elo points for a draw; in fact,
                # ignore such a game altogether in the statistics
                continue

            if p0 is None:
                k0 = "robot-" + str(rl)
            else:
                k0 = p0
            if p1 is None:
                k1 = "robot-" + str(rl)
            else:
                k1 = p1

            if k0 in users:
                urec0 = users[k0]
            else:
                users[k0] = urec0 = _init_stat(p0, rl if p0 is None else 0)
            if k1 in users:
                urec1 = users[k1]
            else:
                users[k1] = urec1 = _init_stat(p1, rl if p1 is None else 0)
            # Number of games played
            urec0.games += 1
            urec1.games += 1
            if not robot_game:
                urec0.human_games += 1
                urec1.human_games += 1
            # Total scores
            urec0.score += s0
            urec1.score += s1
            urec0.score_against += s1
            urec1.score_against += s0
            if not robot_game:
                urec0.human_score += s0
                urec1.human_score += s1
                urec0.human_score_against += s1
                urec1.human_score_against += s0
            # Wins and losses
            if s0 > s1:
                urec0.wins += 1
                urec1.losses += 1
            elif s1 > s0:
                urec1.wins += 1
                urec0.losses += 1
            if not robot_game:
                if s0 > s1:
                    urec0.human_wins += 1
                    urec1.human_losses += 1
                elif s1 > s0:
                    urec1.human_wins += 1
                    urec0.human_losses += 1
            # Find out whether players are established or beginners
            est0 = urec0.games > ESTABLISHED_MARK
            est1 = urec1.games > ESTABLISHED_MARK
            # Save the Elo point state used in the calculation
            gm.elo0, gm.elo1 = urec0.elo, urec1.elo
            # Compute the Elo points of both players
            adj = _compute_elo((urec0.elo, urec1.elo), s0, s1, est0, est1)
            # When an established player is playing a beginning (provisional) player,
            # leave the Elo score of the established player unchanged
            # Adjust player 0
            if est0 and not est1:
                adj = (0, adj[1])
            gm.elo0_adj = adj[0]
            urec0.elo += adj[0]
            # Adjust player 1
            if est1 and not est0:
                adj = (adj[0], 0)
            gm.elo1_adj = adj[1]
            urec1.elo += adj[1]
            # If not a robot game, compute the human-only Elo
            if not robot_game:
                gm.human_elo0, gm.human_elo1 = urec0.human_elo, urec1.human_elo
                adj = _compute_elo((urec0.human_elo, urec1.human_elo), s0, s1, est0, est1)
                # Adjust player 0
                if est0 and not est1:
                    adj = (0, adj[1])
                gm.human_elo0_adj = adj[0]
                urec0.human_elo += adj[0]
                # Adjust player 1
                if est1 and not est0:
                    adj = (adj[0], 0)
                gm.human_elo1_adj = adj[1]
                urec1.human_elo += adj[1]
            # Save the game object with the new Elo adjustment statistics
            gm.put()
            # Save the last processed timestamp
            ts_last_processed = lm
            cnt += 1
            # Report on our progress
            if (i + 1) % 1000 == 0:
                logging.info(u"Processed {0} games".format(i + 1))

    except DeadlineExceededError as ex:
        # Hit deadline: save the stuff we already have and
        # defer a new task to continue where we left off
        logging.info(u"Deadline exceeded in stats loop after {0} games and {1} users"
            .format(cnt, len(users)))
        logging.info(u"Resuming from timestamp {0}".format(ts_last_processed))
        if ts_last_processed is not None:
            _write_stats(ts_last_processed, users)
        deferred.defer(deferred_stats,
            from_time = ts_last_processed or from_time, to_time = to_time)
        # Normal return prevents this task from being run again
        return

    except Exception as ex:
        logging.info(u"Exception in stats loop: {0}".format(ex))
        # Avoid having the task retried
        raise deferred.PermanentTaskFailure()

    # Completed without incident
    logging.info(u"Normal completion of stats for {1} games and {0} users".format(len(users), cnt))

    _write_stats(to_time, users)
Example #4
0
def _run_stats(from_time, to_time):
    """ Runs a process to update user statistics and Elo ratings """

    logging.info(u"Generating stats from {0} to {1}".format(
        from_time, to_time))

    if from_time is None or to_time is None:
        # Time range must be specified
        return

    if from_time >= to_time:
        # Null time range
        return

    # Iterate over all finished games within the time span in temporal order
    q = GameModel.query(ndb.AND(GameModel.ts_last_move > from_time, GameModel.ts_last_move <= to_time)) \
        .order(GameModel.ts_last_move) \
        .filter(GameModel.over == True)

    # The accumulated user statistics
    users = dict()

    def _init_stat(user_id, robot_level):
        """ Returns the newest StatsModel instance available for the given user """
        return StatsModel.newest_before(from_time, user_id, robot_level)

    cnt = 0
    ts_last_processed = None

    try:
        # Use i as a progress counter
        i = 0
        for gm in iter_q(q, chunk_size=250):
            i += 1
            lm = Alphabet.format_timestamp(gm.ts_last_move or gm.timestamp)
            p0 = None if gm.player0 is None else gm.player0.id()
            p1 = None if gm.player1 is None else gm.player1.id()
            robot_game = (p0 is None) or (p1 is None)
            if robot_game:
                rl = gm.robot_level
            else:
                rl = 0
            s0 = gm.score0
            s1 = gm.score1

            if (s0 == 0) and (s1 == 0):
                # When a game ends by resigning immediately,
                # make sure that the weaker player
                # doesn't get Elo points for a draw; in fact,
                # ignore such a game altogether in the statistics
                continue

            if p0 is None:
                k0 = "robot-" + str(rl)
            else:
                k0 = p0
            if p1 is None:
                k1 = "robot-" + str(rl)
            else:
                k1 = p1

            if k0 in users:
                urec0 = users[k0]
            else:
                users[k0] = urec0 = _init_stat(p0, rl if p0 is None else 0)
            if k1 in users:
                urec1 = users[k1]
            else:
                users[k1] = urec1 = _init_stat(p1, rl if p1 is None else 0)
            # Number of games played
            urec0.games += 1
            urec1.games += 1
            if not robot_game:
                urec0.human_games += 1
                urec1.human_games += 1
            # Total scores
            urec0.score += s0
            urec1.score += s1
            urec0.score_against += s1
            urec1.score_against += s0
            if not robot_game:
                urec0.human_score += s0
                urec1.human_score += s1
                urec0.human_score_against += s1
                urec1.human_score_against += s0
            # Wins and losses
            if s0 > s1:
                urec0.wins += 1
                urec1.losses += 1
            elif s1 > s0:
                urec1.wins += 1
                urec0.losses += 1
            if not robot_game:
                if s0 > s1:
                    urec0.human_wins += 1
                    urec1.human_losses += 1
                elif s1 > s0:
                    urec1.human_wins += 1
                    urec0.human_losses += 1
            # Find out whether players are established or beginners
            est0 = urec0.games > ESTABLISHED_MARK
            est1 = urec1.games > ESTABLISHED_MARK
            # Save the Elo point state used in the calculation
            gm.elo0, gm.elo1 = urec0.elo, urec1.elo
            # Compute the Elo points of both players
            adj = _compute_elo((urec0.elo, urec1.elo), s0, s1, est0, est1)
            # When an established player is playing a beginning (provisional) player,
            # leave the Elo score of the established player unchanged
            # Adjust player 0
            if est0 and not est1:
                adj = (0, adj[1])
            gm.elo0_adj = adj[0]
            urec0.elo += adj[0]
            # Adjust player 1
            if est1 and not est0:
                adj = (adj[0], 0)
            gm.elo1_adj = adj[1]
            urec1.elo += adj[1]
            # If not a robot game, compute the human-only Elo
            if not robot_game:
                gm.human_elo0, gm.human_elo1 = urec0.human_elo, urec1.human_elo
                adj = _compute_elo((urec0.human_elo, urec1.human_elo), s0, s1,
                                   est0, est1)
                # Adjust player 0
                if est0 and not est1:
                    adj = (0, adj[1])
                gm.human_elo0_adj = adj[0]
                urec0.human_elo += adj[0]
                # Adjust player 1
                if est1 and not est0:
                    adj = (adj[0], 0)
                gm.human_elo1_adj = adj[1]
                urec1.human_elo += adj[1]
            # Save the game object with the new Elo adjustment statistics
            gm.put()
            # Save the last processed timestamp
            ts_last_processed = lm
            cnt += 1
            # Report on our progress
            if i % 1000 == 0:
                logging.info(u"Processed {0} games".format(i))

    except DeadlineExceededError as ex:
        # Hit deadline: save the stuff we already have and
        # defer a new task to continue where we left off
        logging.info(
            u"Deadline exceeded in stats loop after {0} games and {1} users".
            format(cnt, len(users)))
        logging.info(u"Resuming from timestamp {0}".format(ts_last_processed))
        if ts_last_processed is not None:
            _write_stats(ts_last_processed, users)
        deferred.defer(deferred_stats,
                       from_time=ts_last_processed or from_time,
                       to_time=to_time)
        # Normal return prevents this task from being run again
        return

    except Exception as ex:
        logging.info(u"Exception in stats loop: {0}".format(ex))
        # Avoid having the task retried
        raise deferred.PermanentTaskFailure()

    # Completed without incident
    logging.info(
        u"Normal completion of stats for {1} games and {0} users".format(
            len(users), cnt))

    _write_stats(to_time, users)