Created
December 20, 2018 12:47
-
-
Save A-w-K/91446718a46f3e001c19533298b5756c to your computer and use it in GitHub Desktop.
added some more statistics to final output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
import sys | |
# Importing all the bots | |
from forty_game_bots import * | |
# Class for handling the game logic and relaying information to the bots | |
class Controller: | |
def __init__(self, bots_per_game, games, bots): | |
"""Initiates all fields relevant to the simulation | |
Keyword arguments: | |
bots_per_game -- the number of bots that should be included in a game | |
games -- the number of games that should be simulated | |
bots -- a list of all available bot classes | |
""" | |
self.bots_per_game = bots_per_game | |
self.games = games | |
self.bots = bots | |
self.number_of_bots = len(self.bots) | |
self.wins = {bot.__name__: 0 for bot in self.bots} | |
self.highscore = {bot.__name__: [0, 0, 0] for bot in self.bots} | |
self.played_games = {bot.__name__: 0 for bot in self.bots} | |
self.end_score = 40 | |
# Returns a fair dice throw | |
def throw_die(self): | |
return random.randint(1,6) | |
# Handles selecting bots for each game, and counting how many times | |
# each bot has participated in a game | |
def simulate_games(self): | |
for game in range(self.games): | |
# Print the current game number without newline | |
print( | |
"\r\t\t%5d/%5d" % (game+1, self.games), | |
flush = True, | |
end = "" | |
) | |
game_bot_indices = random.sample( | |
range(self.number_of_bots), | |
self.bots_per_game | |
) | |
game_bots = [None for _ in range(self.bots_per_game)] | |
for i, bot_index in enumerate(game_bot_indices): | |
self.played_games[self.bots[bot_index].__name__] += 1 | |
game_bots[i] = self.bots[bot_index](i, self.end_score) | |
self.play(game_bots) | |
self.print_results() | |
def play(self, game_bots): | |
"""Simulates a single game between the bots present in game_bots | |
Keyword arguments: | |
game_bots -- A list of instantiated bot objects for the game | |
""" | |
last_round = False | |
last_round_initiator = -1 | |
round_number = 0 | |
game_scores = [0 for _ in range(self.bots_per_game)] | |
# continue until one bot has reached end_score points | |
while not last_round: | |
for index, bot in enumerate(game_bots): | |
self.single_bot(index, bot, game_scores, last_round) | |
if game_scores[index] >= self.end_score: | |
last_round = True | |
last_round_initiator = index | |
round_number += 1 | |
# maximum of 200 rounds per game | |
if round_number > 199: | |
last_round = True | |
# this ensures that everyone gets their last turn | |
last_round_initiator = self.bots_per_game | |
# make sure that all bots get their last round | |
for index, bot in enumerate(game_bots[:last_round_initiator]): | |
self.single_bot(index, bot, game_scores, last_round) | |
# calculate which bots have the highest score | |
max_score = max(game_scores) | |
for i in range(self.bots_per_game): | |
self.highscore[game_bots[i].__class__.__name__][1] += game_scores[i] | |
if self.highscore[game_bots[i].__class__.__name__][0] < game_scores[i]: | |
self.highscore[game_bots[i].__class__.__name__][0] = game_scores[i] | |
if game_scores[i] == max_score: | |
self.highscore[game_bots[i].__class__.__name__][2] += game_scores[i] | |
self.wins[game_bots[i].__class__.__name__] += 1 | |
def single_bot(self, index, bot, game_scores, last_round): | |
"""Simulates a single round for one bot | |
Keyword arguments: | |
index -- The player index of the bot (e.g. 0 if the bot goes first) | |
bot -- The bot object about to be simulated | |
game_scores -- A list of ints containing the scores of all players | |
last_round -- Boolean describing whether it is currently the last round | |
""" | |
# To get more information about the ongoing game, use these two lines | |
# desc = "Bot %d plays with scores %s and last round == %s" | |
# print(desc % (index, game_scores, last_round)) | |
current_throws = [self.throw_die()] | |
if current_throws[-1] != 6: | |
bot.update_state(current_throws[:]) | |
for throw in bot.make_throw(game_scores, last_round): | |
# break if the bot yields False | |
if not throw: | |
break | |
current_throws.append(self.throw_die()) | |
# break if the cast die is a 6 | |
if current_throws[-1] == 6: | |
break | |
# send the last die cast to the bot | |
bot.update_state(current_throws[:]) | |
if current_throws[-1] == 6: | |
# reset total score if running total is above end_score | |
if game_scores[index] + sum(current_throws) - 6 >= self.end_score: | |
game_scores[index] = 0 | |
else: | |
# add to total score if no 6 is cast | |
game_scores[index] += sum(current_throws) | |
# Print the high score after the simulation | |
def print_results(self): | |
print("\n") | |
# Find the name of each bot, the number of wins, the number | |
# of played games, and the win percentage | |
bot_stats = [[ | |
bot.__name__, | |
self.wins[bot.__name__], | |
self.played_games[bot.__name__], | |
0, | |
self.highscore[bot.__name__] | |
] for bot in self.bots] | |
for i, bot in enumerate(bot_stats): | |
if bot[2] > 0: | |
bot[3] = bot[1] / bot[2] | |
bot_stats[i] = tuple(bot) | |
# Sort the bots by their winning percentage | |
sorted_scores = sorted(bot_stats, key=lambda x: x[3], reverse=True) | |
# Find the longest class name for any bot | |
max_len = max([len(b[0]) for b in bot_stats]) | |
# Print the highscore list | |
for bot, wins, played, score, highscore in sorted_scores: | |
space_fill = " "*(max_len-len(bot)+1) | |
format_arguments = (bot, space_fill, score, wins, played, highscore[0], highscore[1] / played, | |
highscore[2] / wins) | |
print("\t%s:%s%.3f (%6d/%6d) %6d %6.3f %6.3f" % format_arguments) | |
print() | |
# Prints the help for the script | |
def print_help(): | |
print("\nThis is the controller for the PPCG KotH challenge " + \ | |
"'A game of dice, but avoid number 6'") | |
print("For any question, send a message to maxb\n") | |
print("Usage: python %s [OPTIONS]" % sys.argv[0]) | |
print("\n -n\t\tthe number of games to simluate") | |
print(" -b\t\tthe number of bots per round") | |
print(" -h\t--help\tshow this help\n") | |
if __name__ == "__main__": | |
bots = get_all_bots() | |
games = 100000 | |
bots_per_game = 4 | |
for i, arg in enumerate(sys.argv): | |
if arg == "-n" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit(): | |
games = int(sys.argv[i+1]) | |
if arg == "-b" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit(): | |
bots_per_game = int(sys.argv[i+1]) | |
if arg == "-h" or arg == "--help": | |
print_help() | |
quit() | |
print() | |
if bots_per_game > len(bots): | |
bots_per_game = len(bots) | |
if bots_per_game < 2: | |
print("\tAt least 2 bots per game is needed") | |
bots_per_game = 2 | |
if games <= 0: | |
print("\tAt least 1 game is needed") | |
games = 1 | |
print("\tStarting simulation with %d bots" % len(bots)) | |
sim_str = "\tSimulating %d games with %d bots per game" | |
print(sim_str % (games, bots_per_game)) | |
if len(sys.argv) == 1: | |
print("\tFor help running the script, use the -h flag") | |
print() | |
controller = Controller(bots_per_game, games, bots) | |
controller.simulate_games() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample run. Stats added in order: highest score reached, average score reached, average win-score