Torneo terminado!
¡El torneo ya terminó! La simulación final se realizó durante la noche, un total de juegos. El ganador es Christian Sievers con su bot OptFor2X . Christian Sievers también logró asegurar el segundo lugar con Rebel . ¡Felicidades! A continuación puedes ver la lista oficial de puntajes más altos para el torneo.
Si todavía quieres jugar, puedes usar el controlador publicado a continuación y usar el código para crear tu propio juego.
Me invitaron a jugar un juego de dados del que nunca había oído hablar. Las reglas eran simples, pero creo que sería perfecto para un desafío KotH.
Las normas
El comienzo del juego
El dado gira alrededor de la mesa, y cada vez que es tu turno, puedes lanzar el dado tantas veces como quieras. Sin embargo, debes lanzarlo al menos una vez. Lleva un registro de la suma de todos los lanzamientos de su ronda. Si elige detenerse, el puntaje de la ronda se agrega a su puntaje total.
Entonces, ¿por qué dejarías de tirar el dado? Porque si obtienes 6, tu puntaje para toda la ronda se convierte en cero, y el dado se pasa. Por lo tanto, el objetivo inicial es aumentar su puntaje lo más rápido posible.
¿Quién es el ganador?
Cuando el primer jugador alrededor de la mesa alcanza 40 puntos o más, comienza la última ronda. Una vez que la última ronda ha comenzado, todos menos la persona que inició la última ronda tienen un turno más.
Las reglas para la última ronda son las mismas que para cualquier otra ronda. Eliges seguir tirando o parar. Sin embargo, sabe que no tiene posibilidades de ganar si no obtiene una puntuación más alta que las que tenía antes en la última ronda. Pero si sigues yendo demasiado lejos, entonces podrías obtener un 6.
Sin embargo, hay una regla más a tener en cuenta. Si su puntaje total actual (su puntaje anterior + su puntaje actual para la ronda) es 40 o más, y alcanza un 6, su puntaje total se establece en 0. Eso significa que debe comenzar de nuevo. Si alcanzas un 6 cuando tu puntaje total actual es 40 o más, el juego continúa de manera normal, excepto que ahora estás en el último lugar. La última ronda no se activa cuando se restablece su puntaje total. Todavía puedes ganar la ronda, pero se vuelve más desafiante.
El ganador es el jugador con la puntuación más alta una vez que finaliza la última ronda. Si dos o más jugadores comparten el mismo puntaje, todos serán contados como vencedores.
Una regla adicional es que el juego continúa durante un máximo de 200 rondas. Esto es para evitar casos en los que varios bots básicamente continúan lanzando hasta que alcanzan 6 para mantenerse en su puntaje actual. Una vez que se pasa la 199a ronda, last_round
se establece en verdadero y se juega una ronda más. Si el juego llega a 200 rondas, el bot (o bots) con la puntuación más alta es el ganador, incluso si no tienen 40 puntos o más.
Resumen
- En cada ronda sigues tirando el dado hasta que eliges detenerte o obtienes un 6
- Debes lanzar el dado una vez (si tu primer lanzamiento es un 6, tu ronda termina inmediatamente)
- Si obtiene un 6, su puntaje actual se establece en 0 (no su puntaje total)
- Agrega su puntaje actual a su puntaje total después de cada ronda
- Cuando un bot finaliza su turno dando como resultado una puntuación total de al menos 40, todos los demás obtienen un último turno
- Si su puntaje total actual es y obtiene un 6, su puntaje total se establece en 0 y su ronda ha terminado
- La última ronda no se activa cuando ocurre lo anterior
- La persona con el puntaje total más alto después de la última ronda es el ganador.
- En caso de que haya múltiples ganadores, todos se contarán como ganadores.
- El juego dura un máximo de 200 rondas.
Aclaración de las puntuaciones.
- Puntuación total: la puntuación que has guardado de rondas anteriores
- Puntuación actual: la puntuación de la ronda actual
- Puntaje total actual: la suma de los dos puntajes anteriores
Como participas
Para participar en este desafío de KotH, debe escribir una clase de Python que herede de Bot
. Debe implementar la función: make_throw(self, scores, last_round)
. Esa función se llamará una vez que sea su turno, y su primer lanzamiento no fue un 6. Para seguir lanzando, debe hacerlo yield True
. Para dejar de tirar, deberías yield False
. Después de cada lanzamiento, update_state
se llama a la función padre . Por lo tanto, tiene acceso a sus lanzamientos para la ronda actual utilizando la variable self.current_throws
. También tiene acceso a su propio índice usando self.index
. Por lo tanto, para ver su propia puntuación total que usaría scores[self.index]
. También puedes acceder al end_score
juego usando self.end_score
, pero puedes asumir con seguridad que será 40 para este desafío.
Se le permite crear funciones auxiliares dentro de su clase. También puede anular las funciones existentes en la Bot
clase principal, por ejemplo, si desea agregar más propiedades de clase. No está permitido modificar el estado del juego de ninguna manera, excepto ceder True
o False
.
Puedes buscar inspiración en esta publicación y copiar cualquiera de los dos bots que he incluido aquí. Sin embargo, me temo que no son particularmente efectivos ...
Sobre permitir otros idiomas
Tanto en el sandbox como en The Nineteenth Byte, hemos tenido discusiones sobre cómo permitir envíos en otros idiomas. Después de leer sobre tales implementaciones y escuchar argumentos de ambos lados, he decidido restringir este desafío solo a Python. Esto se debe a dos factores: el tiempo requerido para admitir múltiples idiomas y la aleatoriedad de este desafío que requiere una gran cantidad de iteraciones para alcanzar la estabilidad. Espero que sigas participando, y si quieres aprender algo de Python para este desafío, intentaré estar disponible en el chat con la mayor frecuencia posible.
Para cualquier pregunta que pueda tener, puede escribir en la sala de chat para este desafío . ¡Te veo allí!
Reglas
- El sabotaje está permitido y alentado. Es decir, sabotaje contra otros jugadores.
- Cualquier intento de jugar con el controlador, el tiempo de ejecución u otras presentaciones será descalificado. Todos los envíos solo deberían funcionar con las entradas y el almacenamiento que se les proporciona.
- Cualquier bot que use más de 500 MB de memoria para tomar su decisión será descalificado (si necesita tanta memoria, debe repensar sus elecciones)
- Un bot no debe implementar exactamente la misma estrategia que una existente, intencional o accidentalmente.
- Se le permite actualizar su bot durante el tiempo del desafío. Sin embargo, también podría publicar otro bot si su enfoque es diferente.
Ejemplo
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Este bot continuará hasta que tenga un puntaje de al menos 10 para la ronda, o arroje un 6. Tenga en cuenta que no necesita ninguna lógica para manejar el lanzamiento 6. También tenga en cuenta que si su primer lanzamiento es un 6, make_throw
es nunca llamó, ya que su ronda ha terminado de inmediato.
Para aquellos que son nuevos en Python (y nuevos en el yield
concepto), pero quieren probar esto, la yield
palabra clave es similar a un retorno en algunos aspectos, pero diferente en otros. Puedes leer sobre el concepto aquí . Básicamente, una vez que usted yield
, su función se detendrá, y el valor que yield
editó será enviado de vuelta al controlador. Allí, el controlador maneja su lógica hasta que sea hora de que su bot tome otra decisión. Luego, el controlador le envía el lanzamiento de dados, y su make_throw
función continuará ejecutándose justo donde se detuvo antes, básicamente en la línea después de la yield
declaración anterior .
De esta manera, el controlador del juego puede actualizar el estado sin requerir una llamada a la función bot por separado para cada lanzamiento de dados.
Especificación
Puede usar cualquier biblioteca de Python disponible en pip
. Para garantizar que pueda obtener un buen promedio, tiene un límite de tiempo de 100 milisegundos por ronda. Estaría muy feliz si tu guión fuera mucho más rápido que eso, para poder correr más rondas.
Evaluación
Para encontrar el ganador, tomaré todos los bots y los ejecutaré en grupos aleatorios de 8. Si hay menos de 8 clases enviadas, los ejecutaré en grupos aleatorios de 4 para evitar tener siempre todos los bots en cada ronda. Ejecutaré simulaciones durante aproximadamente 8 horas, y el ganador será el bot con el mayor porcentaje de victorias. ¡Comenzaré las simulaciones finales a principios de 2019, y te daré toda la Navidad para codificar tus bots! La fecha final preliminar es el 4 de enero, pero si es muy poco tiempo, puedo cambiarla a una fecha posterior.
Hasta entonces, intentaré hacer una simulación diaria usando 30-60 minutos de tiempo de CPU y actualizando el marcador. Este no será el puntaje oficial, pero servirá como guía para ver qué bots funcionan mejor. Sin embargo, cuando se acerca la Navidad, espero que entiendan que no estaré disponible en todo momento. Haré todo lo posible para ejecutar simulaciones y responder cualquier pregunta relacionada con el desafío.
Pruébelo usted mismo
Si desea ejecutar sus propias simulaciones, aquí está el código completo del controlador que ejecuta la simulación, incluidos dos bots de ejemplo.
Controlador
Aquí está el controlador actualizado para este desafío. Admite salidas ANSI, subprocesos múltiples y recopila estadísticas adicionales gracias a AKroell ! Cuando realice cambios en el controlador, actualizaré la publicación una vez que se complete la documentación.
Gracias a BMO , el controlador ahora puede descargar todos los bots de esta publicación usando la -d
bandera. Otra funcionalidad no ha cambiado en esta versión. ¡Esto debería garantizar que todos sus últimos cambios se simulen lo antes posible!
#!/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 desea acceder al controlador original para este desafío, está disponible en el historial de edición. El nuevo controlador tiene exactamente la misma lógica para ejecutar el juego, la única diferencia es el rendimiento, la recopilación de estadísticas y la impresión más bonita.
Bots
En mi máquina, los bots se guardan en el archivo forty_game_bots.py
. Si usa cualquier otro nombre para el archivo, debe actualizar la import
declaración en la parte superior del controlador.
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
Ejecutando la simulación
Para ejecutar una simulación, guarde los dos fragmentos de código publicados anteriormente en dos archivos separados. Los he guardado como forty_game_controller.py
y forty_game_bots.py
. Luego simplemente usa python forty_game_controller.py
o python3 forty_game_controller.py
depende de su configuración de Python. Siga las instrucciones a partir de ahí si desea configurar su simulación aún más, o intente jugar con el código si lo desea.
Estadísticas del juego
Si está haciendo un bot que apunta a un cierto puntaje sin tener en cuenta otros bots, estos son los percentiles de puntaje ganador:
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 44|
| 75.00| 48|
| 90.00| 51|
| 95.00| 54|
| 99.00| 58|
| 99.90| 67|
| 99.99| 126|
+----------+-----+
Puntuaciones altas
A medida que se publiquen más respuestas, intentaré mantener esta lista actualizada. El contenido de la lista siempre será de la última simulación. Los bots ThrowTwiceBot
y GoToTenBot
son los bots del código anterior, y se usan como referencia. Hice una simulación con 10 ^ 8 juegos, que tardó aproximadamente 1 hora. Luego vi que el juego alcanzó la estabilidad en comparación con mis carreras con 10 ^ 7 juegos. Sin embargo, con la gente aún publicando bots, no haré más simulaciones hasta que la frecuencia de las respuestas haya disminuido.
Intento agregar todos los bots nuevos y agregar cualquier cambio que haya realizado a los bots existentes. Si parece que me he perdido su bot o cualquier cambio nuevo que tenga, escriba en el chat y me aseguraré de tener su última versión en la próxima simulación.
¡Ahora tenemos más estadísticas para cada bot gracias a AKroell ! Las tres nuevas columnas contienen el puntaje máximo en todos los juegos, el puntaje promedio por juego y el puntaje promedio al ganar para cada bot.
Como se señaló en los comentarios, hubo un problema con la lógica del juego que hizo que los bots que tenían un índice más alto dentro de un juego obtuvieran una ronda adicional en algunos casos. Esto se ha solucionado ahora, y las puntuaciones a continuación reflejan esto.
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|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
Los siguientes bots (excepto Rebel
) están hechos para romper las reglas, y los creadores han acordado no participar en el torneo oficial. Sin embargo, sigo pensando que sus ideas son creativas y merecen una mención honorífica. Rebel también está en esta lista porque usa una estrategia inteligente para evitar el sabotaje, y en realidad funciona mejor con el bot de sabotaje en juego.
Los bots NeoBot
y KwisatzHaderach
siguen las reglas, pero usan un vacío al predecir el generador aleatorio. Como estos robots requieren muchos recursos para simular, he agregado sus estadísticas de una simulación con menos juegos. El bot HarkonnenBot
logra la victoria al deshabilitar todos los demás bots, lo cual es estrictamente contrario a las reglas.
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|
+----------+-----+
Respuestas:
OptFor2X
Este bot sigue una aproximación a la estrategia óptima para la versión de dos jugadores de este juego, utilizando solo su puntaje y el puntaje del mejor oponente. En la última ronda, la versión actualizada considera todos los puntajes.
fuente
NeoBot
En cambio, solo trate de darse cuenta de la verdad: no hay cuchara
NeoBot se asoma a la matriz (también conocido como aleatorio) y predice si el próximo lanzamiento será un 6 o no, no puede hacer nada para recibir un 6 para comenzar, pero está más que feliz de esquivar un ender de racha.
NeoBot en realidad no modifica el controlador o el tiempo de ejecución, solo pide cortésmente a la biblioteca más información.
fuente
Enjambre cooperativo
Estrategia
No creo que nadie más haya notado la importancia de esta regla:
Si todos los bots rodaran hasta que se rompieran, ¡todos tendrían un puntaje de cero al final de la ronda 200 y todos ganarían! Por lo tanto, la estrategia del Enjambre Cooperativo es cooperar siempre que todos los jugadores tengan un puntaje de cero, pero jugar normalmente si alguien anota algún punto.
En esta publicación, envío dos bots: el primero es CooperativeSwarmBot y el segundo es CooperativeThrowTwice. CooperativeSwarmBot sirve como una clase base para todos los bots que son formalmente parte del enjambre cooperativo, y tiene un comportamiento de marcador de posición de simplemente aceptar su primer lanzamiento exitoso cuando falla la cooperación. CooperativeSwarmBot tiene CooperativeSwarmBot como padre y es idéntico en todos los aspectos, excepto que su comportamiento no cooperativo es hacer dos rollos en lugar de uno. En los próximos días revisaré esta publicación para agregar nuevos bots que usen comportamientos mucho más inteligentes que juegan contra bots no cooperativos.
Código
Análisis
Viabilidad
Es muy difícil cooperar en este juego porque necesitamos el apoyo de los ocho jugadores para que funcione. Dado que cada clase de bot está limitada a una instancia por juego, este es un objetivo difícil de lograr. Por ejemplo, las probabilidades de elegir ocho bots cooperativos de un grupo de 100 bots cooperativos y 30 bots no cooperativos son:
En términos más generales, las probabilidades de elegir bots cooperativos de un grupo de bots cooperativos bots no cooperativos son:yo C norte
A partir de esta ecuación, podemos mostrar fácilmente que necesitaríamos alrededor de 430 bots cooperativos para que el 50% de los juegos terminen cooperativamente, o alrededor de 2900 bots para el 90% (usando según las reglas, ).i = 8 n = 38
Caso de estudio
Por varias razones (vea las notas al pie 1 y 2), un enjambre cooperativo adecuado nunca competirá en los juegos oficiales. Como tal, resumiré los resultados de una de mis propias simulaciones en esta sección.
Esta simulación ejecutó 10000 juegos usando los otros 38 bots que habían sido publicados aquí la última vez que revisé y 2900 bots que tenían CooperativeSwarmBot como su clase principal. El controlador informó que 9051 de los 10000 juegos (90.51%) terminaron en 200 rondas, lo que está bastante cerca de la predicción de que el 90% de los juegos serían cooperativos. La implementación de estos bots fue trivial; aparte de CooperativeSwarmBot, todos tomaron esta forma:
Menos del 3% de los bots tenían un porcentaje de victorias inferior al 80%, y poco más del 11% de los bots ganaron cada juego que jugaron. La mediana del porcentaje de victorias de los 2900 bots en el enjambre es de alrededor del 86%, lo cual es escandalosamente bueno. En comparación, los mejores jugadores en la clasificación oficial actual ganan menos del 22% de sus juegos. No puedo ajustar la lista completa del enjambre cooperativo dentro de la longitud máxima permitida para una respuesta, por lo que si desea ver eso, tendrá que ir aquí: https://pastebin.com/3Zc8m1Ex
Dado que cada bot jugó en un promedio de aproximadamente 27 juegos, la suerte juega un rol relativamente grande cuando observa los resultados de los bots individuales. Como todavía no he implementado una estrategia avanzada para juegos no cooperativos, la mayoría de los otros bots se beneficiaron drásticamente al jugar contra el enjambre cooperativo, realizando incluso la tasa de ganancia media del enjambre cooperativo del 86%.
Los resultados completos de los bots que no están en el enjambre se enumeran a continuación; Hay dos bots cuyos resultados creo que merecen especial atención. Primero, StopBot no pudo ganar ningún juego. Esto es particularmente trágico porque el enjambre cooperativo estaba usando exactamente la misma estrategia que StopBot; habrías esperado que StopBot ganara ocho de sus juegos por casualidad, y un poco más porque el enjambre cooperativo se ve obligado a dar a sus oponentes el primer movimiento. Sin embargo, el segundo resultado interesante es que el arduo trabajo de PointsAreForNerdsBot finalmente dio sus frutos: ¡cooperó con el enjambre y logró ganar todos los juegos que jugó!
Defectos
Hay un par de inconvenientes en este enfoque cooperativo. Primero, cuando juegan contra bots no cooperativos, los bots cooperativos nunca obtienen la ventaja del primer turno porque cuando juegan primero, todavía no saben si sus oponentes están dispuestos a cooperar y, por lo tanto, no tienen más remedio que obtener un puntaje de cero. Del mismo modo, esta estrategia cooperativa es extremadamente vulnerable a la explotación por parte de robots maliciosos; Por ejemplo, durante el juego cooperativo, el bot que juega el último en la última ronda puede optar por dejar de tirar inmediatamente para que todos los demás pierdan (suponiendo, por supuesto, que su primer lanzamiento no fue un seis).
Al cooperar, todos los bots pueden lograr la solución óptima de una tasa de ganancia del 100%. Como tal, si la tasa de ganancia fuera lo único que importaba, entonces la cooperación sería un equilibrio estable y no habría nada de qué preocuparse. Sin embargo, algunos bots pueden priorizar otros objetivos, como llegar a la cima de la clasificación. Esto significa que existe el riesgo de que otro bot falle después de tu último turno, lo que crea un incentivo para que lo deseches primero. Debido a que la configuración de esta competencia no nos permite ver lo que hicieron nuestros oponentes en sus juegos anteriores, no podemos penalizar a las personas que desertaron. Por lo tanto, la cooperación es, en última instancia, un equilibrio inestable condenado al fracaso.
Notas al pie
[1]: Las razones principales por las que no quiero enviar miles de bots en lugar de solo dos son que hacerlo retrasaría la simulación por un factor del orden de 1000 [2], y que hacerlo afectaría significativamente ganar porcentajes ya que otros bots estarían jugando casi exclusivamente contra el enjambre en lugar de entre sí. Sin embargo, lo más importante es el hecho de que, incluso si quisiera, no sería capaz de hacer tantos bots en un plazo razonable sin romper el espíritu de la regla de que "un bot no debe implementar exactamente la misma estrategia que un existente, intencional o accidentalmente ".
[2]: Creo que hay dos razones principales por las que la simulación se ralentiza cuando se ejecuta un enjambre cooperativo. Primero, más bots significan más juegos si quieres que cada bot juegue en la misma cantidad de juegos (en el caso de estudio, la cantidad de juegos diferirá en un factor de aproximadamente 77). Segundo, los juegos cooperativos solo toman más tiempo porque duran 200 rondas completas, y dentro de una ronda los jugadores tienen que seguir rodando indefinidamente. Para mi configuración, los juegos tardaron 40 veces más en simularse: el estudio de caso tardó un poco más de tres minutos en ejecutar 10000 juegos, pero después de eliminar el enjambre cooperativo terminaría 10000 juegos en solo 4.5 segundos. Entre estas dos razones, calculo que tomaría aproximadamente 3100 veces más tiempo medir con precisión el rendimiento de los bots cuando hay un enjambre que compite en comparación con cuando no lo hay.
fuente
GoTo20Bot
Solo inténtalo con todos
GoToNBot
, y 20, 22, 24 juega mejor. No se porque.Actualización: siempre deja de lanzar si obtienes un puntaje de 40 o más.
fuente
end_score
4000 (y cambié su bot para usar esto en eltarget
cálculo), los 15-16 bots fueron mucho mejores. Pero si el juego fuera solo para aumentar tu puntaje, sería trivial.end_score
es 4000, es casi imposible obtener 4000 antes de 200 turnos. Y el juego es simplemente quién obtuvo la puntuación más alta en 200 turnos. Y detenerse en 15 debería funcionar, ya que esta vez la estrategia para la puntuación más alta en un turno es igual a la puntuación más alta en 200 turnos.Rodillo Adaptativo
Comienza más agresivo y se calma hacia el final de la ronda.
Si cree que está ganando, tira un tiempo extra por seguridad.
fuente
lim = max(min(self.end_score - scores[self.index], 24), 6)
elevar el máximo a 24 y agregar un mínimo de 6 aumentan el porcentaje ganador por sí mismos y aún más combinados.Alfa
Alpha se niega a ser el segundo de nadie. Mientras haya un bot con una puntuación más alta, seguirá rodando.
fuente
yield
funciona, si comienza a rodar, nunca se detendrá. Querrás actualizarmy_score
en el ciclo.NotTooFarBehindBot
La idea es que otros bots pueden perder puntos, por lo que estar en segundo lugar no es malo, pero si estás muy atrasado, también podrías ir a la quiebra.
fuente
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
. A pesar de que su bot está a la cabeza después de 7 lanzamientos, continúa hasta que alcanza un 6. ¡Mientras escribo esto, descubrí el problema! Elscores
único contiene los puntajes totales, no los casos de dado para la ronda actual. Deberías modificarlo para que seacurrent_score = scores[self.index] + sum(self.current_throws)
.GoHomeBot
Queremos ir a lo grande o ir a casa, ¿verdad? GoHomeBot en su mayoría solo se va a casa. (¡Pero sorprendentemente bien!)
fuente
scores
lista. Había un bot como este antes (el bot GoToEnd), pero David eliminó su respuesta. Reemplazaré ese bot por el tuyo.Asegúrese de llevar
AsegúreseLead toma prestadas ideas de GoTo20Bot. Agrega el concepto que siempre considera (cuando está en last_round o llegando a 40) que hay otros que tendrán al menos un rollo más. Por lo tanto, el bot intenta adelantarse un poco, de modo que tienen que ponerse al día.
fuente
Roll6TimesV2
No supera el mejor actual, pero creo que será mucho mejor con más bots en juego.
Juego realmente impresionante por cierto.
fuente
StopBot
Literalmente solo un tiro.
Esto es equivalente a la
Bot
clase base .fuente
BringMyOwn_dice (BMO_d)
Este bot ama los dados, trae 2 (parece que realiza el mejor) dado propio. Antes de lanzar dados en una ronda, lanza sus propios 2 dados y calcula su suma, esta es la cantidad de lanzamientos que realizará, solo lanza si aún no tiene 40 puntos.
fuente
FooBot
fuente
# Must throw at least once
no es necesario: se lanza una vez antes de llamar a su bot. Tu bot siempre lanzará un mínimo de dos veces.make_throw
método desde el principio, cuando quería que los jugadores pudieran saltar su turno. Supongo que sería un nombre más apropiadokeep_throwing
. Gracias por los comentarios en la caja de arena, ¡realmente ayudó a hacer de este un desafío adecuado!Ir a lo grande temprano
Concepto: Intenta ganar en grande en una tirada temprana (llegando a 25) y luego avanza lentamente desde allí 2 tiradas a la vez.
fuente
BinaryBot
Intenta acercarse al puntaje final, de modo que tan pronto como alguien más active la última ronda, pueda superar su puntaje para la victoria. El objetivo siempre está a medio camino entre el puntaje actual y el puntaje final.
fuente
Hesitate
también se niega a cruzar la línea primero. Necesita rodear su función con lasclass
cosas.PointsAreForNerdsBot
Este no necesita explicación.
OneInFiveBot
Sigue rodando hasta que saca un cinco en su propio dado de 5 lados. ¡Cinco es menos de seis, así que TIENE QUE GANAR!
fuente
OneInFiveBot
es una idea ingeniosa, pero creo que sufre en el final del juego en comparación con algunos de los robots más avanzados. Sigue siendo una gran presentación!OneInFiveBot
es bastante interesante en la forma en que constantemente tiene la puntuación más alta alcanzada.StopBot
un saco de boxeo: P. El OneInFiveBot en realidad es bastante bueno, ¡buen trabajo!OneInFiveBot
y me está yendo mucho mejor de lo que esperabaLizduadacBot
Intenta ganar en 1 paso. La condición final es algo arbitraria.
Esta es también mi primera publicación (y soy nuevo en Python), así que si venciera a "PointsAreForNerdsBot", ¡estaría feliz!
fuente
PointsAreForNerdsBot
, pero a tu bot realmente le va bastante bien. Actualizaré el puntaje más tarde esta noche o mañana, pero su tasa de ganancia es de aproximadamente 15%, que es más alta que el promedio de 12.5%.Comienzo lento
Este bot implementa el algoritmo TCP Slow Start. Ajusta su número de lanzamientos ( ni ) de acuerdo con su turno anterior: si no sacó un 6 en el turno anterior, aumenta el ni para este turno; mientras que se reduce ni si lo hizo.
fuente
def updateValues():
deberían serdef updateValues(self):
(odef update_values(self):
si desea seguir PEP8). En segundo lugar, la llamadaupdateValues()
debería serself.updateValues()
(oself.update_vales()
).i
variable en el ciclo while. En este momento, su bot pasa el ciclo while por completo o está atascado en el ciclo while hasta queself.nor
y ver cómo afecta el rendimiento de tu bot.KwisatzHaderach
En los primeros días de este desafío (es decir, antes
NeoBot
se publicó), escribí unOracle
bot casi trivial :pero no lo publiqué porque no pensé que fuera lo suficientemente interesante;) Pero una vez
NeoBot
tomé la delantera, comencé a pensar en cómo vencer su capacidad perfecta para predecir el futuro. Así que aquí hay una cita de Dune; es cuando Paul Atreides, el Kwisatz Haderach, se encuentra en un nexo desde el cual se puede desenrollar una infinidad de futuros diferentes:Así que aquí estaba la respuesta: prever el futuro es cambiarlo; y si tiene mucho cuidado, entonces, por acción selectiva o inacción, puede cambiarlo de manera ventajosa, al menos la mayor parte del tiempo. ¡Incluso
KwisatzHaderach
no pueden obtener una tasa de ganancia del 100%!fuente
NeoBot
sino también mejor! También me gusta cómo das un ejemplo de lo que debería hacer todo lo que usa la aleatoriedad (especialmente el controlador) aquí: usa tu propiarandom.Random
instancia. ComoNeoBot
, esto parece un poco sensible a los cambios de detalles de implementación no especificados del controlador.HarkonnenBot
no toca el RNG; no le importan en absoluto los números aleatorios. Simplemente envenena a todos los demás bots, luego camina hasta la línea de meta lo más lentamente posible. Como muchas delicias culinarias, la venganza es un plato que se saborea mejor lentamente, después de una preparación larga y delicada.NeoBot
(yHarkonnenBot
), seKwisatzHaderach
basa en un solo detalle de la implementación; en particular, no necesita saber cómo se implementa random.random (), solo que el controlador lo usa; DKwisatzHaderach
yHarkonnenBot
del mismo modo queNeoBot
. Recibirán sus puntajes de una simulación con menos juegos, y no estarán en la simulación oficial. Sin embargo, terminarán en la lista de puntajes muy parecidosNeoBot
. La razón principal para que no estén en la simulación oficial es que arruinarán otras estrategias de bot. Sin embargo.WisdomOfCrowds
debería ser adecuado para la participación, y tengo curiosidad por los nuevos cambios que ha realizado.Bueno, eso es obvio.
fuente
LastRound actúa como si siempre fuera la última ronda y es el último bot: sigue rodando hasta que está a la cabeza. Tampoco quiere conformarse con menos de 15 puntos a menos que en realidad sea la última ronda o llegue a 40 puntos.
fuente
CuotaBot
Implementé un ingenuo sistema de "cuotas", que en realidad parecía tener un puntaje bastante alto en general.
fuente
if own_score mean + 5:
me da un error Tambiénwhile sum(self.current_throws)
<
y>
símbolos que interferían con las<pre>
etiquetas que estaba usandoExpectativasBot
Simplemente juega directamente, calcula el valor esperado para el lanzamiento de dados y solo lo hace si es positivo.
Estaba teniendo problemas para ejecutar el controlador, obtuve un "NameError: el nombre 'bots_per_game' no está definido" en el multiproceso, así que realmente no tengo idea de cómo funciona.
fuente
BlessRNG
BlessRNG FrankerZ GabeN BlessRNG
fuente
Cuarenta Adolescentes
Intenta 14 puntos hasta la última ronda, luego asume que todos los demás intentarán 14 puntos e intentarán empatar ese puntaje.
fuente
TypeError: unsupported operand type(s) for -: 'list' and 'int'
con tu bot.max_projected_score
debería ser el máximo de la lista en lugar de la lista completa, ¿estoy en lo cierto? De lo contrario, tengo el mismo problema que tsh.Vacilar
Hace dos pasos modestos, luego espera a que alguien más cruce la línea. La versión actualizada ya no intenta superar la puntuación más alta, solo quiere alcanzarla, ¡mejorando el rendimiento eliminando dos bytes del código fuente!
fuente
Rebelde
Este bot combina la estrategia simple de
Hesitate
con la estrategia avanzada de la última rondaBotFor2X
, intenta recordar quién es y se vuelve loco cuando descubre que vive en una ilusión.fuente
HarkonnenBot
para queRebel
ya no se pueda deshacer;) y también lo modifiquéTleilaxuBot
para queRebel
ya no lo detecte.Toma cinco
La mitad del tiempo, sacaremos un 5 antes que un 6. Cuando lo hagamos, retiraremos.
fuente
Cazador
El cazador intenta alcanzar la posición uno. Si es la última ronda, intenta desesperadamente alcanzar al menos 50 puntos. Solo por si acaso, lanza al menos cuatro veces, pase lo que pase.
[editar 1: estrategia de ir por el oro agregada en la última ronda]
[editar 2: lógica actualizada porque pensé erróneamente que un bot obtendría una puntuación de 40 en lugar de solo la puntuación más alta del bot]
[editar 3: hizo al cazador un poco más defensivo en el juego final]
fuente
FutureBot
OneStepAheadBot
Un par de bots, traen sus propios juegos de dados y los tira para predecir el futuro. Si uno es un 6, se detienen, FutureBot no puede recordar cuál de sus 2 dados fue para el próximo lanzamiento, por lo que se rinde.
Me pregunto cuál lo hará mejor.
OneStepAhead es un poco demasiado similar a OneInFive para mi gusto, pero también quiero ver cómo se compara con FutureBot y OneInFive.
Editar: ahora se detienen después de llegar a 45
fuente