Torneio terminado!
O torneio acabou agora! A simulação final foi realizada durante a noite, totalizando jogos. O vencedor é Christian Sievers com seu bot OptFor2X . Christian Sievers também conseguiu garantir o segundo lugar com Rebel . Parabéns! Abaixo você pode ver a lista oficial de recordes do torneio.
Se você ainda deseja jogar, é bem-vindo ao usar o controlador postado abaixo e ao usar o código nele para criar seu próprio jogo.
Fui convidado a jogar um jogo de dados que nunca tinha ouvido falar. As regras eram simples, mas acho que seriam perfeitas para um desafio KotH.
As regras
O começo do jogo
O dado gira em torno da mesa e, toda vez que é a sua vez, você joga o dado quantas vezes quiser. No entanto, você deve jogá-lo pelo menos uma vez. Você acompanha a soma de todos os lançamentos da sua rodada. Se você optar por parar, a pontuação da rodada será adicionada à sua pontuação total.
Então, por que você pararia de jogar o dado? Porque se você obtiver 6, sua pontuação na rodada inteira será zero e o dado será transmitido. Assim, o objetivo inicial é aumentar sua pontuação o mais rápido possível.
Quem é o vencedor?
Quando o primeiro jogador em volta da mesa atinge 40 pontos ou mais, a última rodada começa. Uma vez iniciada a última rodada, todos, exceto a pessoa que iniciou a última rodada, recebem mais um turno.
As regras para a última rodada são as mesmas de qualquer outra rodada. Você escolhe continuar jogando ou parar. No entanto, você sabe que não tem chance de ganhar se não obtiver uma pontuação maior que a anterior na última rodada. Mas se você continuar indo longe demais, poderá receber um 6.
No entanto, há mais uma regra a ser levada em consideração. Se sua pontuação total atual (sua pontuação anterior + sua pontuação atual da rodada) for 40 ou mais e você acertar um 6, sua pontuação total será 0. Isso significa que você deve começar tudo de novo. Se você acertar um 6 quando sua pontuação total atual for 40 ou mais, o jogo continuará normal, exceto que você está no último lugar. A última rodada não é acionada quando sua pontuação total é redefinida. Você ainda pode vencer a rodada, mas ela se torna mais desafiadora.
O vencedor é o jogador com a maior pontuação assim que a última rodada terminar. Se dois ou mais jogadores compartilharem a mesma pontuação, todos serão contados como vencedores.
Uma regra adicional é que o jogo continue por no máximo 200 rodadas. Isso evita os casos em que vários bots basicamente continuam jogando até atingir 6 para permanecer na pontuação atual. Depois que a 199ª rodada é aprovada, last_round
é definido como verdadeiro e mais uma rodada é jogada. Se o jogo for para 200 rodadas, o bot (ou bots) com a maior pontuação será o vencedor, mesmo que eles não tenham 40 pontos ou mais.
Recapitular
- A cada rodada, você continua jogando o dado até optar por parar ou obter um 6
- Você deve jogar o dado uma vez (se o seu primeiro lançamento for um 6, sua rodada termina imediatamente)
- Se você obtiver um 6, sua pontuação atual será definida como 0 (não a pontuação total)
- Você adiciona sua pontuação atual à sua pontuação total após cada rodada
- Quando um bot termina seu turno, resultando em uma pontuação total de pelo menos 40, todo mundo recebe o último turno
- Se sua pontuação total atual é e você obtém um 6, sua pontuação total é definida como 0 e sua rodada termina.
- A última rodada não é acionada quando ocorrer o acima
- A pessoa com a maior pontuação total após a última rodada é o vencedor
- Caso haja vários vencedores, todos serão contados como vencedores
- O jogo dura no máximo 200 rodadas
Esclarecimento das pontuações
- Pontuação total: a pontuação que você salvou das rodadas anteriores
- Pontuação atual: a pontuação da rodada atual
- Pontuação total atual: a soma das duas pontuações acima
Como você participa
Para participar deste desafio do KotH, você deve escrever uma classe Python que herda Bot
. Você deve implementar a função: make_throw(self, scores, last_round)
. Essa função será chamada assim que for a sua vez, e seu primeiro lançamento não for 6. Para continuar jogando, você deve yield True
. Para parar de jogar, você deveria yield False
. Após cada lançamento, a função pai update_state
é chamada. Assim, você tem acesso a sua lança para a rodada atual usando a variável self.current_throws
. Você também tem acesso ao seu próprio índice usando self.index
. Assim, para ver sua própria pontuação total, você usaria scores[self.index]
. Você também pode acessar o end_score
para o jogo usando self.end_score
, mas pode assumir com segurança que serão 40 para esse desafio.
Você tem permissão para criar funções auxiliares dentro da sua classe. Você também pode substituir as funções existentes na Bot
classe pai, por exemplo, se desejar adicionar mais propriedades de classe. Você não tem permissão para modificar o estado do jogo de qualquer forma, exceto cedendo True
ou False
.
Você pode buscar inspiração nesta postagem e copiar qualquer um dos dois bots que incluí aqui. No entanto, receio que não sejam particularmente eficazes ...
Ao permitir outros idiomas
Tanto na sandbox quanto no The XIX XIX Byte, tivemos discussões sobre como permitir envios em outros idiomas. Depois de ler sobre essas implementações e ouvir argumentos de ambos os lados, decidi restringir esse desafio apenas ao Python. Isso se deve a dois fatores: o tempo necessário para suportar vários idiomas e a aleatoriedade desse desafio, exigindo um número alto de iterações para alcançar a estabilidade. Espero que você ainda participe e, se quiser aprender algum Python para esse desafio, tentarei estar disponível no bate-papo o mais rápido possível.
Para qualquer dúvida que possa ter, escreva na sala de bate-papo para este desafio . Vejo você lá!
Regras
- A sabotagem é permitida e incentivada. Ou seja, sabotar contra outros jogadores
- Qualquer tentativa de mexer com o controlador, tempo de execução ou outros envios será desqualificada. Todos os envios devem funcionar apenas com as entradas e armazenamento fornecidos.
- Qualquer bot que use mais de 500 MB de memória para tomar sua decisão será desqualificado (se você precisar de tanta memória, deve repensar suas escolhas)
- Um bot não deve implementar exatamente a mesma estratégia que uma existente, intencional ou acidentalmente.
- Você tem permissão para atualizar seu bot durante o período do desafio. No entanto, você também pode postar outro bot se sua abordagem for diferente.
Exemplo
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Esse bot continuará até ter uma pontuação de pelo menos 10 na rodada ou lançar um 6. Observe que você não precisa de lógica para lidar com o arremesso 6. Observe também que, se seu primeiro arremesso for 6, make_throw
é nunca ligou, já que sua rodada termina imediatamente.
Para quem é novo no Python (e novo no yield
conceito), mas quer experimentar, a yield
palavra-chave é semelhante a um retorno em alguns aspectos, mas diferente em outros aspectos. Você pode ler sobre o conceito aqui . Basicamente, uma vez que você yield
, sua função será interrompida, e o valor yield
editado será enviado de volta ao controlador. Lá, o controlador lida com sua lógica até a hora de seu bot tomar outra decisão. Em seguida, o controlador envia o lançamento dos dados e sua make_throw
função continuará sendo executada exatamente onde parou antes, basicamente na linha após a yield
instrução anterior .
Dessa forma, o controlador do jogo pode atualizar o estado sem exigir uma chamada de função bot separada para cada lançamento de dados.
Especificação
Você pode usar qualquer biblioteca Python disponível em pip
. Para garantir que eu seja capaz de obter uma boa média, você tem um limite de tempo de 100 milissegundos por rodada. Eu ficaria muito feliz se o seu script fosse muito mais rápido que isso, para que eu possa rodar mais rodadas.
Avaliação
Para encontrar o vencedor, pegarei todos os bots e os executarei em grupos aleatórios de 8. Se houver menos de 8 classes enviadas, executarei em grupos aleatórios de 4 para evitar sempre ter todos os bots em cada rodada. Vou executar simulações por cerca de 8 horas, e o vencedor será o bot com a maior porcentagem de vitórias. Vou iniciar as simulações finais no início de 2019, dando todo o Natal para codificar seus bots! A data final preliminar é 4 de janeiro, mas, se houver muito pouco tempo, posso alterá-la para uma data posterior.
Até lá, tentarei fazer uma simulação diária usando 30 a 60 minutos de tempo da CPU e atualizando o placar. Essa não será a pontuação oficial, mas servirá como um guia para ver quais robôs têm o melhor desempenho. No entanto, com o Natal chegando, espero que você entenda que eu não estarei disponível o tempo todo. Farei o meu melhor para executar simulações e responder a quaisquer perguntas relacionadas ao desafio.
Teste você mesmo
Se você deseja executar suas próprias simulações, aqui está o código completo do controlador que está executando a simulação, incluindo dois bots de exemplo.
Controlador
Aqui está o controlador atualizado para este desafio. Ele suporta saídas ANSI, multi-threading e coleta estatísticas adicionais graças ao AKroell ! Quando eu fizer alterações no controlador, atualizarei a postagem assim que a documentação estiver concluída.
Graças ao BMO , o controlador agora pode baixar todos os bots desta postagem usando a -d
bandeira. Outra funcionalidade é inalterada nesta versão. Isso deve garantir que todas as suas alterações mais recentes sejam simuladas o mais rápido possível!
#!/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)
Se você deseja acessar o controlador original para esse desafio, ele está disponível no histórico de edições. O novo controlador possui exatamente a mesma lógica para executar o jogo, a única diferença é desempenho, coleta de estatísticas e impressão mais bonita.
Bots
Na minha máquina, os bots são mantidos no arquivo forty_game_bots.py
. Se você usar outro nome para o arquivo, atualize a import
instrução na parte superior do 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
Executando a simulação
Para executar uma simulação, salve os dois trechos de código publicados acima em dois arquivos separados. Eu os salvei como forty_game_controller.py
e forty_game_bots.py
. Então você simplesmente usa python forty_game_controller.py
ou python3 forty_game_controller.py
depende da sua configuração do Python. Siga as instruções de lá se desejar configurar ainda mais sua simulação ou tente mexer no código, se desejar.
Estatísticas do jogo
Se você está criando um bot que visa uma determinada pontuação sem levar em consideração outros bots, estes são os percentis de pontuação vencedora:
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 44|
| 75.00| 48|
| 90.00| 51|
| 95.00| 54|
| 99.00| 58|
| 99.90| 67|
| 99.99| 126|
+----------+-----+
Notas altas
À medida que mais respostas forem postadas, tentarei manter essa lista atualizada. O conteúdo da lista sempre será da última simulação. Os bots ThrowTwiceBot
e GoToTenBot
são os bots do código acima e são usados como referência. Fiz uma simulação com 10 ^ 8 jogos, o que levou cerca de 1 hora. Então eu vi que o jogo alcançou estabilidade em comparação com minhas corridas com 10 ^ 7 jogos. No entanto, com as pessoas ainda postando bots, não farei mais simulações até que a frequência das respostas diminua.
Tento adicionar todos os novos bots e quaisquer alterações feitas nos bots existentes. Se parece que eu perdi o seu bot ou qualquer alteração nova que você tenha, escreva no chat e eu me certificarei de ter a sua versão mais recente na próxima simulação.
Agora temos mais estatísticas para cada bot, graças ao AKroell ! As três novas colunas contêm a pontuação máxima em todos os jogos, a pontuação média por jogo e a pontuação média ao ganhar para cada bot.
Como apontado nos comentários, houve um problema com a lógica do jogo que fez bots com um índice mais alto em um jogo obter uma rodada extra em alguns casos. Isso foi corrigido agora, e as pontuações abaixo refletem isso.
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|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
Os seguintes bots (exceto Rebel
) são feitos para violar as regras, e os criadores concordaram em não participar do torneio oficial. No entanto, ainda acho que suas idéias são criativas e merecem uma menção honrosa. Rebelde também está nesta lista porque usa uma estratégia inteligente para evitar sabotagem e, na verdade, tem um desempenho melhor com o bot de sabotagem em jogo.
Os bots NeoBot
e KwisatzHaderach
faz seguir as regras, mas usa uma brecha prevendo o gerador aleatório. Como esses robôs precisam de muitos recursos para simular, adicionei suas estatísticas a partir de uma simulação com menos jogos. O bot HarkonnenBot
alcança a vitória desativando todos os outros bots, o que é estritamente contra as regras.
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|
+----------+-----+
Respostas:
OptFor2X
Este bot segue uma aproximação à estratégia ideal para a versão para dois jogadores deste jogo, usando apenas sua pontuação e a pontuação do melhor oponente. Na última rodada, a versão atualizada considera todas as pontuações.
fonte
NeoBot
Em vez disso, tente apenas perceber a verdade - não há colher
O NeoBot espreita a matriz (também conhecida como aleatória) e prevê se o próximo lançamento será um 6 ou não - ele não pode fazer nada em relação a receber um 6 para começar, mas é mais do que feliz em evitar um ender.
O NeoBot, na verdade, não modifica o controlador ou o tempo de execução, apenas solicita educadamente à biblioteca mais informações.
fonte
Enxame Cooperativo
Estratégia
Acho que ninguém mais notou o significado dessa regra:
Se todos os bot rolassem sempre até serem eliminados, todos teriam pontuação zero no final do round 200 e todos venceria! Assim, a estratégia do Enxame Cooperativo é cooperar desde que todos os jogadores tenham pontuação zero, mas jogar normalmente se alguém marcar algum ponto.
Neste post, estou enviando dois bots: o primeiro é CooperativeSwarmBot e o segundo é CooperativeThrowTwice. O CooperativeSwarmBot serve como uma classe base para todos os bots que fazem parte formalmente do enxame cooperativo e tem um comportamento reservado simplesmente de aceitar sua primeira jogada bem-sucedida quando a cooperação falha. CooperativeSwarmBot tem CooperativeSwarmBot como pai e é idêntico a ele em todos os aspectos, exceto que seu comportamento não cooperativo é fazer duas rolagens em vez de uma. Nos próximos dias, revisarei este post para adicionar novos bots que usam um comportamento muito mais inteligente jogando contra bots não cooperativos.
Código
Análise
Viabilidade
É muito difícil cooperar neste jogo, porque precisamos do apoio de todos os oito jogadores para que ele funcione. Como cada classe de bot é limitada a uma instância por jogo, esse é um objetivo difícil de alcançar. Por exemplo, as chances de escolher oito robôs cooperativos de um conjunto de 100 robôs cooperativos e 30 robôs não cooperativos são:
Estudo de caso
Por várias razões (veja as notas de rodapé 1 e 2), um enxame cooperativo adequado nunca competirá nos jogos oficiais. Como tal, resumirei os resultados de uma de minhas próprias simulações nesta seção.
Essa simulação executou 10000 jogos usando os outros 38 bots postados aqui na última vez que verifiquei e 2900 bots que tinham o CooperativeSwarmBot como sua classe pai. O controlador informou que 9051 dos 10000 jogos (90,51%) terminaram em 200 rodadas, o que é bastante próximo da previsão de que 90% dos jogos seriam cooperativos. A implementação desses bots foi trivial; exceto CooperativeSwarmBot, todos eles assumiram este formato:
Menos de 3% dos bots tiveram uma porcentagem de vitória abaixo de 80% e pouco mais de 11% dos bots venceram todos os jogos que disputaram. A porcentagem média de vitórias dos 2900 bots no enxame é de cerca de 86%, o que é escandalosamente bom. Para comparação, os melhores jogadores da tabela oficial de líderes atual vencem menos de 22% de seus jogos. Não consigo encaixar a lista completa do enxame cooperativo dentro do comprimento máximo permitido para uma resposta; portanto, se você quiser ver que precisará ir aqui: https://pastebin.com/3Zc8m1Ex
Como cada bot jogou em média cerca de 27 jogos, a sorte joga um rolo relativamente grande quando você olha para os resultados de bots individuais. Como ainda não implementei uma estratégia avançada para jogos não cooperativos, a maioria dos outros bots se beneficiou drasticamente ao jogar contra o enxame cooperativo, realizando até a taxa média de vitórias média de 86% do enxame cooperativo.
Os resultados completos para bots que não estão no enxame estão listados abaixo; existem dois robôs cujos resultados eu acho que merecem atenção especial. Primeiro, o StopBot não conseguiu vencer nenhum jogo. Isso é particularmente trágico porque o enxame cooperativo estava realmente usando exatamente a mesma estratégia que o StopBot; você esperaria que o StopBot vencesse oito de seus jogos por acaso, e um pouco mais porque o enxame cooperativo é forçado a dar aos seus oponentes a primeira jogada. O segundo resultado interessante, no entanto, é que o trabalho duro de PointsAreForNerdsBot finalmente valeu a pena: ele cooperou com o enxame e conseguiu vencer todos os jogos que jogou!
Falhas
Existem algumas desvantagens nessa abordagem cooperativa. Primeiro, quando jogam contra bots não cooperativos, os bots cooperativos nunca obtêm a vantagem do primeiro turno porque, quando jogam primeiro, ainda não sabem se seus oponentes estão ou não dispostos a cooperar e, portanto, não têm escolha a não ser obter pontuação zero. Da mesma forma, essa estratégia cooperativa é extremamente vulnerável à exploração por bots maliciosos; por exemplo, durante uma jogada cooperativa, o bot que joga por último na última rodada pode optar por parar de rolar imediatamente para fazer com que todo mundo perca (supondo, é claro, que o primeiro lançamento não tenha sido seis).
Ao cooperar, todos os bots podem alcançar a solução ideal de uma taxa de vitória de 100%. Como tal, se a taxa de vitória fosse a única coisa que importava, a cooperação seria um equilíbrio estável e não haveria nada com que se preocupar. No entanto, alguns bots podem priorizar outros objetivos, como chegar ao topo da classificação. Isso significa que há um risco de que outro bot possa desertar após o seu último turno, o que cria um incentivo para você desertar primeiro. Como a organização desta competição não nos permite ver o que nossos adversários fizeram em seus jogos anteriores, não podemos penalizar as pessoas que desertaram. Assim, a cooperação é, em última análise, um equilíbrio instável, fadado ao fracasso.
Notas de rodapé
[1]: As principais razões pelas quais eu não quero enviar milhares de bots em vez de apenas dois são que isso atrasaria a simulação por um fator da ordem de 1000 [2], e que isso afetaria significativamente ganhar porcentagens, já que outros bots jogariam quase exclusivamente contra o enxame e não um contra o outro. Mais importante, no entanto, é o fato de que, mesmo que eu quisesse, não seria capaz de fazer muitos bots em um período de tempo razoável sem quebrar o espírito da regra de que "um bot não deve implementar exatamente a mesma estratégia que um existente, intencional ou acidentalmente ".
[2]: Eu acho que há duas razões principais pelas quais a simulação fica mais lenta ao executar um enxame cooperativo. Primeiro, mais bots significam mais jogos se você quiser que cada bot jogue no mesmo número de jogos (no estudo de caso, o número de jogos diferirá por um fator de cerca de 77). Segundo, os jogos cooperativos demoram mais porque duram 200 rodadas completas e, dentro de uma rodada, os jogadores precisam continuar rolando indefinidamente. Para minha configuração, os jogos demoraram cerca de 40 vezes mais para simular: o estudo de caso demorou um pouco mais de três minutos para executar 10000 jogos, mas após remover o enxame cooperativo, ele terminava 10000 jogos em apenas 4,5 segundos. Entre essas duas razões, eu estimo que demoraria cerca de 3100 vezes mais para medir com precisão o desempenho dos bots quando houver um enxame competindo em comparação com quando não houver.
fonte
GoTo20Bot
Apenas tente com todos
GoToNBot
, e 20, 22, 24 joga melhor. Não sei porque.Atualização: sempre pare o arremesso se conseguir pontuação 40 ou mais.
fonte
end_score
para 4000 (e mudei seu bot para usar isso notarget
cálculo), os 15-16 bots eram muito melhores. Mas se o jogo fosse apenas para aumentar sua pontuação, seria trivial.end_score
for 4000, é quase impossível obter 4000 antes das 200 voltas. E o jogo é simplesmente quem obteve a maior pontuação em 200 turnos. E parar aos 15 deve funcionar, pois dessa vez a estratégia para a pontuação mais alta em um turno é igual à pontuação mais alta em 200 turnos.Rolo adaptável
Começa mais agressivo e se acalma no final da rodada.
Se acreditar que está ganhando, dedique mais tempo à segurança.
fonte
lim = max(min(self.end_score - scores[self.index], 24), 6)
elevar o máximo para 24 e adicionar um mínimo de 6 aumenta a porcentagem de vitórias por conta própria e ainda mais.Alfa
Alpha se recusa a ficar em segundo lugar com qualquer um. Enquanto houver um bot com uma pontuação mais alta, ele continuará rolando.
fonte
yield
funciona, se começar a rolar, nunca irá parar. Você deseja atualizarmy_score
no loop.NotTooFarBehindBot
A idéia é que outros bots possam perder pontos, portanto, ficar em 2º não é ruim - mas se você estiver muito atrasado, pode muito bem ir à falência.
fonte
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
. Mesmo que seu bot esteja na liderança após 7 jogadas, ele continua até atingir um 6. Enquanto eu digito isso, descobri o problema! Osscores
únicos contêm as pontuações totais, não os casos dos dados da rodada atual. Você deve modificá-lo para sercurrent_score = scores[self.index] + sum(self.current_throws)
.GoHomeBot
Queremos ir grande ou ir para casa, certo? O GoHomeBot geralmente vai para casa. (Mas surpreendentemente bem!)
fonte
scores
lista. Havia um bot como esse antes (o bot do GoToEnd), mas david excluiu a resposta. Vou substituir esse bot pelo seu.AssegurarLead
Certifique-se de emprestar idéias do GoTo20Bot. Ele adiciona o conceito que sempre considera (quando está no last_round ou chega a 40) que existem outros que terão pelo menos mais um teste. Assim, o bot tenta ficar um pouco à frente deles, de modo que eles precisam alcançá-los.
fonte
Roll6TimesV2
Não supera o melhor atual, mas acho que será melhor com mais bots em jogo.
Jogo realmente incrível pelo caminho.
fonte
StopBot
Literalmente, apenas um lance.
Isso é equivalente à
Bot
classe base .fonte
BringMyOwn_dice (BMO_d)
Esse bot adora dados, traz 2 (parece ter o melhor) dados por conta própria. Antes de jogar dados em uma rodada, ele lança seus próprios 2 dados e calcula sua soma, este é o número de lançamentos que ele vai realizar, só joga se ainda não tiver 40 pontos.
fonte
FooBot
fonte
# Must throw at least once
é desnecessário - ele lança uma vez antes de ligar para o seu bot. Seu bot sempre jogará no mínimo duas vezes.make_throw
método desde o início, quando queria que os jogadores pudessem pular sua vez. Eu acho que um nome mais apropriado seriakeep_throwing
. Obrigado pelo feedback na caixa de areia, isso realmente ajudou a tornar este um desafio adequado!Go Big Early
Conceito: Tente ganhar muito em um rolo inicial (chegando a 25) e suba a partir daí 2 rolos de cada vez.
fonte
BinaryBot
Tenta chegar perto da pontuação final, para que assim que outra pessoa inicie a última rodada, ela possa superar a pontuação da vitória. A meta está sempre a meio caminho entre a pontuação atual e a pontuação final.
fonte
Hesitate
também se recusa a cruzar a linha primeiro. Você precisa cercar sua função com essasclass
coisas.PointsAreForNerdsBot
Este não precisa de explicação.
OneInFiveBot
Continua rolando até atingir cinco no seu próprio dado de 5 lados. Cinco é menor que seis, por isso TEM QUE GANHAR!
fonte
OneInFiveBot
é uma ideia interessante, mas acho que ela sofre no final do jogo, em comparação com alguns dos bots mais avançados. Ainda é uma ótima finalização!OneInFiveBot
é bastante interessante na maneira como ele tem consistentemente a maior pontuação geral alcançada.StopBot
um saco de pancadas: P. O OneInFiveBot, na verdade, é um trabalho muito legal e agradável!OneInFiveBot
e está indo muito melhor do que eu esperava #LizduadacBot
Tenta ganhar em 1 passo. A condição final é um tanto arbitrária.
Este também é meu primeiro post (e eu sou novo no Python), por isso, se eu vencer o "PointsAreForNerdsBot", ficaria feliz!
fonte
PointsAreForNerdsBot
, mas seu bot realmente se sai muito bem. Atualizarei a pontuação mais tarde esta noite ou amanhã, mas sua taxa de vitórias é de cerca de 15%, que é superior à média de 12,5%.SlowStart
Este bot implementa o algoritmo TCP Slow Start. Ele ajusta seu número de jogadas ( nor ) de acordo com o turno anterior: se não rolou um 6 no turno anterior, aumenta o nor para esse turno; enquanto reduz nem se o fez.
fonte
def updateValues():
devem serdef updateValues(self):
(oudef update_values(self):
se você deseja seguir o PEP8). Em segundo lugar, a chamadaupdateValues()
deve serself.updateValues()
(ouself.update_vales()
).i
variável no loop while. No momento, seu bot passa o loop while inteiramente ou fica preso no loop while até atingir 6.self.nor
e ver como isso afeta o desempenho do seu bot.KwisatzHaderach
Nos primeiros dias deste desafio (ou seja, antes de
NeoBot
ser publicado), escrevi umOracle
bot quase trivial :mas não o publiquei porque não achei interessante o suficiente;) Mas uma vez
NeoBot
assumi a liderança, comecei a pensar em como superar sua capacidade perfeita de prever o futuro. Então, aqui está uma citação de Dune; é quando Paul Atreides, o Kwisatz Haderach, fica em um nexo do qual uma infinidade de diferentes futuros pode se desenrolar:Então aqui estava a resposta: prever o futuro é mudá-lo; e se você for muito cuidadoso, por ação ou inação seletiva, poderá alterá-lo de uma maneira vantajosa - pelo menos na maioria das vezes. Mesmo os
KwisatzHaderach
que não conseguem obter uma taxa de vitória de 100%!fonte
NeoBot
mas também melhor! Também gosto de como você dá um exemplo do que tudo que usa aleatoriedade (especialmente o controlador) aqui deve fazer: use sua própriarandom.Random
instância. AssimNeoBot
, isso parece um pouco sensível a alterações de detalhes de implementação não especificados do controlador.HarkonnenBot
não toca no RNG; não se importa com números aleatórios. Envenena todos os outros bots e depois caminha até a linha de chegada o mais lentamente possível. Como muitas iguarias culinárias, a vingança é um prato melhor saboreado lentamente, após uma preparação longa e delicada.NeoBot
(eHarkonnenBot
),KwisatzHaderach
conta apenas com um detalhe da implementação; em particular, ele não precisa saber como o random.random () é implementado, apenas que o controlador o usa; DKwisatzHaderach
eHarkonnenBot
da mesma maneira queNeoBot
. Eles receberão suas pontuações de uma simulação com menos jogos e não estarão na simulação oficial. No entanto, eles vão acabar na lista de recordes muito parecidoNeoBot
. A principal razão para eles não estarem na simulação oficial é que eles vão estragar outras estratégias de bot. Contudo.WisdomOfCrowds
deve ser bem adequado para participação, e estou curioso sobre as novas mudanças que você fez para isso!Bem, esse é óbvio
fonte
LastRound age como se fosse sempre a última rodada e o último bot: ele continua rolando até que esteja na liderança. Ele também não quer se contentar com menos de 15 pontos, a menos que seja a última rodada ou atinja 40 pontos.
fonte
QuotaBot
Eu implementei um sistema ingênuo de "cota", que na verdade parecia ter uma pontuação bastante alta em geral.
fonte
if own_score mean + 5:
dá um erro para mim. Tambémwhile sum(self.current_throws)
<
e>
símbolos que interferiam com as<pre>
marcas que eu estava usandoExpectationsBot
Apenas joga direto, calcula o valor esperado para o lançamento de dados e só o faz se for positivo.
Eu estava tendo problemas para executar o controlador, obtive um "NameError: name 'bots_per_game' não está definido" no multithreaded, então realmente não faço ideia de como isso funciona.
fonte
BlessRNG
BlessRNG FrankerZ GabeN BlessRNG
fonte
Quarenta
Tente 14 pontos até a última rodada, então assuma que todos os outros tentarão 14 pontos e tente empatar essa pontuação.
fonte
TypeError: unsupported operand type(s) for -: 'list' and 'int'
com seu bot.max_projected_score
deve ser o máximo da lista e não a lista inteira, estou correto? Caso contrário, eu recebo o mesmo problema que o tsh.Hesitar
Dá dois passos modestos e espera que outra pessoa cruze a linha. A versão atualizada não tenta mais superar o recorde, apenas quer alcançá-lo - melhorando o desempenho excluindo dois bytes do código-fonte!
fonte
Rebelde
Este bot combina a estratégia simples da
Hesitate
avançada estratégia da última rodadaBotFor2X
, tenta lembrar quem é e fica louco quando descobre que vive em uma ilusão.fonte
HarkonnenBot
para queRebel
não possa mais se desfeitar;) e também aprimoreiTleilaxuBot
para queRebel
não a detecte mais!Pegue cinco
Metade do tempo, rolamos um 5 antes de um 6. Quando o fizermos, sacaremos.
fonte
Caçador
O artilheiro tenta alcançar a primeira posição. Se for a última rodada, ele tenta desesperadamente alcançar pelo menos 50 pontos. Apenas por uma boa medida, ele lança pelo menos quatro vezes, não importa o que
[edit 1: adicionada estratégia go-for-gold na última rodada]
[editar 2: lógica atualizada porque pensei erroneamente que um bot teria uma pontuação de 40 em vez de apenas a maior pontuação de bot]
[editar 3: deixou o chaser um pouco mais defensivo no final do jogo]
fonte
FutureBot
OneStepAheadBot
Um par de bots, eles trazem seus próprios conjuntos de dados e os rolam para prever o futuro. Se um deles é um 6, o FutureBot não consegue se lembrar qual dos 2 dados era para o próximo lançamento, então ele desiste.
Eu me pergunto o que fará melhor.
O OneStepAhead é um pouco parecido com o OneInFive para o meu gosto, mas também quero ver como ele se compara ao FutureBot e ao OneInFive.
Edit: Agora eles param depois de atingir 45
fonte