Турнир окончен!
Турнир окончен! Финальная симуляция была проведена ночью, всего игр. Победителем стал Кристиан Сиверс со своим ботом OptFor2X . Кристиану Сиверсу также удалось завоевать второе место с повстанцами . Поздравляем! Ниже вы можете увидеть официальный список рекордов турнира.
Если вы все еще хотите играть в игру, вы можете использовать контроллер, указанный ниже, и использовать код для создания своей собственной игры.
Меня пригласили сыграть в игру в кости, о которой я никогда не слышал. Правила были просты, но я думаю, что это было бы идеально для вызова KotH.
Правила
Начало игры
Кубик проходит по столу, и каждый раз, когда наступает ваша очередь, вы можете бросать кубик столько раз, сколько захотите. Тем не менее, вы должны бросить его хотя бы один раз. Вы отслеживаете сумму всех бросков за ваш раунд. Если вы решите остановиться, счет за раунд добавляется к вашему общему счету.
Так почему бы тебе перестать бросать кубик? Потому что, если вы получите 6, ваш счет за весь раунд станет нулевым, и кубик будет передан. Таким образом, первоначальная цель - как можно быстрее увеличить ваш счет.
Кто является победителем?
Когда первый игрок за столом набирает 40 или более очков, начинается последний раунд. Как только начался последний раунд, все, кроме того, кто инициировал последний раунд, получают еще один ход.
Правила для последнего раунда такие же, как и для любого другого раунда. Вы выбираете продолжать бросать или останавливаться. Тем не менее, вы знаете, что у вас нет шансов на победу, если вы не наберете больше очков, чем те, которые были до вас в последнем раунде. Но если вы продолжите идти слишком далеко, вы можете получить 6.
Однако есть еще одно правило, которое необходимо учитывать. Если ваш текущий общий балл (ваш предыдущий балл + ваш текущий балл за раунд) составляет 40 или более, и вы набрали 6, ваш общий балл устанавливается равным 0. Это означает, что вы должны начинать все сначала. Если вы набрали 6, когда ваш текущий общий счет 40 или больше, игра продолжится как обычно, за исключением того, что вы сейчас на последнем месте. Последний раунд не запускается, когда ваш общий счет сбрасывается. Вы все еще можете выиграть раунд, но он становится более сложным.
Победителем становится игрок с наибольшим количеством очков после окончания последнего раунда. Если два или более игроков имеют одинаковый счет, все они будут считаться победителями.
Дополнительное правило заключается в том, что игра продолжается максимум 200 раундов. Это сделано для предотвращения случаев, когда несколько ботов в основном продолжают бросать, пока не достигнут 6, чтобы остаться на своем текущем счете. После того, как 199-й раунд пройден, last_round
устанавливается значение true и воспроизводится еще один раунд. Если игра идет до 200 раундов, бот (или боты) с наибольшим количеством очков считается победителем, даже если у них нет 40 и более очков.
резюмировать
- Каждый раунд вы продолжаете бросать кубик, пока не решите остановиться или не получите 6
- Вы должны бросить кубик один раз (если ваш первый бросок 6, ваш раунд сразу заканчивается)
- Если вы получите 6, ваш текущий счет будет установлен на 0 (не ваш общий счет)
- Вы добавляете свой текущий счет к общему количеству очков после каждого раунда.
- Когда бот заканчивает свой ход, в результате чего общий счет не менее 40, все остальные получают последний ход
- Если ваш текущий общий балл и вы получаете 6, ваш общий балл будет равен 0 и ваш раунд окончен
- Последний раунд не запускается, когда происходит выше
- Человек с наибольшим общим счетом после последнего тура является победителем
- В случае, если есть несколько победителей, все будут считаться победителями
- Игра длится не более 200 раундов
Разъяснение результатов
- Общая оценка: оценка, которую вы сохранили в предыдущих раундах
- Текущий счет: счет за текущий раунд
- Текущий общий балл: сумма двух баллов выше
Как вы участвуете
Чтобы принять участие в этой задаче KotH, вы должны написать класс Python, который наследуется от Bot
. Вы должны реализовать функцию: make_throw(self, scores, last_round)
. Эта функция будет вызываться, как только наступит ваш ход, и ваш первый бросок не был 6. Чтобы продолжать бросать, вы должны это сделать yield True
. Чтобы прекратить бросать, вы должны yield False
. После каждого броска update_state
вызывается родительская функция . Таким образом, у вас есть доступ к вашим броскам за текущий раунд с использованием переменной self.current_throws
. У вас также есть доступ к своему индексу с помощью self.index
. Таким образом, чтобы увидеть свой общий счет, вы бы использовали scores[self.index]
. Вы также можете получить доступ end_score
к игре с помощью self.end_score
, но вы можете смело предположить, что для этого испытания будет 40.
Вам разрешено создавать вспомогательные функции внутри вашего класса. Вы также можете переопределить функции, существующие в Bot
родительском классе, например, если вы хотите добавить больше свойств класса. Вам не разрешается изменять состояние игры каким-либо образом, кроме сдачи True
или False
.
Вы можете черпать вдохновение из этого поста и скопировать любого из двух ботов, которые я здесь включил. Тем не менее, я боюсь, что они не особенно эффективны ...
На разрешении других языков
Как в песочнице, так и в «Девятнадцатом байте» мы обсуждали вопрос о разрешении подачи заявок на других языках. Прочитав о таких реализациях и выслушав аргументы обеих сторон, я решил ограничить этот вызов только Python. Это связано с двумя факторами: временем, необходимым для поддержки нескольких языков, и случайностью этой задачи, требующей большого количества итераций для достижения стабильности. Я надеюсь, что вы по-прежнему будете участвовать, и если вы хотите изучить Python для этой задачи, я постараюсь быть доступным в чате как можно чаще.
По любым вопросам, которые у вас могут возникнуть, вы можете написать в чате на эту проблему . Увидимся там!
правила
- Саботаж допускается и поощряется. То есть саботаж против других игроков
- Любая попытка возиться с контроллером, во время выполнения или другими представлениями будет дисквалифицирована. Все представления должны работать только с входами и хранилищами, которые им предоставляются.
- Любой бот, который использует более 500 МБ памяти для принятия решения, будет дисквалифицирован (если вам нужно столько памяти, вам следует пересмотреть свой выбор)
- Бот не должен реализовывать ту же стратегию, что и существующая, намеренно или случайно.
- Вы можете обновить своего бота во время испытания. Тем не менее, вы также можете разместить другого бота, если ваш подход отличается.
пример
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Этот бот будет продолжать работать до тех пор, пока у него не будет по крайней мере 10 очков за раунд или пока он не выбросит 6. Обратите внимание, что вам не нужна логика для обработки броска 6. Также обратите внимание, что если ваш первый бросок равен 6, make_throw
это никогда не звонил, так как ваш раунд сразу закончился.
Для тех, кто плохо знаком с Python (и новичок в yield
концепции), но хочет попробовать, yield
ключевое слово похоже на возврат в некоторых отношениях, но отличается в других. Вы можете прочитать о концепции здесь . По сути, как только вы yield
, ваша функция остановится, и значение, которое вы yield
отредактировали, будет отправлено обратно в контроллер. Там контроллер обрабатывает свою логику до тех пор, пока ваш бот не примет другое решение. Затем контроллер отправляет вам бросок игральных костей, и ваша make_throw
функция будет продолжать выполняться там, где она остановлена раньше, в основном на строке после предыдущего yield
оператора.
Таким образом, игровой контроллер может обновлять состояние, не требуя отдельного вызова функции бота для каждого броска костей.
Спецификация
Вы можете использовать любую библиотеку Python, доступную в pip
. Чтобы убедиться, что я смогу получить хорошее среднее значение, у вас есть ограничение в 100 миллисекунд на раунд. Я был бы очень рад, если бы ваш сценарий был намного быстрее, чтобы я мог запустить больше раундов.
оценка
Чтобы найти победителя, я беру всех ботов и запускаю их в случайных группах по 8. Если представлено менее 8 классов, я буду запускать их в случайных группах по 4, чтобы избежать того, чтобы в каждом раунде были все боты. Я буду проводить симуляции около 8 часов, и победителем станет бот с самым высоким процентом побед. Я начну финальные симуляции в начале 2019 года, и у вас будет все Рождество, чтобы кодировать своих ботов! Предварительная окончательная дата - 4 января, но если это слишком мало времени, я могу изменить ее на более позднюю дату.
До этого я постараюсь делать ежедневные симуляции, используя 30-60 минут процессорного времени, и обновлять табло. Это не будет официальным счетом, но он послужит руководством, чтобы увидеть, какие боты показывают лучшие результаты. Однако, с приближением Рождества, я надеюсь, вы понимаете, что я не буду доступен в любое время. Я сделаю все возможное, чтобы проводить симуляции и отвечать на любые вопросы, связанные с задачей.
Проверь это сам
Если вы хотите запустить собственное моделирование, вот полный код контроллера, выполняющего моделирование, включая два примера ботов.
контроллер
Вот обновленный контроллер для этой задачи. Он поддерживает выходы ANSI, многопоточность и собирает дополнительную статистику благодаря AKroell ! Когда я внесу изменения в контроллер, я обновлю сообщение, как только документация будет завершена.
Благодаря BMO , контроллер теперь может загружать всех ботов из этого поста, используя -d
флаг. Другие функции не изменились в этой версии. Это должно гарантировать, что все ваши последние изменения будут смоделированы как можно скорее!
#!/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)
Если вы хотите получить доступ к исходному контроллеру для этой задачи, он доступен в истории редактирования. Новый контроллер имеет ту же логику для запуска игры, единственное отличие - производительность, сбор статистики и более приятная печать.
Боты
На моей машине боты хранятся в файле forty_game_bots.py
. Если вы используете любое другое имя для файла, вы должны обновить import
оператор в верхней части контроллера.
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
Запуск симуляции
Чтобы запустить симуляцию, сохраните оба приведенных выше фрагмента кода в двух отдельных файлах. Я сохранил их как forty_game_controller.py
и forty_game_bots.py
. Затем вы просто используете python forty_game_controller.py
или в python3 forty_game_controller.py
зависимости от конфигурации Python. Следуйте инструкциям оттуда, если вы хотите настроить симуляцию дальше, или попробуйте поработать с кодом, если хотите.
Статистика игры
Если вы создаете бота, который стремится к определенному количеству очков, не принимая во внимание других ботов, это процентили победных очков:
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 44|
| 75.00| 48|
| 90.00| 51|
| 95.00| 54|
| 99.00| 58|
| 99.90| 67|
| 99.99| 126|
+----------+-----+
Лучшие результаты
По мере появления новых ответов я постараюсь обновлять этот список. Содержимое списка всегда будет из последней симуляции. Боты ThrowTwiceBot
и GoToTenBot
боты из кода выше, и используются в качестве ссылки. Я провел симуляцию с 10 ^ 8 играми, что заняло около 1 часа. Затем я увидел, что игра достигла стабильности по сравнению с моими пробежками с 10 ^ 7 играми. Однако, поскольку люди все еще публикуют ботов, я больше не буду делать симуляции, пока частота ответов не уменьшится.
Я пытаюсь добавить всех новых ботов и добавить любые изменения, которые вы внесли в существующие боты. Если мне кажется, что я пропустил вашего бота или какие-либо изменения, которые у вас есть, напишите в чате, и я позабочусь о том, чтобы в следующей симуляции была ваша самая последняя версия.
Теперь у нас есть больше статистики для каждого бота, благодаря AKroell ! Три новых столбца содержат максимальный балл по всем играм, средний балл за игру и средний балл при победе каждого бота.
Как отмечено в комментариях, была проблема с игровой логикой, из-за которой боты с более высоким индексом в игре получали дополнительный раунд в некоторых случаях. Это было исправлено сейчас, и оценки ниже отражают это.
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|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
Следующие боты (кроме Rebel
) сделаны, чтобы изменить правила, и создатели согласились не принимать участие в официальном турнире. Тем не менее, я все еще думаю, что их идеи являются творческими, и они заслуживают достойного упоминания. Мятежник также в этом списке, потому что он использует умную стратегию, чтобы избежать саботажа, и на самом деле лучше работает с саботирующим ботом в игре.
Боты NeoBot
и KwisatzHaderach
правда следуют правилам, но используют лазейки, предсказывая случайный генератор. Поскольку эти боты требуют много ресурсов для симуляции, я добавил их статистику из симуляции с меньшим количеством игр. Бот HarkonnenBot
добивается победы, отключая всех других ботов, что строго противоречит правилам.
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|
+----------+-----+
Ответы:
OptFor2X
Этот бот следует приближению к оптимальной стратегии для двухпользовательской версии этой игры, используя только свой счет и счет лучшего противника. В последнем туре обновленная версия учитывает все оценки.
источник
NeoBot
Вместо этого только попытайтесь осознать истину - ложки нет
NeoBot заглядывает в матрицу (она же случайная) и предсказывает, будет ли следующий бросок 6 или нет - он ничего не может поделать с получением 6 для начала, но более чем рад уклониться от конца серии.
NeoBot на самом деле не изменяет контроллер или среду выполнения, просто вежливо просит библиотеку для получения дополнительной информации.
источник
Кооперативный Рой
стратегия
Я не думаю, что кто-то еще заметил значение этого правила:
Если бы каждый бот всегда катался до тех пор, пока он не рухнул, у каждого был бы нулевой счет в конце раунда 200, и все выиграли бы! Таким образом, стратегия Cooperative Swarm состоит в том, чтобы сотрудничать до тех пор, пока все игроки имеют нулевой счет, но играть нормально, если кто-либо набрал какие-либо очки.
В этом посте я отправляю двух ботов: первый - CooperativeSwarmBot, а второй - CooperativeThrowTwice. CooperativeSwarmBot служит базовым классом для всех ботов, которые формально являются частью кооперативного роя, и имеет поведение заполнителя, просто принимая свой первый успешный бросок, когда сотрудничество не удается. CooperativeSwarmBot имеет CooperativeSwarmBot в качестве родителя и идентичен ему во всех отношениях, за исключением того, что его некооперативное поведение состоит в том, чтобы сделать два броска вместо одного. В следующие несколько дней я буду пересматривать этот пост, чтобы добавить новых ботов, которые используют гораздо более интеллектуальное поведение, играя против не-кооперативных ботов.
Код
Анализ
осуществимость
В этой игре очень сложно сотрудничать, потому что для ее работы нам нужна поддержка всех восьми игроков. Поскольку каждый класс ботов ограничен одним экземпляром на игру, это трудная цель для достижения. Например, вероятность выбора восьми кооперативных ботов из пула из 100 кооперативных ботов и 30 неработающих ботов:
Тематическое исследование
По ряду причин (см. Сноски 1 и 2) надлежащий кооперативный рой никогда не будет соревноваться в официальных играх. Поэтому я подведу итоги одного из моих собственных симуляций в этом разделе.
Этот симулятор запустил 10000 игр, используя 38 других ботов, которые были опубликованы здесь в прошлый раз, когда я проверял, и 2900 ботов, у которых CooperativeSwarmBot был их родительским классом. Контролер сообщил, что 9051 из 10000 игр (90,51%) закончились за 200 раундов, что довольно близко к прогнозу, что 90% игр будут кооперативными. Реализация этих ботов была тривиальной; кроме CooperativeSwarmBot они все приняли эту форму:
Менее 3% ботов имели процент выигрышей ниже 80%, и чуть более 11% ботов выигрывали каждую игру, в которую они играли. Средний процент выигрыша у 2900 ботов в рое составляет около 86%, что невероятно хорошо. Для сравнения, лучшие игроки в текущем официальном списке лидеров выигрывают менее чем в 22% своих игр. Я не могу уместить полный список кооперативного роя в пределах максимально допустимой длины ответа, поэтому, если вы хотите увидеть, что вам придется вместо этого перейти сюда: https://pastebin.com/3Zc8m1Ex
Так как каждый бот играл в среднем около 27 игр, удача играет относительно большой бросок, когда вы смотрите на результаты для отдельных ботов. Поскольку я еще не реализовал продвинутую стратегию для некооперативных игр, большинство других ботов сильно выиграли от игры против кооперативного роя, выполняя даже средний показатель выигрыша кооперативного роя в 86%.
Полные результаты для ботов, которых нет в рое, перечислены ниже; Есть два бота, результаты которых, я думаю, заслуживают особого внимания. Во-первых, StopBot не смог выиграть ни одной игры. Это особенно трагично, потому что кооперативный рой фактически использовал ту же стратегию, что и StopBot; Вы бы ожидали, что StopBot выиграет восемь своих игр случайно, и чуть больше, потому что кооперативный рой вынужден дать своим оппонентам первый ход. Второй интересный результат, однако, заключается в том, что кропотливая работа PointsAreForNerdsBot наконец-то окупилась: он сотрудничал с роем и сумел выиграть каждую игру, в которую играл!
Дефекты
У этого совместного подхода есть несколько недостатков. Во-первых, когда играешь против не-кооперативных ботов, кооперативные боты никогда не получают преимущества первого хода, потому что, когда они играют в первую очередь, они еще не знают, готовы ли их противники сотрудничать, и поэтому у них нет выбора, кроме как получить счет ноль. Точно так же эта кооперативная стратегия чрезвычайно уязвима для использования злонамеренными ботами; например, во время совместной игры бот, который играет последним в последнем раунде, может немедленно прекратить бросок, чтобы остальные проиграли (при условии, конечно, что его первый бросок не был шестеркой).
Сотрудничая, все боты могут достичь оптимального решения при 100% выигрыше. Таким образом, если бы выигрыш был единственным, что имело значение, тогда сотрудничество было бы устойчивым равновесием, и не о чем беспокоиться. Тем не менее, некоторые боты могут расставлять приоритеты перед другими целями, такими как достижение вершины списка лидеров. Это означает, что существует риск того, что другой бот может выйти из строя после вашего последнего хода, что создает стимул для вас в первую очередь. Поскольку установка этого соревнования не позволяет нам видеть то, что делали наши оппоненты в их предыдущих играх, мы не можем наказать тех, кто бежал. Таким образом, сотрудничество в конечном итоге является неустойчивым равновесием, обреченным на провал.
Сноски
[1]: Главные причины, по которым я не хочу отправлять тысячи ботов вместо двух, заключаются в том, что это замедлит симуляцию в 1000 раз [2], и это может существенно испортить ситуацию. процент выигрышей, так как другие боты будут играть исключительно против роя, а не друг против друга. Более важным, однако, является тот факт, что даже если бы я захотел, я не смог бы создать столько ботов в разумные сроки, не нарушая дух правила: «Бот не должен реализовывать ту же стратегию, что и существующий, намеренно или случайно ".
[2]: Я думаю, что есть две основные причины, по которым симуляция замедляется при запуске кооперативного роя. Во-первых, чем больше ботов, тем больше игр, если вы хотите, чтобы каждый бот играл в одинаковом количестве игр (в данном примере количество игр будет отличаться примерно в 77 раз). Во-вторых, совместные игры занимают больше времени, потому что они длятся целых 200 раундов, и в течение раунда игроки должны продолжать катиться бесконечно. Для моей установки игры имитировали примерно в 40 раз больше: для запуска 10000 игр на практическом примере потребовалось чуть более трех минут, но после удаления кооперативного роя 10000 игр были бы завершены всего за 4,5 секунды. Я полагаю, что между этими двумя причинами потребуется приблизительно 3100 раз больше времени, чтобы точно измерить производительность ботов, когда конкурирует рой по сравнению с тем, когда их нет.
источник
GoTo20Bot
Просто попробуйте все
GoToNBot
, А 20, 22, 24 играет лучше всего. Я не знаю почему.Обновление: всегда останавливайте бросок, если получите счет 40 или больше.
источник
end_score
4000 (и изменил своего бота, чтобы использовать это вtarget
расчете), 15-16 ботов были намного лучше. Но если бы игра была только для увеличения вашего счета, это было бы тривиально.end_score
4000, то почти 200 нельзя получить до 200 ходов. А игра просто в том, кто набрал наибольшее количество очков за 200 ходов. И остановка на 15 должна сработать, так как на этот раз стратегия наивысшего результата за один ход такая же, как наивысшая оценка за 200 ходов.Адаптивный ролик
Начинает более агрессивно и успокаивается к концу раунда.
Если он считает, что это победа, бросьте дополнительное время для безопасности.
источник
lim = max(min(self.end_score - scores[self.index], 24), 6)
Увеличение максимального значения до 24 и добавление минимального значения 6 увеличивает процент выигрыша сам по себе и, тем более, в совокупности.Альфа
Альфа отказывается когда-либо уступать кому-либо. Пока есть бот с более высоким счетом, он будет продолжать катиться.
источник
yield
, как работает, если он начинает катиться, он никогда не остановится. Вы хотите обновитьmy_score
в цикле.NotTooFarBehindBot
Идея состоит в том, что другие боты могут потерять очки, поэтому быть вторым неплохо - но если вы сильно отстали, вы могли бы пойти ва-банк.
источник
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
. Несмотря на то, что ваш бот лидирует после 7 бросков, он продолжается до тех пор, пока не достигнет 6 баллов. Когда я набирал это, я понял проблему!scores
Содержат только общее количество баллов, а не штампованные случаев для текущего раунда. Вы должны изменить это, чтобы бытьcurrent_score = scores[self.index] + sum(self.current_throws)
.GoHomeBot
Мы хотим стать большими или пойти домой, верно? GoHomeBot в основном просто идет домой. (Но на удивление хорошо!)
источник
scores
списке. Раньше был такой бот (бот GoToEnd), но Дэвид удалил свой ответ. Я заменю этого бота вашим.EnsureLead
EnsureLead заимствует идеи из GoTo20Bot. Он добавляет концепцию, которая всегда учитывает (когда в last_round или достижении 40), что есть другие, которые будут иметь по крайней мере еще один бросок. Таким образом, бот пытается опередить их, так что им нужно наверстать упущенное.
источник
Roll6TimesV2
Не побеждает текущий лучше, но я думаю, что будет лучше, если в игре будет больше ботов.
Кстати, классная игра.
источник
StopBot
Буквально только один бросок.
Это эквивалентно базовому
Bot
классу.источник
BringMyOwn_dice (BMO_d)
Этот бот любит кости, он приносит 2 (кажется, лучше всего) свои кости. Прежде чем бросать кости в раунде, он бросает свои 2 кости и вычисляет их сумму, это количество бросков, которые он собирается выполнить, он бросает только если у него еще нет 40 очков.
источник
FooBot
источник
# Must throw at least once
не нужен - он бросает один раз перед вызовом вашего бота. Ваш бот всегда будет бросать минимум два раза.make_throw
метод рано, когда хотел, чтобы игроки могли пропустить свой ход. Я думаю, что более подходящее имя будетkeep_throwing
. Спасибо за отзыв в песочнице, это действительно помогло сделать это правильным испытанием!Идти пораньше
Концепция: Постарайтесь выиграть крупно на раннем броске (до 25), затем выползайте оттуда по 2 броска за раз.
источник
BinaryBot
Пытается приблизиться к окончательному счету, чтобы, как только кто-то еще запустил последний раунд, он мог побить свой счет за победу. Цель всегда находится на полпути между текущей оценкой и конечной оценкой.
источник
Hesitate
также отказывается пересекать черту первым. Вы должны окружить свою функциюclass
материалом.PointsAreForNerdsBot
Это не нуждается в объяснении.
OneInFiveBot
Продолжает катиться, пока не выпадет пятерка на своем собственном 5-ти стороннем кубике. Пять меньше шести, так что ВЫ ДОЛЖНЫ ВЫИГРАТЬ!
источник
OneInFiveBot
Это отличная идея, но я думаю , что она страдает в конце игры по сравнению с некоторыми из более продвинутых ботов. Все еще отличная подача!OneInFiveBot
довольно интересно в том , что он постоянно имеет самый высокий общий балл достиг.StopBot
боксерскую грушу: P. OneInFiveBot на самом деле довольно аккуратный, хорошая работа!OneInFiveBot
и у меня всеLizduadacBot
Пытается выиграть в 1 шаг. Конечное состояние несколько произвольно.
Это также мой первый пост (и я новичок в Python), поэтому, если я побью "PointsAreForNerdsBot", я буду счастлив!
источник
PointsAreForNerdsBot
, но ваш бот на самом деле неплохо держится. Я обновлю счет позже вечером или завтра, но ваш выигрыш составляет около 15%, что выше, чем в среднем на 12,5%.SlowStart
Этот бот реализует алгоритм медленного старта TCP. Он корректирует количество бросков ( ни ) в соответствии с предыдущим ходом: если он не бросил 6 в предыдущем ходу, увеличивает ни для этого хода; в то время как это уменьшает, или если это сделало.
источник
def updateValues():
должны бытьdef updateValues(self):
(илиdef update_values(self):
если вы хотите следовать PEP8). Во-вторых, вызовupdateValues()
должен быть вместоself.updateValues()
(илиself.update_vales()
).i
переменную в цикле while. Прямо сейчас ваш бот либо полностью проходит цикл while, либо застревает в цикле while, пока не достигнет 6.self.nor
и посмотреть, как оно влияет на производительность вашего бота.KwisatzHaderach
Еще в первые дни этого испытания (то есть до того, как
NeoBot
был опубликован) я написал почти тривиальныйOracle
бот:но не опубликовал его, поскольку я не думал, что это было достаточно интересно;) Но как только я
NeoBot
вышел в лидеры, я начал думать о том, как превзойти его совершенную способность предсказывать будущее. Итак, вот цитата из Дюны; это когда Пол Атрейдес, Квадзац Хадерах, стоит на стыке, из которого может развернуться бесконечность различных вариантов будущего:Итак, вот ответ: предвидеть будущее - значит менять его; и если вы очень осторожны, то с помощью избирательных действий или бездействия вы можете изменить это выгодным способом - по крайней мере, большую часть времени. Даже
KwisatzHaderach
не может получить 100% выигрыш!источник
NeoBot
но и лучше! Мне также нравится, как вы приводите пример того, что все, что использует случайность (особенно контроллер), здесь должны делать: использовать свой собственныйrandom.Random
экземпляр. МолNeoBot
, это кажется немного чувствительным к изменениям неуказанных деталей реализации контроллера.HarkonnenBot
не касается RNG; его вообще не волнуют случайные числа. Он просто отравляет всех остальных ботов, а затем медленно доходит до финиша. Как и многие кулинарные деликатесы, месть - это блюдо, которое лучше всего наслаждаться медленно, после долгой и деликатной подготовки.NeoBot
(иHarkonnenBot
),KwisatzHaderach
опирается только на одну деталь реализации; в частности, ему не нужно знать, как реализован random.random (), только то, что его использует контроллер; DKwisatzHaderach
и такHarkonnenBot
же, какNeoBot
. Они получат свои результаты в симуляции с меньшим количеством игр, и не будут в официальной симуляции. Тем не менее, они окажутся в списке рекордов так же, какNeoBot
. Основная причина, по которой они не участвуют в официальной симуляции, заключается в том, что они испортят другие стратегии ботов. Тем не мение.WisdomOfCrowds
должен хорошо подходить для участия, и мне любопытно, какие новые изменения вы внесли в это!Ну, это очевидно
источник
LastRound действует так, как будто это всегда последний раунд и последний бот: он продолжает катиться до тех пор, пока не выйдет в лидеры. Он также не хочет соглашаться на менее чем 15 очков, если он не является последним раундом или не достигает 40 очков.
источник
QuotaBot
Я реализовал наивную систему «квот», которая в целом казалась довольно высокой.
источник
if own_score mean + 5:
выдает ошибку для меня. Такжеwhile sum(self.current_throws)
<
и>
символы, которые мешали<pre>
тегам, которые я использовалExpectationsBot
Просто проигрывает, вычисляет ожидаемое значение для броска костей и делает его только в том случае, если оно положительное.
У меня были проблемы с запуском контроллера, я получил «NameError: имя« bots_per_game »не определено» на многопоточном, так что на самом деле не знаю, как это работает.
источник
BlessRNG
BlessRNG FrankerZ GabeN BlessRNG
источник
FortyTeen
Попробуйте набрать 14 очков до последнего раунда, затем предположите, что все остальные попытаются набрать 14 очков и попытаться связать этот счет.
источник
TypeError: unsupported operand type(s) for -: 'list' and 'int'
с твоим ботом.max_projected_score
должны быть максимумом списка, а не всего списка, я прав? В противном случае я получаю ту же проблему, что и Tsh.Колебаться
Делает два скромных шага, затем ждет, пока кто-нибудь еще пересечет черту. Обновленная версия больше не пытается побить рекорд, а только хочет достичь ее - улучшая производительность, удаляя два байта исходного кода!
источник
Бунтарь
Этот бот сочетает в себе простую стратегию
Hesitate
с продвинутой стратегией последнего раундаBotFor2X
, пытается вспомнить, кто он, и сходит с ума, когда обнаруживает, что живет в иллюзии.источник
HarkonnenBot
так, чтоRebel
больше не могу отменить самоочищение;), и я также настроилTleilaxuBot
так, чтобы онRebel
больше не обнаруживал!Дай пять
В половине случаев мы бросим 5 до 6. Когда мы сделаем, обналичить.
источник
преследователь
Chaser пытается догнать первую позицию. Если это последний раунд, он отчаянно пытается набрать, по крайней мере, 50 очков. Просто для хорошей меры он бросает, по крайней мере, четыре раза, несмотря ни на что
[править 1: добавлена стратегия получения золота в последнем раунде]
[править 2: обновленная логика, потому что я ошибочно думал, что бот получит 40 баллов, а не только самый высокий рейтинг ботов]
[править 3: в конце игры чейзер стал немного более оборонительным]
источник
FutureBot
OneStepAheadBot
Пара ботов, они приносят свои собственные наборы игральных костей и бросают их, чтобы предсказать будущее. Если один из них 6, они останавливаются, FutureBot не может вспомнить, какой из двух кубиков был для следующего броска, поэтому он сдается.
Интересно, что будет лучше.
OneStepAhead немного похож на OneInFive на мой вкус, но я также хочу увидеть, как он сравнивается с FutureBot и OneInFive.
Изменить: теперь они останавливаются после удара 45
источник