Tournoi terminé!
Le tournoi est maintenant terminé! La simulation finale s’est déroulée pendant la nuit, avec un total de parties. Le gagnant est Christian Sievers avec son bot OptFor2X . Christian Sievers a également réussi à s'assurer la deuxième place avec Rebel . Toutes nos félicitations! Ci-dessous, vous pouvez voir la liste officielle des meilleurs scores du tournoi.
Si vous souhaitez toujours jouer au jeu, vous pouvez utiliser le contrôleur affiché ci-dessous et utiliser le code qu'il contient pour créer votre propre jeu.
J'ai été invité à jouer à un jeu de dés dont je n'avais jamais entendu parler. Les règles étaient simples, mais je pense que ce serait parfait pour un défi KotH.
Les règles
Le début du jeu
Le dé fait le tour de la table et chaque fois que c'est votre tour, vous lancez le dé autant de fois que vous le souhaitez. Cependant, vous devez le jeter au moins une fois. Vous gardez une trace de la somme de tous les lancers pour votre tour. Si vous choisissez d'arrêter, le score de la ronde s'ajoute à votre score total.
Alors, pourquoi voudriez-vous jamais arrêter de jeter le dé? Parce que si vous obtenez 6, votre score pour le tour entier devient zéro et le dé est transmis. Ainsi, l'objectif initial est d'augmenter votre score le plus rapidement possible.
Qui est le gagnant?
Lorsque le premier joueur autour de la table atteint 40 points ou plus, le dernier tour commence. Une fois que le dernier tour a commencé, tout le monde sauf celui qui a initié le dernier tour a droit à un tour supplémentaire.
Les règles du dernier tour sont les mêmes que pour tout autre tour. Vous choisissez de continuer à lancer ou de vous arrêter. Cependant, vous savez que vous n’avez aucune chance de gagner si vous n’obtenez pas un score plus élevé que ceux que vous aviez au dernier tour. Mais si vous continuez à aller trop loin, alors vous pourriez obtenir un 6.
Cependant, il y a une autre règle à prendre en compte. Si votre score total actuel (votre score précédent + votre score actuel du tour) est égal ou supérieur à 40 et que vous atteignez un 6, votre score total est défini sur 0. Cela signifie que vous devez tout recommencer. Si vous frappez un 6 lorsque votre score total actuel est de 40 ou plus, le jeu continue normalement, sauf que vous êtes maintenant à la dernière place. Le dernier tour n'est pas déclenché lorsque votre score total est réinitialisé. Vous pouvez toujours gagner la manche, mais cela devient plus difficile.
Le gagnant est le joueur avec le score le plus élevé une fois le dernier tour terminé. Si deux joueurs ou plus partagent le même score, ils seront tous comptés comme vainqueurs.
Une règle supplémentaire est que le jeu continue pour un maximum de 200 tours. Ceci permet d'éviter les cas où plusieurs robots continuent à lancer jusqu'à ce qu'ils atteignent 6 pour rester à leur score actuel. Une fois que le 199ème tour est passé, last_round
est défini sur true et un autre tour est joué. Si le jeu se déroule à 200 tours, le bot (ou les bots) avec le score le plus élevé est le vainqueur, même s’ils n’ont pas 40 points ou plus.
résumer
- À chaque tour, vous continuez à lancer le dé jusqu'à ce que vous choisissiez de vous arrêter ou que vous obteniez un 6
- Vous devez lancer le dé une fois (si votre premier lancer est un 6, votre tour est immédiatement terminé)
- Si vous obtenez un 6, votre score actuel est mis à 0 (pas votre score total)
- Vous ajoutez votre score actuel à votre score total après chaque tour
- Lorsqu'un bot termine son tour, ce qui donne un score total d'au moins 40, tout le monde a son dernier tour.
- Si votre score total actuel est de et que vous obtenez un 6, votre score total est mis à 0 et votre partie est terminée
- Le dernier tour n'est pas déclenché lorsque ce qui précède se produit
- La personne avec le score total le plus élevé après le dernier tour est le gagnant
- Au cas où il y aurait plusieurs gagnants, tous seront comptés comme gagnants.
- Le jeu dure 200 tours maximum
Clarification des scores
- Score total: le score que vous avez enregistré lors des tours précédents
- Score actuel: le score du tour en cours
- Score total actuel: la somme des deux scores ci-dessus
Comment participez-vous
Pour participer à ce défi KotH, vous devez écrire une classe Python dont elle hérite Bot
. Vous devez implémenter la fonction: make_throw(self, scores, last_round)
. Cette fonction sera appelée une fois que c'est votre tour, et votre premier lancer n'était pas un 6. Pour continuer à lancer, vous devriez yield True
. Pour arrêter de lancer, vous devriez yield False
. Après chaque lancer, la fonction parent update_state
est appelée. Ainsi, vous avez accès à vos lancers pour le tour en cours en utilisant la variable self.current_throws
. Vous avez également accès à votre propre index en utilisant self.index
. Ainsi, pour voir votre propre score total, vous utiliseriez scores[self.index]
. Vous pouvez également accéder au end_score
jeu en utilisant self.end_score
, mais vous pouvez supposer en toute sécurité qu'il sera de 40 pour ce défi.
Vous êtes autorisé à créer des fonctions d’aide dans votre classe. Vous pouvez également remplacer des fonctions existantes dans la Bot
classe parente, par exemple si vous souhaitez ajouter d'autres propriétés de classe. Vous n'êtes pas autorisé à modifier l'état du jeu de quelque manière que ce True
soit, sauf céder ou False
.
Vous êtes libre de vous inspirer de ce message et de copier l'un des deux robots que j'ai inclus ici. Cependant, j'ai bien peur qu'ils ne soient pas particulièrement efficaces ...
En autorisant d'autres langues
Dans le bac à sable et sur le dix-neuvième octet, nous avons discuté de l’autorisation des soumissions dans d’autres langues. Après avoir pris connaissance de ces implémentations et entendu les arguments des deux côtés, j'ai décidé de limiter ce défi à Python uniquement. Cela est dû à deux facteurs: le temps nécessaire pour prendre en charge plusieurs langues et le caractère aléatoire de ce défi qui nécessite un grand nombre d'itérations pour atteindre la stabilité. J'espère que vous continuerez à participer et si vous voulez apprendre un peu de Python pour ce défi, je vais essayer d'être disponible dans le chat le plus souvent possible.
Pour toute question que vous pourriez avoir, vous pouvez écrire dans la salle de discussion pour ce défi . On se voit là-bas!
Règles
- Le sabotage est autorisé et encouragé. C'est-à-dire sabotage contre d'autres joueurs
- Toute tentative de modification du contrôleur, de l'exécution ou de toute autre soumission sera rejetée. Toutes les soumissions ne doivent fonctionner qu'avec les entrées et le stockage qui leur sont attribués.
- Tout bot utilisant plus de 500 Mo de mémoire pour prendre sa décision sera disqualifié (si vous avez besoin de plus de mémoire, vous devriez repenser vos choix).
- Un bot ne doit pas implémenter exactement la même stratégie qu'un existant, intentionnellement ou accidentellement.
- Vous êtes autorisé à mettre à jour votre bot pendant la durée du défi. Cependant, vous pouvez également poster un autre bot si votre approche est différente.
Exemple
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Ce bot va continuer jusqu'à ce qu'il ait un score d'au moins 10 pour le tour, ou il jette un 6. Notez que vous ne avez pas besoin de logique pour gérer lancer 6. Notez également que si votre premier jet est un 6, make_throw
est jamais appelé, puisque votre tour est immédiatement terminé.
Pour ceux qui découvrent Python (et qui ne connaissent pas bien le yield
concept), mais qui veulent essayer, le yield
mot clé ressemble à un retour, à certains égards, mais différent à d'autres. Vous pouvez lire sur le concept ici . En gros, une fois que yield
votre fonction sera arrêtée, la valeur que vous yield
aurez modifiée sera renvoyée au contrôleur. Là, le contrôleur gère sa logique jusqu’à ce que votre bot prenne une autre décision. Ensuite, le contrôleur vous envoie le lancer de dés, et votre make_throw
fonction continuera à s’exécuter exactement là où elle s’est arrêtée avant, essentiellement sur la ligne après la yield
déclaration précédente .
De cette façon, le contrôleur de jeu peut mettre à jour l'état sans nécessiter un appel de fonction bot distinct pour chaque lancer de dés.
spécification
Vous pouvez utiliser n’importe quelle bibliothèque Python disponible dans pip
. Pour que je puisse obtenir une bonne moyenne, vous avez une limite de temps de 100 millisecondes par tour. Je serais vraiment heureux si votre script était bien plus rapide que ça, afin que je puisse faire plus de rondes.
Évaluation
Pour trouver le gagnant, je prendrai tous les robots et les ferai par groupes de 8. Si moins de 8 classes ont été soumises, je les ferai par groupes de 4 au hasard pour éviter d'avoir toujours tous les bots à chaque tour. Je vais faire des simulations pendant environ 8 heures, et le gagnant sera le bot avec le pourcentage de victoire le plus élevé. Je lancerai les dernières simulations au début de 2019, en vous donnant tout Noël pour coder vos robots! La date finale préliminaire est le 4 janvier, mais si le temps est compté, je peux le changer pour une date ultérieure.
En attendant, je vais essayer de faire une simulation quotidienne en utilisant 30 à 60 minutes de temps CPU et en mettant à jour le tableau de résultats. Ce ne sera pas le score officiel, mais il servira de guide pour voir quels bots sont les plus performants. Cependant, à l'approche de Noël, j'espère que vous comprendrez que je ne serai pas disponible à tout moment. Je ferai de mon mieux pour exécuter des simulations et répondre à toutes les questions relatives au défi.
Testez-le vous-même
Si vous souhaitez exécuter vos propres simulations, voici le code complet destiné au contrôleur qui exécute la simulation, y compris deux exemples de robots.
Manette
Voici le contrôleur mis à jour pour ce défi. Il supporte les sorties ANSI, le multi-threading et collecte des statistiques supplémentaires grâce à AKroell ! Lorsque je modifie le contrôleur, je mets à jour le message une fois la documentation terminée.
Grâce à BMO , le contrôleur est maintenant en mesure de télécharger tous les robots de cet article en utilisant le -d
drapeau. Les autres fonctionnalités ne sont pas modifiées dans cette version. Cela devrait vous assurer que toutes vos dernières modifications sont simulées dès que possible!
#!/usr/bin/env python3
import re
import json
import math
import random
import requests
import sys
import time
from numpy import cumsum
from collections import defaultdict
from html import unescape
from lxml import html
from multiprocessing import Pool
from os import path, rename, remove
from sys import stderr
from time import strftime
# If you want to see what each bot decides, set this to true
# Should only be used with one thread and one game
DEBUG = False
# If your terminal supports ANSI, try setting this to true
ANSI = False
# File to keep base class and own bots
OWN_FILE = 'forty_game_bots.py'
# File where to store the downloaded bots
AUTO_FILE = 'auto_bots.py'
# If you want to use up all your quota & re-download all bots
DOWNLOAD = False
# If you want to ignore a specific user's bots (eg. your own bots): add to list
IGNORE = []
# The API-request to get all the bots
URL = "https://api.stackexchange.com/2.2/questions/177765/answers?page=%s&pagesize=100&order=desc&sort=creation&site=codegolf&filter=!bLf7Wx_BfZlJ7X"
def print_str(x, y, string):
print("\033["+str(y)+";"+str(x)+"H"+string, end = "", flush = True)
class bcolors:
WHITE = '\033[0m'
GREEN = '\033[92m'
BLUE = '\033[94m'
YELLOW = '\033[93m'
RED = '\033[91m'
ENDC = '\033[0m'
# Class for handling the game logic and relaying information to the bots
class Controller:
def __init__(self, bots_per_game, games, bots, thread_id):
"""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 = defaultdict(int)
self.played_games = defaultdict(int)
self.bot_timings = defaultdict(float)
# self.wins = {bot.__name__: 0 for bot in self.bots}
# self.played_games = {bot.__name__: 0 for bot in self.bots}
self.end_score = 40
self.thread_id = thread_id
self.max_rounds = 200
self.timed_out_games = 0
self.tied_games = 0
self.total_rounds = 0
self.highest_round = 0
#max, avg, avg_win, throws, success, rounds
self.highscore = defaultdict(lambda:[0, 0, 0, 0, 0, 0])
self.winning_scores = defaultdict(int)
# self.highscore = {bot.__name__: [0, 0, 0] for bot in self.bots}
# Returns a fair dice throw
def throw_die(self):
return random.randint(1,6)
# Print the current game number without newline
def print_progress(self, progress):
length = 50
filled = int(progress*length)
fill = "="*filled
space = " "*(length-filled)
perc = int(100*progress)
if ANSI:
col = [
bcolors.RED,
bcolors.YELLOW,
bcolors.WHITE,
bcolors.BLUE,
bcolors.GREEN
][int(progress*4)]
end = bcolors.ENDC
print_str(5, 8 + self.thread_id,
"\t%s[%s%s] %3d%%%s" % (col, fill, space, perc, end)
)
else:
print(
"\r\t[%s%s] %3d%%" % (fill, space, perc),
flush = True,
end = ""
)
# 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):
if self.games > 100:
if game % (self.games // 100) == 0 and not DEBUG:
if self.thread_id == 0 or ANSI:
progress = (game+1) / self.games
self.print_progress(progress)
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)
if not DEBUG and (ANSI or self.thread_id == 0):
self.print_progress(1)
self.collect_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):
t0 = time.clock()
self.single_bot(index, bot, game_scores, last_round)
t1 = time.clock()
self.bot_timings[bot.__class__.__name__] += t1-t0
if game_scores[index] >= self.end_score and not last_round:
last_round = True
last_round_initiator = index
round_number += 1
# maximum of 200 rounds per game
if round_number > self.max_rounds - 1:
last_round = True
self.timed_out_games += 1
# 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]):
t0 = time.clock()
self.single_bot(index, bot, game_scores, last_round)
t1 = time.clock()
self.bot_timings[bot.__class__.__name__] += t1-t0
# calculate which bots have the highest score
max_score = max(game_scores)
nr_of_winners = 0
for i in range(self.bots_per_game):
bot_name = game_bots[i].__class__.__name__
# average score per bot
self.highscore[bot_name][1] += game_scores[i]
if self.highscore[bot_name][0] < game_scores[i]:
# maximum score per bot
self.highscore[bot_name][0] = game_scores[i]
if game_scores[i] == max_score:
# average winning score per bot
self.highscore[bot_name][2] += game_scores[i]
nr_of_winners += 1
self.wins[bot_name] += 1
if nr_of_winners > 1:
self.tied_games += 1
self.total_rounds += round_number
self.highest_round = max(self.highest_round, round_number)
self.winning_scores[max_score] += 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
"""
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):
# send the last die cast to the bot
if not throw:
break
current_throws.append(self.throw_die())
if current_throws[-1] == 6:
break
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)
if DEBUG:
desc = "%d: Bot %24s plays %40s with " + \
"scores %30s and last round == %5s"
print(desc % (index, bot.__class__.__name__,
current_throws, game_scores, last_round))
bot_name = bot.__class__.__name__
# average throws per round
self.highscore[bot_name][3] += len(current_throws)
# average success rate per round
self.highscore[bot_name][4] += int(current_throws[-1] != 6)
# total number of rounds
self.highscore[bot_name][5] += 1
# Collects all stats for the thread, so they can be summed up later
def collect_results(self):
self.bot_stats = {
bot.__name__: [
self.wins[bot.__name__],
self.played_games[bot.__name__],
self.highscore[bot.__name__]
]
for bot in self.bots}
#
def print_results(total_bot_stats, total_game_stats, elapsed_time):
"""Print the high score after the simulation
Keyword arguments:
total_bot_stats -- A list containing the winning stats for each thread
total_game_stats -- A list containing controller stats for each thread
elapsed_time -- The number of seconds that it took to run the simulation
"""
# Find the name of each bot, the number of wins, the number
# of played games, and the win percentage
wins = defaultdict(int)
played_games = defaultdict(int)
highscores = defaultdict(lambda: [0, 0, 0, 0, 0, 0])
bots = set()
timed_out_games = sum(s[0] for s in total_game_stats)
tied_games = sum(s[1] for s in total_game_stats)
total_games = sum(s[2] for s in total_game_stats)
total_rounds = sum(s[4] for s in total_game_stats)
highest_round = max(s[5] for s in total_game_stats)
average_rounds = total_rounds / total_games
winning_scores = defaultdict(int)
bot_timings = defaultdict(float)
for stats in total_game_stats:
for score, count in stats[6].items():
winning_scores[score] += count
percentiles = calculate_percentiles(winning_scores, total_games)
for thread in total_bot_stats:
for bot, stats in thread.items():
wins[bot] += stats[0]
played_games[bot] += stats[1]
highscores[bot][0] = max(highscores[bot][0], stats[2][0])
for i in range(1, 6):
highscores[bot][i] += stats[2][i]
bots.add(bot)
for bot in bots:
bot_timings[bot] += sum(s[3][bot] for s in total_game_stats)
bot_stats = [[bot, wins[bot], played_games[bot], 0] for bot in bots]
for i, bot in enumerate(bot_stats):
bot[3] = 100 * bot[1] / bot[2] if bot[2] > 0 else 0
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
if ANSI:
print_str(0, 9 + threads, "")
else:
print("\n")
sim_msg = "\tSimulation or %d games between %d bots " + \
"completed in %.1f seconds"
print(sim_msg % (total_games, len(bots), elapsed_time))
print("\tEach game lasted for an average of %.2f rounds" % average_rounds)
print("\t%d games were tied between two or more bots" % tied_games)
print("\t%d games ran until the round limit, highest round was %d\n"
% (timed_out_games, highest_round))
print_bot_stats(sorted_scores, max_len, highscores)
print_score_percentiles(percentiles)
print_time_stats(bot_timings, max_len)
def calculate_percentiles(winning_scores, total_games):
percentile_bins = 10000
percentiles = [0 for _ in range(percentile_bins)]
sorted_keys = list(sorted(winning_scores.keys()))
sorted_values = [winning_scores[key] for key in sorted_keys]
cumsum_values = list(cumsum(sorted_values))
i = 0
for perc in range(percentile_bins):
while cumsum_values[i] < total_games * (perc+1) / percentile_bins:
i += 1
percentiles[perc] = sorted_keys[i]
return percentiles
def print_score_percentiles(percentiles):
n = len(percentiles)
show = [.5, .75, .9, .95, .99, .999, .9999]
print("\t+----------+-----+")
print("\t|Percentile|Score|")
print("\t+----------+-----+")
for p in show:
print("\t|%10.2f|%5d|" % (100*p, percentiles[int(p*n)]))
print("\t+----------+-----+")
print()
def print_bot_stats(sorted_scores, max_len, highscores):
"""Print the stats for the bots
Keyword arguments:
sorted_scores -- A list containing the bots in sorted order
max_len -- The maximum name length for all bots
highscores -- A dict with additional stats for each bot
"""
delimiter_format = "\t+%s%s+%s+%s+%s+%s+%s+%s+%s+%s+"
delimiter_args = ("-"*(max_len), "", "-"*4, "-"*8,
"-"*8, "-"*6, "-"*6, "-"*7, "-"*6, "-"*8)
delimiter_str = delimiter_format % delimiter_args
print(delimiter_str)
print("\t|%s%s|%4s|%8s|%8s|%6s|%6s|%7s|%6s|%8s|"
% ("Bot", " "*(max_len-3), "Win%", "Wins",
"Played", "Max", "Avg", "Avg win", "Throws", "Success%"))
print(delimiter_str)
for bot, wins, played, score in sorted_scores:
highscore = highscores[bot]
bot_max_score = highscore[0]
bot_avg_score = highscore[1] / played
bot_avg_win_score = highscore[2] / max(1, wins)
bot_avg_throws = highscore[3] / highscore[5]
bot_success_rate = 100 * highscore[4] / highscore[5]
space_fill = " "*(max_len-len(bot))
format_str = "\t|%s%s|%4.1f|%8d|%8d|%6d|%6.2f|%7.2f|%6.2f|%8.2f|"
format_arguments = (bot, space_fill, score, wins,
played, bot_max_score, bot_avg_score,
bot_avg_win_score, bot_avg_throws, bot_success_rate)
print(format_str % format_arguments)
print(delimiter_str)
print()
def print_time_stats(bot_timings, max_len):
"""Print the execution time for all bots
Keyword arguments:
bot_timings -- A dict containing information about timings for each bot
max_len -- The maximum name length for all bots
"""
total_time = sum(bot_timings.values())
sorted_times = sorted(bot_timings.items(),
key=lambda x: x[1], reverse = True)
delimiter_format = "\t+%s+%s+%s+"
delimiter_args = ("-"*(max_len), "-"*7, "-"*5)
delimiter_str = delimiter_format % delimiter_args
print(delimiter_str)
print("\t|%s%s|%7s|%5s|" % ("Bot", " "*(max_len-3), "Time", "Time%"))
print(delimiter_str)
for bot, bot_time in sorted_times:
space_fill = " "*(max_len-len(bot))
perc = 100 * bot_time / total_time
print("\t|%s%s|%7.2f|%5.1f|" % (bot, space_fill, bot_time, perc))
print(delimiter_str)
print()
def run_simulation(thread_id, bots_per_game, games_per_thread, bots):
"""Used by multithreading to run the simulation in parallel
Keyword arguments:
thread_id -- A unique identifier for each thread, starting at 0
bots_per_game -- How many bots should participate in each game
games_per_thread -- The number of games to be simulated
bots -- A list of all bot classes available
"""
try:
controller = Controller(bots_per_game,
games_per_thread, bots, thread_id)
controller.simulate_games()
controller_stats = (
controller.timed_out_games,
controller.tied_games,
controller.games,
controller.bot_timings,
controller.total_rounds,
controller.highest_round,
controller.winning_scores
)
return (controller.bot_stats, controller_stats)
except KeyboardInterrupt:
return {}
# 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(" -t\t\tthe number of threads")
print(" -d\t--download\tdownload all bots from codegolf.SE")
print(" -A\t--ansi\trun in ANSI mode, with prettier printing")
print(" -D\t--debug\trun in debug mode. Sets to 1 thread, 1 game")
print(" -h\t--help\tshow this help\n")
# Make a stack-API request for the n-th page
def req(n):
req = requests.get(URL % n)
req.raise_for_status()
return req.json()
# Pull all the answers via the stack-API
def get_answers():
n = 1
api_ans = req(n)
answers = api_ans['items']
while api_ans['has_more']:
n += 1
if api_ans['quota_remaining']:
api_ans = req(n)
answers += api_ans['items']
else:
break
m, r = api_ans['quota_max'], api_ans['quota_remaining']
if 0.1 * m > r:
print(" > [WARN]: only %s/%s API-requests remaining!" % (r,m), file=stderr)
return answers
def download_players():
players = {}
for ans in get_answers():
name = unescape(ans['owner']['display_name'])
bots = []
root = html.fromstring('<body>%s</body>' % ans['body'])
for el in root.findall('.//code'):
code = el.text
if re.search(r'^class \w+\(\w*Bot\):.*$', code, flags=re.MULTILINE):
bots.append(code)
if not bots:
print(" > [WARN] user '%s': couldn't locate any bots" % name, file=stderr)
elif name in players:
players[name] += bots
else:
players[name] = bots
return players
# Download all bots from codegolf.stackexchange.com
def download_bots():
print('pulling bots from the interwebs..', file=stderr)
try:
players = download_players()
except Exception as ex:
print('FAILED: (%s)' % ex, file=stderr)
exit(1)
if path.isfile(AUTO_FILE):
print(' > move: %s -> %s.old' % (AUTO_FILE,AUTO_FILE), file=stderr)
if path.exists('%s.old' % AUTO_FILE):
remove('%s.old' % AUTO_FILE)
rename(AUTO_FILE, '%s.old' % AUTO_FILE)
print(' > writing players to %s' % AUTO_FILE, file=stderr)
f = open(AUTO_FILE, 'w+', encoding='utf8')
f.write('# -*- coding: utf-8 -*- \n')
f.write('# Bots downloaded from https://codegolf.stackexchange.com/questions/177765 @ %s\n\n' % strftime('%F %H:%M:%S'))
with open(OWN_FILE, 'r') as bfile:
f.write(bfile.read()+'\n\n\n# Auto-pulled bots:\n\n')
for usr in players:
if usr not in IGNORE:
for bot in players[usr]:
f.write('# User: %s\n' % usr)
f.write(bot+'\n\n')
f.close()
print('OK: pulled %s bots' % sum(len(bs) for bs in players.values()))
if __name__ == "__main__":
games = 10000
bots_per_game = 8
threads = 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 == "-t" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit():
threads = int(sys.argv[i+1])
if arg == "-d" or arg == "--download":
DOWNLOAD = True
if arg == "-A" or arg == "--ansi":
ANSI = True
if arg == "-D" or arg == "--debug":
DEBUG = True
if arg == "-h" or arg == "--help":
print_help()
quit()
if ANSI:
print(chr(27) + "[2J", flush = True)
print_str(1,3,"")
else:
print()
if DOWNLOAD:
download_bots()
exit() # Before running other's code, you might want to inspect it..
if path.isfile(AUTO_FILE):
exec('from %s import *' % AUTO_FILE[:-3])
else:
exec('from %s import *' % OWN_FILE[:-3])
bots = get_all_bots()
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
if threads <= 0:
print("\tAt least 1 thread is needed")
threads = 1
if DEBUG:
print("\tRunning in debug mode, with 1 thread and 1 game")
threads = 1
games = 1
games_per_thread = math.ceil(games / threads)
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))
print("\tRunning simulation on %d threads" % threads)
if len(sys.argv) == 1:
print("\tFor help running the script, use the -h flag")
print()
with Pool(threads) as pool:
t0 = time.time()
results = pool.starmap(
run_simulation,
[(i, bots_per_game, games_per_thread, bots) for i in range(threads)]
)
t1 = time.time()
if not DEBUG:
total_bot_stats = [r[0] for r in results]
total_game_stats = [r[1] for r in results]
print_results(total_bot_stats, total_game_stats, t1-t0)
Si vous souhaitez accéder au contrôleur d'origine pour ce défi, il est disponible dans l'historique des modifications. Le nouveau contrôleur a exactement la même logique pour exécuter le jeu, la seule différence est la performance, la collecte de statistiques et une impression plus jolie.
Bots
Sur ma machine, les robots sont conservés dans le fichier forty_game_bots.py
. Si vous utilisez un autre nom pour le fichier, vous devez mettre à jour l' import
instruction en haut du contrôleur.
import sys, inspect
import random
import numpy as np
# Returns a list of all bot classes which inherit from the Bot class
def get_all_bots():
return Bot.__subclasses__()
# The parent class for all bots
class Bot:
def __init__(self, index, end_score):
self.index = index
self.end_score = end_score
def update_state(self, current_throws):
self.current_throws = current_throws
def make_throw(self, scores, last_round):
yield False
class ThrowTwiceBot(Bot):
def make_throw(self, scores, last_round):
yield True
yield False
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Lancer la simulation
Pour exécuter une simulation, enregistrez les deux extraits de code publiés ci-dessus dans deux fichiers distincts. Je les ai sauvegardés au fur forty_game_controller.py
et à mesure forty_game_bots.py
. Ensuite, vous utilisez simplement python forty_game_controller.py
ou en python3 forty_game_controller.py
fonction de votre configuration Python. Suivez les instructions à partir de là si vous souhaitez configurer davantage votre simulation ou essayez de modifier le code si vous le souhaitez.
Statistiques du jeu
Si vous créez un bot qui vise un certain score sans prendre en compte les autres robots, voici les centiles de score gagnants:
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 44|
| 75.00| 48|
| 90.00| 51|
| 95.00| 54|
| 99.00| 58|
| 99.90| 67|
| 99.99| 126|
+----------+-----+
Scores élevés
Comme plus de réponses sont postées, je vais essayer de garder cette liste à jour. Le contenu de la liste sera toujours de la dernière simulation. Les bots ThrowTwiceBot
et GoToTenBot
sont les bots du code ci-dessus, et sont utilisés comme référence. J'ai fait une simulation avec 10 ^ 8 jeux, ce qui a pris environ 1 heure. Ensuite, j'ai constaté que le jeu avait atteint la stabilité par rapport à mes manches de 10 ^ 7 parties. Cependant, comme les gens continuent à envoyer des bots, je ne ferai plus de simulations tant que la fréquence des réponses n’aura pas diminué.
J'essaie d'ajouter de nouveaux robots et d'ajouter les modifications que vous avez apportées aux robots existants. S'il semble que j'ai raté votre bot ou vos modifications, écrivez dans le chat et je m'assurerai d'avoir votre toute dernière version dans la prochaine simulation.
Nous avons maintenant plus de statistiques pour chaque bot grâce à AKroell ! Les trois nouvelles colonnes contiennent le score maximum pour tous les jeux, le score moyen par match et le score moyen lors de la victoire pour chaque bot.
Comme indiqué dans les commentaires, il y avait un problème avec la logique de jeu qui faisait que les bots ayant un indice plus élevé au sein d'un jeu obtenaient une partie supplémentaire dans certains cas. Ceci a été corrigé maintenant, et les scores ci-dessous reflètent cela.
Simulation or 300000000 games between 49 bots completed in 35628.7 seconds
Each game lasted for an average of 3.73 rounds
29127662 games were tied between two or more bots
0 games ran until the round limit, highest round was 22
+-----------------------+----+--------+--------+------+------+-------+------+--------+
|Bot |Win%| Wins| Played| Max| Avg|Avg win|Throws|Success%|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
|OptFor2X |21.6|10583693|48967616| 99| 20.49| 44.37| 4.02| 33.09|
|Rebel |20.7|10151261|48977862| 104| 21.36| 44.25| 3.90| 35.05|
|Hesitate |20.3| 9940220|48970815| 105| 21.42| 44.23| 3.89| 35.11|
|EnsureLead |20.3| 9929074|48992362| 101| 20.43| 44.16| 4.50| 25.05|
|StepBot |20.2| 9901186|48978938| 96| 20.42| 43.47| 4.56| 24.06|
|BinaryBot |20.1| 9840684|48981088| 115| 21.01| 44.48| 3.85| 35.92|
|Roll6Timesv2 |20.1| 9831713|48982301| 101| 20.83| 43.53| 4.37| 27.15|
|AggressiveStalker |19.9| 9767637|48979790| 110| 20.46| 44.86| 3.90| 35.04|
|FooBot |19.9| 9740900|48980477| 100| 22.03| 43.79| 3.91| 34.79|
|QuotaBot |19.9| 9726944|48980023| 101| 19.96| 44.95| 4.50| 25.03|
|BePrepared |19.8| 9715461|48978569| 112| 18.68| 47.58| 4.30| 28.31|
|AdaptiveRoller |19.7| 9659023|48982819| 107| 20.70| 43.27| 4.51| 24.81|
|GoTo20Bot |19.6| 9597515|48973425| 108| 21.15| 43.24| 4.44| 25.98|
|Gladiolen |19.5| 9550368|48970506| 107| 20.16| 45.31| 3.91| 34.81|
|LastRound |19.4| 9509645|48988860| 100| 20.45| 43.50| 4.20| 29.98|
|BrainBot |19.4| 9500957|48985984| 105| 19.26| 45.56| 4.46| 25.71|
|GoTo20orBestBot |19.4| 9487725|48975944| 104| 20.98| 44.09| 4.46| 25.73|
|Stalker |19.4| 9485631|48969437| 103| 20.20| 45.34| 3.80| 36.62|
|ClunkyChicken |19.1| 9354294|48972986| 112| 21.14| 45.44| 3.57| 40.48|
|FortyTeen |18.8| 9185135|48980498| 107| 20.90| 46.77| 3.88| 35.32|
|Crush |18.6| 9115418|48985778| 96| 14.82| 43.08| 5.15| 14.15|
|Chaser |18.6| 9109636|48986188| 107| 19.52| 45.62| 4.06| 32.39|
|MatchLeaderBot |16.6| 8122985|48979024| 104| 18.61| 45.00| 3.20| 46.70|
|Ro |16.5| 8063156|48972140| 108| 13.74| 48.24| 5.07| 15.44|
|TakeFive |16.1| 7906552|48994992| 100| 19.38| 44.68| 3.36| 43.96|
|RollForLuckBot |16.1| 7901601|48983545| 109| 17.30| 50.54| 4.72| 21.30|
|Alpha |15.5| 7584770|48985795| 104| 17.45| 46.64| 4.04| 32.67|
|GoHomeBot |15.1| 7418649|48974928| 44| 13.23| 41.41| 5.49| 8.52|
|LeadBy5Bot |15.0| 7354458|48987017| 110| 17.15| 46.95| 4.13| 31.16|
|NotTooFarBehindBot |15.0| 7338828|48965720| 115| 17.75| 45.03| 2.99| 50.23|
|GoToSeventeenRollTenBot|14.1| 6900832|48976440| 104| 10.26| 49.25| 5.68| 5.42|
|LizduadacBot |14.0| 6833125|48978161| 96| 9.67| 51.35| 5.72| 4.68|
|TleilaxuBot |13.5| 6603853|48985292| 137| 15.25| 45.05| 4.27| 28.80|
|BringMyOwn_dice |12.0| 5870328|48974969| 44| 21.27| 41.47| 4.24| 29.30|
|SafetyNet |11.4| 5600688|48987015| 98| 15.81| 45.03| 2.41| 59.84|
|WhereFourArtThouChicken|10.5| 5157324|48976428| 64| 22.38| 47.39| 3.59| 40.19|
|ExpectationsBot | 9.0| 4416154|48976485| 44| 24.40| 41.55| 3.58| 40.41|
|OneStepAheadBot | 8.4| 4132031|48975605| 50| 18.24| 46.02| 3.20| 46.59|
|GoBigEarly | 6.6| 3218181|48991348| 49| 20.77| 42.95| 3.90| 35.05|
|OneInFiveBot | 5.8| 2826326|48974364| 155| 17.26| 49.72| 3.00| 50.00|
|ThrowThriceBot | 4.1| 1994569|48984367| 54| 21.70| 44.55| 2.53| 57.88|
|FutureBot | 4.0| 1978660|48985814| 50| 17.93| 45.17| 2.36| 60.70|
|GamblersFallacy | 1.3| 621945|48986528| 44| 22.52| 41.46| 2.82| 53.07|
|FlipCoinRollDice | 0.7| 345385|48972339| 87| 15.29| 44.55| 1.61| 73.17|
|BlessRNG | 0.2| 73506|48974185| 49| 14.54| 42.72| 1.42| 76.39|
|StopBot | 0.0| 1353|48984828| 44| 10.92| 41.57| 1.00| 83.33|
|CooperativeSwarmBot | 0.0| 991|48970284| 44| 10.13| 41.51| 1.36| 77.30|
|PointsAreForNerdsBot | 0.0| 0|48986508| 0| 0.00| 0.00| 6.00| 0.00|
|SlowStart | 0.0| 0|48973613| 35| 5.22| 0.00| 3.16| 47.39|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
Les bots suivants (sauf Rebel
) sont faits pour se plier aux règles, et les créateurs ont accepté de ne pas participer au tournoi officiel. Cependant, je pense toujours que leurs idées sont créatives et qu’elles méritent une mention honorable. Rebel figure également sur cette liste car il utilise une stratégie astucieuse pour éviter le sabotage et fonctionne mieux avec le bot saboteur en jeu.
Les bots NeoBot
et KwisatzHaderach
suivent les règles, mais utilisent une échappatoire en prédisant le générateur aléatoire. Comme ces robots nécessitent beaucoup de ressources pour simuler, j'ai ajouté ses statistiques à partir d'une simulation avec moins de jeux. Le bot HarkonnenBot
réalise la victoire en désactivant tous les autres robots, ce qui est strictement contraire aux règles.
Simulation or 300000 games between 52 bots completed in 66.2 seconds
Each game lasted for an average of 4.82 rounds
20709 games were tied between two or more bots
0 games ran until the round limit, highest round was 31
+-----------------------+----+--------+--------+------+------+-------+------+--------+
|Bot |Win%| Wins| Played| Max| Avg|Avg win|Throws|Success%|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
|KwisatzHaderach |80.4| 36986| 46015| 214| 58.19| 64.89| 11.90| 42.09|
|HarkonnenBot |76.0| 35152| 46264| 44| 34.04| 41.34| 1.00| 83.20|
|NeoBot |39.0| 17980| 46143| 214| 37.82| 59.55| 5.44| 50.21|
|Rebel |26.8| 12410| 46306| 92| 20.82| 43.39| 3.80| 35.84|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 45|
| 75.00| 50|
| 90.00| 59|
| 95.00| 70|
| 99.00| 97|
| 99.90| 138|
| 99.99| 214|
+----------+-----+
Réponses:
OptFor2X
Ce bot suit une approximation de la stratégie optimale pour la version à deux joueurs de ce jeu, en utilisant uniquement son score et le score du meilleur adversaire. Au dernier tour, la version mise à jour prend en compte tous les scores.
la source
NeoBot
Au lieu de cela, essayez seulement de réaliser la vérité - il n'y a pas de cuillère
NeoBot jette un coup d'œil à la matrice (c'est-à-dire aléatoire) et prédit si le prochain résultat sera un 6 ou non.
NeoBot ne modifie pas réellement le contrôleur ou le moteur d'exécution, mais demande poliment à la bibliothèque de plus amples informations.
la source
Essaim coopératif
Stratégie
Je pense que personne d'autre n'a encore remarqué la portée de cette règle:
Si chaque bot réussissait toujours jusqu'à ce qu'il soit éliminé, tout le monde aurait un score de zéro à la fin de la ronde 200 et tout le monde gagnerait! Ainsi, la stratégie de Cooperative Swarm est de coopérer tant que tous les joueurs ont un score de zéro, mais de jouer normalement si quelqu'un marque des points.
Dans ce post, je soumets deux robots: le premier est CooperativeSwarmBot et le second est CooperativeThrowTwice. CooperativeSwarmBot sert de classe de base pour tous les bots faisant officiellement partie de l'essaim coopératif, et a pour comportement réservé de simplement accepter son premier jet réussi lorsque la coopération échoue. CooperativeSwarmBot a pour coopérative CooperativeSwarmBot et lui est identique à tous les égards sauf que son comportement non coopératif consiste à faire deux lancers au lieu d'un. Dans les prochains jours, je réviserai ce message afin d'ajouter de nouveaux robots qui utilisent un comportement beaucoup plus intelligent en jouant contre des robots non coopératifs.
Code
Une analyse
Viabilité
Il est très difficile de coopérer dans ce jeu car nous avons besoin du soutien des huit joueurs pour que cela fonctionne. Étant donné que chaque classe de bot est limitée à une instance par match, il s'agit d'un objectif difficile à atteindre. Par exemple, les chances de choisir huit robots coopératifs parmi un pool de 100 robots coopératifs et de 30 robots non coopératifs sont:
Étude de cas
Pour un certain nombre de raisons (voir les notes 1 et 2), un véritable essaim coopératif ne participera jamais aux jeux officiels. En tant que tel, je vais résumer les résultats d'une de mes propres simulations dans cette section.
Cette simulation a fonctionné 10000 jeux en utilisant les 38 autres bots qui avaient été postés ici la dernière fois que j'ai vérifié et 2900 bots qui avaient CooperativeSwarmBot comme classe parent. Le contrôleur a signalé que 9051 des 10 000 parties (90,51%) se sont terminées à 200 tours, ce qui est assez proche de la prédiction selon laquelle 90% des parties seraient coopératives. La mise en œuvre de ces robots était triviale; autres que CooperativeSwarmBot ils ont tous pris cette forme:
Moins de 3% des bots avaient un pourcentage de victoire inférieur à 80% et un peu plus de 11% des bots gagnaient à chaque match. Le pourcentage médian de victoire sur les 2 900 robots de l'essaim est d'environ 86%, ce qui est scandaleusement bon. À titre de comparaison, les meilleurs joueurs du classement officiel actuel gagnent moins de 22% de leurs jeux. Je ne peux pas adapter la liste complète de l'essaim coopératif à la longueur maximale autorisée pour une réponse. Si vous voulez voir que vous devrez aller ici à la place: https://pastebin.com/3Zc8m1Ex
Étant donné que chaque bot a participé à environ 27 parties en moyenne, la chance joue un rôle relativement important lorsque vous examinez les résultats obtenus pour des bots individuels. Comme je n'ai pas encore mis en place de stratégie avancée pour les jeux non coopératifs, la plupart des autres robots ont considérablement profité des avantages de leur confrontation avec l'essaim coopératif, atteignant même le taux de victoire médian de cet essaim coopératif de 86%.
Les résultats complets pour les bots qui ne font pas partie de l'essaim sont énumérés ci-dessous; Je pense que les résultats de deux robots méritent une attention particulière. Premièrement, StopBot n’a remporté aucune partie. Ceci est particulièrement tragique parce que l'essaim coopératif utilisait en fait exactement la même stratégie que StopBot; vous vous attendriez à ce que StopBot gagne huit de ses jeux par hasard, et un peu plus, car l'essaim de la coopérative est obligé de donner le premier coup à ses adversaires. Le deuxième résultat intéressant, cependant, est que le travail acharné de PointsAreForNerdsBot a finalement porté ses fruits: il a coopéré avec l'essaim et a réussi à gagner chaque match joué!
Défauts
Cette approche coopérative présente quelques inconvénients. Premièrement, lorsqu’ils jouent contre des bots non coopératifs, les bots coopératifs n’obtiennent jamais l’avantage du premier tour, car lorsqu’ils jouent les premiers, ils ne savent pas encore si leurs adversaires sont disposés à coopérer et n’ont donc pas le choix. score de zéro. De même, cette stratégie de coopération est extrêmement vulnérable à l’exploitation par des robots malveillants; par exemple, lors d'une partie coopérative, le bot qui joue en dernier dans la dernière manche peut choisir d'arrêter immédiatement de rouler pour faire perdre tout le monde (en supposant, bien sûr, que son premier lancer n'était pas un six).
En coopérant, tous les robots peuvent atteindre la solution optimale d'un taux de victoire de 100%. En tant que tel, si le taux de gain était la seule chose qui importait, la coopération constituerait un équilibre stable et il n'y aurait plus rien à craindre. Cependant, certains robots peuvent donner la priorité à d’autres objectifs, comme atteindre le sommet du classement. Cela signifie qu'un autre bot risque de faire défaut après votre dernier tour, ce qui vous incite à commencer par faire défaut. Parce que la configuration de cette compétition ne nous permet pas de voir ce que nos adversaires ont fait lors de leurs matchs précédents, nous ne pouvons pas pénaliser les individus qui ont fait défection. Ainsi, la coopération est finalement un équilibre instable voué à l'échec.
Notes de bas de page
[1]: Les principales raisons pour lesquelles je ne souhaite pas envoyer des milliers de robots au lieu de deux sont que cela ralentirait la simulation d'un facteur de l'ordre de 1000 [2], et que cela gâcherait considérablement gagner des pourcentages car les autres robots joueraient presque exclusivement contre l’essaim plutôt que les autres. Le plus important, toutefois, est le fait que même si je le voulais, je ne pourrais pas créer autant de robots dans un délai raisonnable sans enfreindre l’esprit de la règle selon laquelle "un bot ne doit pas mettre en œuvre la même stratégie existant, intentionnellement ou accidentellement ".
[2]: Je pense qu'il y a deux raisons principales pour lesquelles la simulation ralentit lors de l'exécution d'un essaim coopératif. Premièrement, plus de robots signifie plus de jeux si vous voulez que chaque bot joue dans le même nombre de jeux (dans l'étude de cas, le nombre de jeux différerait d'un facteur 77 environ). Deuxièmement, les jeux coopératifs prennent plus de temps, car ils durent 200 tours et, dans un tour, les joueurs doivent continuer à rouler indéfiniment. Pour ma configuration, la simulation des jeux prenait environ 40 fois plus de temps: l’étude de cas prenait un peu plus de trois minutes pour exécuter 10 000 parties, mais après la suppression de l’essaim coopératif, elle terminerait 10 000 parties en seulement 4,5 secondes. Entre ces deux raisons, j’estime qu’il faudrait environ 3100 fois plus de temps pour mesurer avec précision les performances des bots lorsqu’un essaim est en compétition par rapport à ce qu’il n’y en a pas.
la source
GoTo20Bot
Juste essayer avec tous
GoToNBot
, et 20, 22, 24 joue mieux. Je ne sais pas pourquoi.Mise à jour: arrêtez toujours le lancer si vous marquez 40 ou plus.
la source
end_score
sur 4000 (et que votre bot a été utilisé pour letarget
calcul), les 15-16 bots étaient bien meilleurs. Mais si le jeu consistait uniquement à augmenter votre score, ce serait trivial.end_score
est 4000, il est presque impossible d'obtenir 4000 avant 200 tours. Et le jeu consiste simplement à savoir qui a obtenu le meilleur score en 200 tours. Et arrêter à 15 heures devrait fonctionner dans la mesure où, cette fois, la stratégie du score le plus élevé en un tour est la même que celle du score le plus élevé en 200 tours.Rouleau Adaptatif
Début plus agressif et calme vers la fin du tour.
S'il croit gagner, roulez un temps supplémentaire pour la sécurité.
la source
lim = max(min(self.end_score - scores[self.index], 24), 6)
en augmentant le maximum à 24 et en ajoutant un minimum de 6, le pourcentage de gagnants augmente et le reste est combiné.Alpha
Alpha refuse de ne jamais être inférieur à quiconque. Tant qu'il y a un bot avec un score plus élevé, il continuera à rouler.
la source
yield
marche, si ça commence à rouler, ça n'arrêtera jamais. Vous aurez envie de mettre à jourmy_score
dans la boucle.NotTooFarBehindBot
L'idée est que les autres bots risquent de perdre des points, donc être 2e n'est pas mauvais, mais si vous êtes très en retard, vous pourriez aussi bien faire une faillite.
la source
6: Bot NotTooFarBehindBot plays [4, 2, 4, 2, 3, 3, 5, 5, 1, 4, 1, 4, 2, 4, 3, 6] with scores [0, 9, 0, 20, 0, 0, 0] and last round == False
. Même si votre bot est en tête après 7 lancers, il continue jusqu'à atteindre un 6. Pendant que je tape ceci, j'ai compris le problème! Lesscores
seuls contiennent les scores totaux, pas les cas de dés pour le tour en cours. Vous devriez le modifier pour êtrecurrent_score = scores[self.index] + sum(self.current_throws)
.GoHomeBot
Nous voulons aller gros ou rentrer à la maison, non? GoHomeBot rentre chez lui pour la plupart. (Mais étonnamment bien!)
la source
scores
liste. Il y avait un bot comme celui-ci auparavant (le bot GoToEnd), mais David a supprimé leur réponse. Je vais remplacer ce bot par le vôtre.EnsureLead
EnsureLead emprunte des idées à GoTo20Bot. Il ajoute le concept selon lequel il considère toujours (en dernier ou à 40 ans) qu'il y en a d'autres qui auront au moins un jet de plus. Ainsi, le bot tente de les devancer, de sorte qu'ils doivent se rattraper.
la source
Roll6TimesV2
Ne bat pas le meilleur actuel, mais je pense que ça ira mieux avec plus de robots en jeu.
Jeu vraiment génial au fait.
la source
StopBot
Littéralement, un seul lancer.
Ceci est équivalent à la
Bot
classe de base .la source
BringMyOwn_dice (BMO_d)
Ce bot aime les dés, il apporte 2 dés (qui semblent donner les meilleurs résultats). Avant de lancer les dés d'un tour, il lance ses propres 2 dés et calcule leur somme, c'est le nombre de lancers qu'il va effectuer, il ne jette que s'il n'a pas déjà 40 points.
la source
FooBot
la source
# Must throw at least once
est inutile - il lance une fois avant d'appeler votre bot. Votre bot lancera toujours un minimum de deux fois.make_throw
méthode très tôt, quand je voulais que les joueurs puissent passer leur tour. Je suppose qu'un nom plus approprié seraitkeep_throwing
. Merci pour les commentaires dans le bac à sable, cela a vraiment contribué à faire de ce défi un vrai défi!Go Big Early
Concept: essayer de gagner gros dès le début (arriver à 25) puis remonter de 2 rouleaux à la fois.
la source
BinaryBot
Essaie de se rapprocher du score final afin que, dès que quelqu'un d'autre déclenche le dernier tour, il peut battre son score pour la victoire. La cible est toujours à mi-chemin entre le score actuel et le score final.
la source
Hesitate
refuse également de franchir la ligne en premier. Vous devez entourer votre fonction de cesclass
choses.PointsAreForNerdsBot
Celui-ci n'a besoin d'aucune explication.
OneInFiveBot
Continue à rouler jusqu'à ce qu'il lance un 5 sur son propre dé à 5 faces. Cinq, c'est moins que six, alors, IL FAUT GAGNER!
la source
OneInFiveBot
une bonne idée, mais je pense que cela finit par souffrir par rapport à certains des robots les plus avancés. Encore une belle soumission!OneInFiveBot
est tout à fait intéressant dans la façon dont il a toujours le meilleur score global atteint.StopBot
un sac de boxe: P. Le OneInFiveBot est en fait assez chouette, bon travail!OneInFiveBot
et ça va beaucoup mieux que ce àLizduadacBot
Essaie de gagner en 1 étape. La condition de fin est quelque peu arbitraire.
C'est aussi mon premier post (et je suis nouveau sur Python), donc si je battais "PointsAreForNerdsBot", je serais heureux!
la source
PointsAreForNerdsBot
, mais votre bot se débrouille plutôt bien. Je mettrai à jour le score plus tard ce soir ou demain, mais votre taux de victoire est d'environ 15%, ce qui est supérieur à la moyenne de 12,5%.SlowStart
Ce bot implémente l'algorithme TCP Slow Start. Il ajuste son nombre de jets ( nor ) en fonction de son tour précédent: s'il n'a pas obtenu un 6 au tour précédent, augmente le nor pour ce tour; alors qu'il ne réduit ni s'il l'a fait.
la source
def updateValues():
devrait êtredef updateValues(self):
(oudef update_values(self):
si vous voulez suivre PEP8). Deuxièmement, l'appelupdateValues()
devrait plutôt êtreself.updateValues()
(ouself.update_vales()
).i
variable dans la boucle while. En ce moment, votre bot passe la boucle while entièrement ou est bloqué dans la boucle tant jusqu'à ce qu'ilself.nor
et voir comment cela affecte les performances de votre bot.KwisatzHaderach
Au tout début de ce défi (c’est-à-dire qu’il
NeoBot
était posté avant ), j’écrivais unOracle
bot presque trivial :mais ne l'a pas publié car je ne pensais pas que c'était assez intéressant;) Mais une fois
NeoBot
que j'ai pris les devants, j'ai commencé à réfléchir à la manière de battre sa capacité parfaite de prédire l'avenir. Alors, voici une citation de Dune; c'est lorsque Paul Atréides, le Kwisatz Haderach, se trouve à un point de rencontre duquel peut se dérouler une infinité d'avenir différents:La réponse a donc été la suivante: prévoir l’avenir, c’est le changer. et si vous faites très attention, alors par action sélective ou par inaction, vous pouvez le changer de manière avantageuse - du moins la plupart du temps. Même les
KwisatzHaderach
ne peuvent pas obtenir un taux de victoire de 100%!la source
NeoBot
mais aussi meilleur! J'aime aussi la façon dont vous donnez un exemple de ce que tout ce qui utilise le caractère aléatoire (en particulier le contrôleur) devrait faire ici: utilisez votre proprerandom.Random
exemple. De mêmeNeoBot
, cela semble un peu sensible aux changements de détails d'implémentation non spécifiés du contrôleur.HarkonnenBot
ne touche pas le RNG; il se fiche des nombres aléatoires. Il empoisonne tous les autres robots, puis avance le plus lentement possible jusqu'à la ligne d'arrivée. Comme beaucoup de délices culinaires, la vengeance est un plat à savourer lentement, après une préparation longue et délicate.NeoBot
(etHarkonnenBot
), neKwisatzHaderach
repose que sur un détail de la mise en œuvre; en particulier, il n'a pas besoin de savoir comment random.random () est implémenté, mais seulement que le contrôleur l'utilise; DKwisatzHaderach
et deHarkonnenBot
la même manière queNeoBot
. Ils recevront leurs scores d'une simulation avec moins de jeux et ne seront pas dans la simulation officielle. Cependant, ils finiront par figurer sur la liste des meilleurs scoresNeoBot
. La raison principale pour laquelle ils ne participent pas à la simulation officielle est qu'ils vont gâcher d'autres stratégies de bot. Pourtant.WisdomOfCrowds
devrait être bien adapté à la participation, et je suis curieux de connaître les nouveaux changements que vous avez apportés à cela!Eh bien, celui-là est évident
la source
LastRound agit comme si c'était toujours le dernier tour et le dernier bot: il continue de rouler jusqu'à ce qu'il soit en tête. De plus, il ne veut pas se contenter de moins de 15 points à moins que ce soit le dernier tour ou qu'il atteigne 40 points.
la source
QuotaBot
Un système de "quota" naïf que j’ai mis en place et qui a semblé en réalité avoir un score assez élevé dans l’ensemble.
la source
if own_score mean + 5:
donne une erreur pour moi. Aussiwhile sum(self.current_throws)
<
et des>
symboles qui interfèrent avec les<pre>
balises que j'utilisaisAttentesBot
Il suffit de jouer droit, calcule la valeur attendue pour le lancer de dés et ne le fait que si c'est positif.
J'avais du mal à exécuter le contrôleur, j'ai un "NameError: le nom 'bots_per_game' n'est pas défini" sur le multithread, donc vraiment aucune idée de la façon dont cela fonctionne.
la source
BlessRNG
BlessRNG FrankerZ GabeN BlessRNG
la source
Quarante ans
Essayez de marquer 14 points jusqu’à la dernière ronde, puis supposez que tous les autres vont essayer d’obtenir 14 points et essayer d’atteindre ce score.
la source
TypeError: unsupported operand type(s) for -: 'list' and 'int'
avec votre bot.max_projected_score
devriez être le maximum de la liste plutôt que la liste entière, est-ce que j'ai raison? Sinon, j'obtiens le même problème que tsh.Hésiter
Fait deux pas modestes, puis attend que quelqu'un d'autre franchisse la ligne. La version mise à jour n'essaie plus de battre le record, elle veut seulement l'atteindre - améliorer les performances en supprimant deux octets du code source!
la source
Rebelle
Ce bot combine la stratégie simple de la stratégie
Hesitate
avancée du dernier tourBotFor2X
, tente de se rappeler de qui il s'agit et se déchaîne lorsqu'il découvre qu'il vit dans une illusion.la source
HarkonnenBot
pour queRebel
cela ne puisse plus s'empoisonner;) et j'ai aussi peaufinéTleilaxuBot
pour queRebel
cela ne le détecte plus!Cinquième prise
La moitié du temps, nous allons lancer un 5 avant un 6. Lorsque nous le faisons, encaisser.
la source
Chaser
Si le dernier tour est terminé, il tente désespérément d'atteindre au moins 50 points. Juste pour faire bonne mesure, il lance au moins quatre fois, peu importe le résultat.
[modifier 1: ajouté stratégie go-for-gold au dernier tour]
[edit 2: logique mise à jour parce que je pensais à tort qu'un bot marquerait à 40 plutôt que seulement le score de bot le plus élevé]
[edit 3: fait un peu plus défensif au jeu final]
la source
FutureBot
OneStepAheadBot
Une paire de robots, ils apportent leurs propres jeux de dés et les lancent pour prédire l'avenir. Si un joueur est un 6, il s'arrête, FutureBot ne peut pas se rappeler lequel de ses 2 dés était destiné au prochain lancer, il abandonne donc.
Je me demande qui fera mieux.
OneStepAhead est un peu trop similaire à OneInFive à mon goût, mais je veux également voir comment il se compare à FutureBot et OneInFive.
Edit: Maintenant, ils s'arrêtent après avoir frappé 45
la source