Skip to content

Instantly share code, notes, and snippets.

@A-w-K
Created December 20, 2018 12:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save A-w-K/91446718a46f3e001c19533298b5756c to your computer and use it in GitHub Desktop.
Save A-w-K/91446718a46f3e001c19533298b5756c to your computer and use it in GitHub Desktop.
added some more statistics to final output
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()
@A-w-K
Copy link
Author

A-w-K commented Dec 20, 2018

Sample run. Stats added in order: highest score reached, average score reached, average win-score

	GoTo20Bot:       0.357 (  4203/ 11761)     71 24.635 42.765
	EnsureLead:      0.355 (  4179/ 11771)     63 24.070 43.415
	StepBot:         0.341 (  4056/ 11880)     68 23.413 44.166
	GoTo16Bot:       0.338 (  4031/ 11917)     64 25.477 43.258
	FooBot:          0.332 (  3918/ 11799)     70 25.831 42.965
	QuotaBot:        0.326 (  3842/ 11781)     68 23.115 45.107
	AdaptiveRoller:  0.306 (  3574/ 11681)     49 25.889 41.776
	BringMyOwn_dice: 0.271 (  3168/ 11710)     49 24.420 41.811
	Chaser:          0.268 (  3130/ 11684)     70 21.881 47.245
	LeadBy5Bot:      0.261 (  3060/ 11709)     73 20.438 46.308
	TakeFive:        0.260 (  3036/ 11681)     68 23.060 43.569
	ExpectationsBot: 0.244 (  2866/ 11744)     49 27.947 41.867
	AWKBot:          0.239 (  2803/ 11731)     49 26.011 41.862
	GoToTenBot:      0.175 (  2054/ 11768)     65 26.036 46.355
	LastRound:       0.156 (  1829/ 11721)     59 23.659 49.145
	OneInFiveBot:    0.109 (  1281/ 11787)    107 19.606 49.930
	ThrowTwiceBot:   0.040 (   480/ 11875)     54 21.651 43.840

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment