Turnamen berakhir!
Turnamen sudah berakhir! Simulasi terakhir dijalankan pada malam hari, total game. Pemenangnya adalah Christian Sievers dengan botnya OptFor2X . Christian Sievers juga berhasil mengamankan tempat kedua dengan Rebel . Selamat! Di bawah ini Anda dapat melihat daftar skor tinggi resmi untuk turnamen.
Jika Anda masih ingin bermain gim, Anda dapat menggunakan pengontrol yang diposting di bawah ini, dan menggunakan kode di dalamnya untuk membuat gim Anda sendiri.
Saya diundang untuk memainkan permainan dadu yang belum pernah saya dengar. Aturannya sederhana, namun saya pikir itu akan sempurna untuk tantangan KotH.
Aturan
Awal permainan
Mati berputar di sekitar meja, dan setiap kali giliran Anda, Anda bisa melempar mati sebanyak yang Anda inginkan. Namun, Anda harus membuangnya setidaknya satu kali. Anda melacak jumlah semua lemparan untuk putaran Anda. Jika Anda memilih untuk berhenti, skor untuk ronde tersebut ditambahkan ke total skor Anda.
Jadi mengapa Anda berhenti membuang dadu? Karena jika Anda mendapatkan 6, skor Anda untuk seluruh putaran menjadi nol, dan mati diteruskan. Dengan demikian, tujuan awal adalah meningkatkan skor Anda secepat mungkin.
Siapa pemenangnya?
Ketika pemain pertama di sekitar meja mencapai 40 poin atau lebih, putaran terakhir dimulai. Setelah putaran terakhir dimulai, semua orang kecuali orang yang memulai putaran terakhir mendapat satu putaran lagi.
Aturan untuk babak terakhir sama dengan untuk putaran lainnya. Anda memilih untuk terus melempar atau berhenti. Namun, Anda tahu bahwa Anda tidak memiliki peluang untuk menang jika Anda tidak mendapatkan skor lebih tinggi daripada skor sebelum Anda di babak terakhir. Tetapi jika Anda terus melangkah terlalu jauh, maka Anda mungkin mendapat nilai 6.
Namun, ada satu aturan lagi yang perlu dipertimbangkan. Jika skor total Anda saat ini (skor Anda sebelumnya + skor Anda saat ini untuk putaran) adalah 40 atau lebih, dan Anda menekan 6, skor total Anda ditetapkan ke 0. Itu berarti Anda harus memulai dari awal. Jika Anda menekan angka 6 saat skor total Anda saat ini adalah 40 atau lebih, permainan berlanjut seperti biasa, kecuali bahwa Anda sekarang berada di tempat terakhir. Babak terakhir tidak terpicu ketika skor total Anda diatur ulang. Anda masih bisa memenangkan ronde, tetapi itu menjadi lebih menantang.
Pemenangnya adalah pemain dengan skor tertinggi setelah babak terakhir berakhir. Jika dua atau lebih pemain berbagi skor yang sama, mereka semua akan dihitung sebagai pemenang.
Aturan tambahan adalah bahwa permainan berlanjut hingga maksimum 200 putaran. Ini untuk mencegah kasus di mana banyak bot pada dasarnya terus melempar sampai mereka mencapai 6 untuk tetap pada skor mereka saat ini. Setelah putaran ke-199 berlalu, last_round
disetel ke true, dan satu putaran lagi dimainkan. Jika permainan berlangsung hingga 200 putaran, bot (atau bot) dengan skor tertinggi adalah pemenangnya, bahkan jika mereka tidak memiliki 40 poin atau lebih.
Rekap
- Setiap putaran Anda terus melempar dadu sampai Anda memilih untuk berhenti atau Anda mendapatkan 6
- Anda harus melempar dadu sekali (jika lemparan pertama Anda adalah 6, putaran Anda segera berakhir)
- Jika Anda mendapatkan 6, skor Anda saat ini ditetapkan ke 0 (bukan skor total Anda)
- Anda menambahkan skor Anda saat ini ke skor total Anda setelah setiap putaran
- Ketika bot mengakhiri giliran mereka yang menghasilkan skor total setidaknya 40, semua orang mendapat giliran terakhir
- Jika skor total Anda saat ini adalah dan Anda mendapatkan 6, skor total Anda ditetapkan ke 0 dan putaran Anda berakhir
- Babak terakhir tidak terpicu ketika hal di atas terjadi
- Orang dengan skor total tertinggi setelah babak terakhir adalah pemenang
- Jika ada beberapa pemenang, semua akan dihitung sebagai pemenang
- Permainan ini berlangsung selama maksimal 200 putaran
Klarifikasi skor
- Skor total: skor yang telah Anda simpan dari babak sebelumnya
- Skor saat ini: skor untuk putaran saat ini
- Skor total saat ini: jumlah dari dua skor di atas
Bagaimana Anda berpartisipasi?
Untuk berpartisipasi dalam tantangan KotH ini, Anda harus menulis kelas Python yang diturunkan dari Bot
. Anda harus menerapkan fungsi: make_throw(self, scores, last_round)
. Fungsi itu akan dipanggil begitu giliran Anda, dan lemparan pertama Anda bukan 6. Untuk terus melempar, Anda harus yield True
. Untuk berhenti melempar, Anda harus yield False
. Setelah setiap lemparan, fungsi induk update_state
disebut. Dengan demikian, Anda memiliki akses ke lemparan Anda untuk putaran saat ini menggunakan variabel self.current_throws
. Anda juga memiliki akses ke indeks Anda sendiri menggunakan self.index
. Dengan demikian, untuk melihat skor total Anda sendiri, Anda akan menggunakan scores[self.index]
. Anda juga dapat mengakses end_score
permainan untuk menggunakan self.end_score
, tetapi Anda dapat dengan aman menganggap bahwa itu akan menjadi 40 untuk tantangan ini.
Anda diizinkan membuat fungsi pembantu di dalam kelas Anda. Anda juga dapat mengesampingkan fungsi yang ada di Bot
kelas induk, misalnya jika Anda ingin menambahkan lebih banyak properti kelas. Anda tidak diizinkan untuk mengubah keadaan permainan dengan cara apa pun selain menghasilkan True
atau False
.
Anda bebas mencari inspirasi dari pos ini, dan menyalin salah satu dari dua bot yang saya sertakan di sini. Namun, saya khawatir mereka tidak terlalu efektif ...
Pada memungkinkan bahasa lain
Baik di kotak pasir dan pada The Nineteenth Byte, kami telah berdiskusi tentang mengizinkan pengiriman dalam bahasa lain. Setelah membaca tentang implementasi seperti itu, dan mendengarkan argumen dari kedua belah pihak, saya telah memutuskan untuk membatasi tantangan ini hanya untuk Python. Ini disebabkan oleh dua faktor: waktu yang diperlukan untuk mendukung berbagai bahasa, dan keacakan tantangan ini membutuhkan jumlah iterasi yang tinggi untuk mencapai stabilitas. Saya harap Anda akan tetap berpartisipasi, dan jika Anda ingin mempelajari beberapa Python untuk tantangan ini, saya akan mencoba untuk selalu tersedia dalam obrolan sesering mungkin.
Untuk setiap pertanyaan yang mungkin Anda miliki, Anda dapat menulis di ruang obrolan untuk tantangan ini . Sampai jumpa!
Aturan
- Sabotase diperbolehkan, dan dianjurkan. Artinya, sabotase terhadap pemain lain
- Setiap upaya untuk mengutak-atik controller, run-time atau pengiriman lainnya akan didiskualifikasi. Semua pengiriman hanya dapat bekerja dengan input dan penyimpanan yang diberikan.
- Bot apa pun yang menggunakan memori lebih dari 500MB untuk mengambil keputusan akan didiskualifikasi (jika Anda membutuhkan banyak memori, Anda harus memikirkan kembali pilihan Anda)
- Bot tidak boleh menerapkan strategi yang sama persis dengan yang sudah ada, sengaja atau tidak sengaja.
- Anda diizinkan untuk memperbarui bot Anda saat tantangan berlangsung. Namun, Anda juga bisa memposting bot lain jika pendekatan Anda berbeda.
Contoh
class GoToTenBot(Bot):
def make_throw(self, scores, last_round):
while sum(self.current_throws) < 10:
yield True
yield False
Bot ini akan terus berjalan sampai memiliki skor setidaknya 10 untuk ronde, atau melempar 6. Perhatikan bahwa Anda tidak perlu logika untuk menangani melempar 6. Juga perhatikan bahwa jika lemparan pertama Anda adalah 6, make_throw
adalah tidak pernah menelepon, karena putaran Anda segera berakhir.
Bagi mereka yang baru mengenal Python (dan baru dalam yield
konsep), tetapi ingin mencoba, yield
kata kunci ini mirip dengan pengembalian dalam beberapa hal, tetapi berbeda dalam hal lain. Anda dapat membaca tentang konsepnya di sini . Pada dasarnya, begitu Anda yield
, fungsi Anda akan berhenti, dan nilai yang Anda yield
edit akan dikirim kembali ke controller. Di sana, pengontrol menangani logikanya sampai tiba saatnya bot Anda membuat keputusan lain. Kemudian controller mengirimi Anda lemparan dadu, dan make_throw
fungsi Anda akan terus mengeksekusi tepat di mana jika berhenti sebelumnya, pada dasarnya di telepon setelah yield
pernyataan sebelumnya .
Dengan cara ini, game controller dapat memperbarui status tanpa memerlukan panggilan fungsi bot terpisah untuk setiap lemparan dadu.
Spesifikasi
Anda dapat menggunakan pustaka Python yang tersedia di pip
. Untuk memastikan bahwa saya bisa mendapatkan rata-rata yang baik, Anda memiliki batas waktu 100 milidetik per putaran. Saya akan sangat senang jika skrip Anda jauh lebih cepat dari itu, sehingga saya dapat menjalankan lebih banyak putaran.
Evaluasi
Untuk menemukan pemenang, saya akan mengambil semua bot dan menjalankannya dalam kelompok acak 8. Jika ada kurang dari 8 kelas yang dikirimkan, saya akan menjalankannya dalam kelompok acak 4 untuk menghindari selalu memiliki semua bot di setiap babak. Saya akan menjalankan simulasi selama sekitar 8 jam, dan pemenangnya adalah bot dengan persentase kemenangan tertinggi. Saya akan menjalankan mulai simulasi terakhir pada awal 2019, memberi Anda semua Natal untuk kode bot Anda! Tanggal akhir pendahuluan adalah 4 Januari, tetapi jika itu terlalu sedikit waktu saya dapat mengubahnya ke tanggal kemudian.
Sampai saat itu, saya akan mencoba membuat simulasi harian menggunakan waktu CPU 30-60 menit, dan memperbarui papan skor. Ini tidak akan menjadi skor resmi, tetapi akan berfungsi sebagai panduan untuk melihat bot mana yang melakukan yang terbaik. Namun, dengan Natal yang akan datang, saya harap Anda bisa mengerti bahwa saya tidak akan tersedia setiap saat. Saya akan melakukan yang terbaik untuk menjalankan simulasi dan menjawab pertanyaan yang terkait dengan tantangan.
Uji sendiri
Jika Anda ingin menjalankan simulasi Anda sendiri, inilah kode lengkap untuk pengontrol yang menjalankan simulasi, termasuk dua bot contoh.
Pengendali
Inilah pengendali yang diperbarui untuk tantangan ini. Ini mendukung keluaran ANSI, multi-threading, dan mengumpulkan statistik tambahan berkat AKroell ! Ketika saya membuat perubahan pada controller, saya akan memperbarui posting setelah dokumentasi selesai.
Berkat BMO , controller sekarang dapat mengunduh semua bot dari pos ini menggunakan -d
flag. Fungsionalitas lain tidak berubah dalam versi ini. Ini harus memastikan bahwa semua perubahan terbaru Anda disimulasikan sesegera mungkin!
#!/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)
Jika Anda ingin akses ke pengontrol asli untuk tantangan ini, ini tersedia di riwayat edit. Kontroler baru memiliki logika yang sama persis untuk menjalankan game, satu-satunya perbedaan adalah kinerja, pengumpulan stat dan pencetakan lebih cantik.
Bot
Di mesin saya, bot disimpan dalam file forty_game_bots.py
. Jika Anda menggunakan nama lain untuk file, Anda harus memperbarui import
pernyataan di bagian atas controller.
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
Menjalankan simulasi
Untuk menjalankan simulasi, simpan kedua cuplikan kode yang diposting di atas ke dua file terpisah. Saya telah menyimpannya sebagai forty_game_controller.py
dan forty_game_bots.py
. Kemudian Anda cukup menggunakan python forty_game_controller.py
atau python3 forty_game_controller.py
bergantung pada konfigurasi Python Anda. Ikuti instruksi dari sana jika Anda ingin mengkonfigurasi simulasi Anda lebih lanjut, atau coba bermain-main dengan kode jika Anda mau.
Statistik game
Jika Anda membuat bot yang bertujuan untuk skor tertentu tanpa mempertimbangkan bot lain, ini adalah persentil skor kemenangan:
+----------+-----+
|Percentile|Score|
+----------+-----+
| 50.00| 44|
| 75.00| 48|
| 90.00| 51|
| 95.00| 54|
| 99.00| 58|
| 99.90| 67|
| 99.99| 126|
+----------+-----+
Nilai yang tinggi
Ketika lebih banyak jawaban diposting, saya akan mencoba untuk menjaga daftar ini diperbarui. Isi daftar akan selalu dari simulasi terbaru. Bot ThrowTwiceBot
dan GoToTenBot
bot dari kode di atas, dan digunakan sebagai referensi. Saya melakukan simulasi dengan 10 ^ 8 game, yang memakan waktu sekitar 1 jam. Kemudian saya melihat bahwa permainan mencapai stabilitas dibandingkan dengan permainan saya dengan 10 ^ 7 pertandingan. Namun, dengan orang-orang masih memposting bot, saya tidak akan melakukan simulasi lagi sampai frekuensi tanggapan turun.
Saya mencoba menambahkan semua bot baru dan menambahkan perubahan yang telah Anda buat ke bot yang ada. Jika sepertinya saya telah melewatkan bot Anda atau perubahan baru yang Anda miliki, tulis di obrolan dan saya akan pastikan untuk memiliki versi terbaru Anda dalam simulasi berikutnya.
Kami sekarang memiliki lebih banyak statistik untuk setiap bot berkat AKroell ! Tiga kolom baru berisi skor maksimum di semua game, skor rata-rata per game, dan skor rata-rata saat menang untuk setiap bot.
Seperti yang ditunjukkan dalam komentar, ada masalah dengan logika permainan yang membuat bot yang memiliki indeks lebih tinggi dalam permainan mendapatkan putaran tambahan dalam beberapa kasus. Ini telah diperbaiki sekarang, dan skor di bawah mencerminkan ini.
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|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
Bot berikut (kecuali Rebel
) dibuat untuk membengkokkan aturan, dan pencipta telah setuju untuk tidak mengambil bagian dalam turnamen resmi. Namun, saya masih berpikir ide-ide mereka kreatif, dan mereka layak disebut terhormat. Pemberontak juga ada di daftar ini karena menggunakan strategi yang cerdas untuk menghindari sabotase, dan benar-benar berkinerja lebih baik dengan bot sabotase yang sedang bermain.
Bot NeoBot
dan KwisatzHaderach
memang mengikuti aturan, tetapi menggunakan celah dengan memprediksi generator acak. Karena bot ini membutuhkan banyak sumber daya untuk disimulasikan, saya telah menambahkan statistiknya dari simulasi dengan lebih sedikit game. Bot HarkonnenBot
mencapai kemenangan dengan melumpuhkan semua bot lainnya, yang sangat bertentangan dengan aturan.
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|
+----------+-----+
Jawaban:
OptFor2X
Bot ini mengikuti perkiraan strategi optimal untuk versi dua pemain game ini, hanya menggunakan skor dan skor lawan terbaiknya. Di babak terakhir, versi yang diperbarui mempertimbangkan semua skor.
sumber
NeoBot
Sebagai gantinya, hanya berusaha untuk menyadari kebenaran - tidak ada sendok
NeoBot mengintip ke dalam matriks (alias acak) dan memprediksi apakah gulungan berikutnya akan menjadi 6 atau tidak - ia tidak bisa berbuat apa-apa untuk menyerahkan 6 untuk memulai, tetapi lebih dari senang untuk menghindari streak ender.
NeoBot sebenarnya tidak memodifikasi controller atau runtime, cukup sopan meminta perpustakaan untuk informasi lebih lanjut.
sumber
Kerumunan Koperasi
Strategi
Saya tidak berpikir ada orang lain yang menyadari pentingnya aturan ini:
Jika setiap bot selalu bergulir sampai rusak, maka semua orang akan mendapat skor nol di akhir babak 200 dan semua orang akan menang! Jadi, strategi Cooperative Swarm adalah untuk bekerja sama selama semua pemain memiliki skor nol, tetapi untuk bermain secara normal jika ada yang mencetak poin.
Dalam posting ini, saya mengirimkan dua bot: yang pertama adalah CooperativeSwarmBot, dan yang kedua adalah CooperativeThrowTwice. CooperativeSwarmBot berfungsi sebagai kelas dasar untuk semua bot yang secara resmi merupakan bagian dari gerombolan koperasi, dan memiliki perilaku placeholder dengan hanya menerima roll pertama yang berhasil ketika kerja sama gagal. CooperativeSwarmBot memiliki CooperativeSwarmBot sebagai induknya dan identik dengan itu dalam segala hal kecuali perilaku non-kooperasinya adalah membuat dua gulungan, bukan satu. Dalam beberapa hari ke depan saya akan merevisi posting ini untuk menambahkan bot baru yang menggunakan perilaku yang jauh lebih cerdas melawan bot non-kooperatif.
Kode
Analisis
Kelangsungan hidup
Sangat sulit untuk bekerja sama dalam permainan ini karena kami membutuhkan dukungan dari semua delapan pemain agar bisa bekerja. Karena setiap kelas bot terbatas pada satu instance per game, ini adalah tujuan yang sulit untuk dicapai. Misalnya, peluang untuk memilih delapan bot kooperatif dari kumpulan 100 bot kooperatif dan 30 bot non-kooperatif adalah:
Lebih umum, kemungkinan memilih bot koperasi dari kolam bot koperasi dan bot nonkooperatif adalah:saya c n
Dari persamaan ini, kita dapat dengan mudah menunjukkan bahwa kita akan membutuhkan sekitar 430 bot kooperatif agar 50% game berakhir secara kooperatif, atau sekitar 2900 bot untuk 90% (menggunakan sesuai aturan, dan ).i = 8 n = 38
Studi kasus
Karena sejumlah alasan (lihat catatan kaki 1 dan 2), segerombolan koperasi yang tepat tidak akan pernah bersaing dalam pertandingan resmi. Dengan demikian, saya akan merangkum hasil dari salah satu simulasi saya sendiri di bagian ini.
Simulasi ini menjalankan 10.000 permainan menggunakan 38 bot lain yang telah diposting di sini terakhir kali saya memeriksa dan 2900 bot yang memiliki CooperativeSwarmBot sebagai kelas induknya. Pengendali melaporkan bahwa 9051 dari 10.000 game (90,51%) berakhir pada 200 putaran, yang cukup dekat dengan prediksi bahwa 90% game akan kooperatif. Implementasi bot ini tidak penting; selain CooperativeSwarmBot mereka semua mengambil formulir ini:
Kurang dari 3% bot memiliki persentase kemenangan di bawah 80%, dan lebih dari 11% bot memenangkan setiap pertandingan yang mereka mainkan. Persentase kemenangan rata-rata dari 2.900 bot di gerombolan adalah sekitar 86%, yang sangat baik. Sebagai perbandingan, pemain papan atas di papan peringkat resmi saat ini menang kurang dari 22% dari gim mereka. Saya tidak dapat memasukkan daftar lengkap dari gerombolan koperasi dalam panjang maksimum yang diizinkan untuk jawaban, jadi jika Anda ingin melihat bahwa Anda harus pergi ke sini sebagai gantinya: https://pastebin.com/3Zc8m1Ex
Karena setiap bot bermain dalam rata-rata sekitar 27 pertandingan, keberuntungan memainkan permainan yang relatif besar ketika Anda melihat hasil untuk masing-masing bot. Karena saya belum menerapkan strategi lanjutan untuk permainan non-kooperatif, sebagian besar bot lainnya diuntungkan secara drastis dari bermain melawan gerombolan koperasi, bahkan melakukan rata-rata tingkat kemenangan rata-rata koperasi swarm sebesar 86%.
Hasil lengkap untuk bot yang tidak ada di swarm tercantum di bawah ini; ada dua bot yang hasilnya menurut saya pantas mendapat perhatian khusus. Pertama, StopBot gagal memenangkan permainan apa pun. Ini sangat tragis karena gerombolan koperasi sebenarnya menggunakan strategi yang sama persis dengan StopBot; Anda akan mengharapkan StopBot memenangkan delapan pertandingan secara kebetulan, dan sedikit lagi karena gerombolan koperasi dipaksa untuk memberi lawannya langkah pertama. Hasil menarik kedua, bagaimanapun, adalah bahwa kerja keras PointsAreForNerdsBot akhirnya terbayar: itu bekerja sama dengan segerombolan dan berhasil memenangkan setiap pertandingan yang dimainkannya!
Kekurangan
Ada beberapa kelemahan dari pendekatan kooperatif ini. Pertama, ketika bermain melawan bot non-kooperatif, koperasi tidak pernah mendapatkan keuntungan giliran pertama karena ketika mereka bermain pertama, mereka belum tahu apakah lawan mereka mau bekerja sama, dan dengan demikian tidak punya pilihan selain mendapatkan skor nol. Demikian pula, strategi kerja sama ini sangat rentan terhadap eksploitasi oleh bot jahat; misalnya, selama bermain kooperatif bot yang bermain terakhir di babak terakhir dapat memilih untuk segera berhenti bergulir untuk membuat semua orang kehilangan (dengan asumsi, tentu saja, bahwa gulungan pertama mereka bukan enam).
Dengan bekerja sama, semua bot dapat mencapai solusi optimal dengan tingkat kemenangan 100%. Dengan demikian, jika tingkat kemenangan adalah satu-satunya hal yang penting maka kerja sama akan menjadi keseimbangan yang stabil dan tidak akan ada yang perlu dikhawatirkan. Namun, beberapa bot mungkin memprioritaskan tujuan lain, seperti mencapai puncak papan peringkat. Ini berarti bahwa ada risiko bot lain mungkin cacat setelah giliran terakhir Anda, yang menciptakan insentif bagi Anda untuk membelot terlebih dahulu. Karena pengaturan kompetisi ini tidak memungkinkan kami untuk melihat apa yang dilakukan lawan kami di pertandingan sebelumnya, kami tidak dapat menghukum individu yang membelot. Dengan demikian, kerja sama pada akhirnya merupakan keseimbangan yang tidak stabil yang ditakdirkan untuk kegagalan.
Catatan kaki
[1]: Alasan utama mengapa saya tidak ingin mengirimkan ribuan bot alih-alih hanya dua adalah bahwa hal itu akan memperlambat simulasi dengan faktor pada urutan 1000 [2], dan hal itu akan secara signifikan mengacaukan menangkan persentase karena bot lain hampir secara eksklusif akan bermain melawan gerombolan daripada satu sama lain. Namun, yang lebih penting adalah kenyataan bahwa bahkan jika saya menginginkannya, saya tidak akan dapat membuat banyak bot dalam kerangka waktu yang masuk akal tanpa melanggar semangat peraturan bahwa "Bot tidak boleh menerapkan strategi yang sama persis dengan yang ada, sengaja atau tidak sengaja ".
[2]: Saya pikir ada dua alasan utama mengapa simulasi ini melambat ketika menjalankan kawanan koperasi. Pertama, lebih banyak bot berarti lebih banyak permainan jika Anda ingin setiap bot bermain dalam jumlah permainan yang sama (dalam studi kasus, jumlah permainan akan berbeda dengan faktor sekitar 77). Kedua, permainan kooperatif hanya membutuhkan waktu lebih lama karena berlangsung selama 200 putaran penuh, dan dalam satu putaran pemain harus terus bergulir tanpa batas. Untuk pengaturan saya, game membutuhkan waktu sekitar 40 kali lebih lama untuk disimulasikan: studi kasus membutuhkan waktu lebih dari tiga menit untuk menjalankan 10.000 game, tetapi setelah menghapus gerombolan koperasi itu akan menyelesaikan 10.000 game hanya dalam 4,5 detik. Di antara dua alasan ini, saya perkirakan akan memakan waktu sekitar 3100 kali lebih lama untuk secara akurat mengukur kinerja bot ketika ada kawanan berkompetisi dibandingkan dengan ketika tidak ada.
sumber
GoTo20Bot
Coba saja dengan yang terbaik
GoToNBot
, Dan 20, 22, 24 bermain terbaik. Saya tidak tahu kenapa.Perbarui: selalu berhenti melempar jika mendapat skor 40 atau lebih.
sumber
end_score
ke 4000 (dan mengubah bot Anda untuk menggunakan ini dalamtarget
perhitungan), 15-16 bot itu jauh lebih baik. Tetapi jika permainan itu hanya tentang meningkatkan skor Anda itu akan sepele.end_score
4000, hampir tidak mungkin untuk mendapatkan 4000 sebelum 200 putaran. Dan permainannya adalah siapa yang mendapat skor tertinggi dalam 200 putaran. Dan berhenti pada 15 harus berhasil karena saat ini strategi untuk skor tertinggi dalam satu giliran sama dengan skor tertinggi dalam 200 putaran.Roller Adaptif
Mulai lebih agresif dan tenang menjelang akhir putaran.
Jika yakin itu menang, putar waktu ekstra untuk keselamatan.
sumber
lim = max(min(self.end_score - scores[self.index], 24), 6)
menaikkan maksimum menjadi 24 dan menambahkan minimum 6 meningkatkan persentase kemenangan sendiri dan bahkan lebih digabungkan.Alfa
Alpha menolak untuk menjadi yang kedua bagi siapa pun. Selama ada bot dengan skor lebih tinggi, itu akan terus bergulir.
sumber
yield
kerjanya, jika mulai bergulir tidak akan pernah berhenti. Anda ingin memperbaruimy_score
dalam loop.NotTooFarBehindBot
Idenya adalah bahwa bot lain mungkin kehilangan poin, jadi menjadi 2 tidak buruk - tetapi jika Anda sangat ketinggalan, Anda mungkin juga bangkrut.
sumber
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
. Meskipun bot Anda memimpin setelah 7 lemparan, ia terus sampai menyentuh 6. Saat saya mengetik ini, saya menemukan masalahnya! Satu-scores
satunya berisi total skor, bukan kasus mati untuk putaran saat ini. Anda harus memodifikasinyacurrent_score = scores[self.index] + sum(self.current_throws)
.GoHomeBot
Kami ingin menjadi besar atau pulang, kan? GoHomeBot sebagian besar baru saja pulang. (Tapi ternyata sangat baik!)
sumber
scores
daftar. Ada bot seperti ini sebelumnya (bot GoToEnd), tetapi david menghapus jawaban mereka. Saya akan mengganti bot itu dengan milik Anda.PastikanLead
EnsureLead meminjam ide dari GoTo20Bot. Ia menambahkan konsep yang selalu dianggapnya (ketika dalam last_round atau mencapai 40) bahwa ada orang lain yang akan memiliki setidaknya satu roll lagi. Dengan demikian, bot mencoba untuk sedikit di depan mereka, sehingga mereka harus mengejar ketinggalan.
sumber
Roll6TimesV2
Tidak mengalahkan yang terbaik saat ini, tapi saya pikir itu akan lebih baik dengan lebih banyak bot bermain.
Game yang benar-benar luar biasa.
sumber
StopBot
Secara harfiah hanya satu lemparan.
Ini setara dengan
Bot
kelas dasar .sumber
BringMyOwn_dice (BMO_d)
Bot ini menyukai dadu, ia membawa 2 (tampaknya melakukan yang terbaik) dadu sendiri. Sebelum melempar dadu dalam satu ronde, ia melempar 2 dadu sendiri dan menghitung jumlah mereka, ini adalah jumlah lemparan yang akan dilakukan, itu hanya melempar jika tidak memiliki 40 poin.
sumber
FooBot
sumber
# Must throw at least once
tidak dibutuhkan - melempar sekali sebelum memanggil bot Anda. Bot Anda akan selalu membuang minimal dua kali.make_throw
metode itu sejak awal, ketika saya ingin para pemain dapat melewati giliran mereka. Saya kira nama yang lebih tepatkeep_throwing
. Terima kasih atas umpan balik di kotak pasir, ini benar-benar membantu menjadikan ini tantangan yang tepat!Go Big Early
Konsep: Cobalah untuk menang besar pada roll awal (mencapai 25) kemudian merangkak naik dari sana 2 roll sekaligus.
sumber
BinaryBot
Berusaha untuk mendekati skor akhir, sehingga begitu orang lain memicu putaran terakhir, itu bisa mengalahkan skor mereka untuk menang. Target selalu setengah di antara skor saat ini dan skor akhir.
sumber
Hesitate
juga menolak untuk melewati batas terlebih dahulu. Anda harus mengelilingi fungsi Anda denganclass
barang - barang.PointsAreForNerdsBot
Yang ini tidak perlu penjelasan.
OneInFiveBot
Terus bergulir sampai gulungan lima pada mati itu sendiri 5-sisi. Lima kurang dari enam, jadi HARUS MENANG!
sumber
OneInFiveBot
adalah ide yang rapi, tetapi saya pikir itu menderita di akhir permainan dibandingkan dengan beberapa bot yang lebih maju. Masih pengiriman yang bagus!OneInFiveBot
cukup menarik dalam cara bahwa ia secara konsisten memiliki keseluruhan nilai tertinggi tercapai.StopBot
karung tinju: P. OneInFiveBot sebenarnya sangat rapi, pekerjaan yang bagus!OneInFiveBot
dan itu melakukan jauh lebih baik daripada yang saya harapkanLizduadacBot
Mencoba menang dalam 1 langkah. Kondisi akhir agak arbritrary.
Ini juga posting pertama saya (dan saya baru di Python), jadi jika saya mengalahkan "PointsAreForNerdsBot", saya akan senang!
sumber
PointsAreForNerdsBot
, tetapi bot Anda sebenarnya tarifnya cukup baik. Saya akan memperbarui skor nanti malam atau besok, tetapi kemenangan Anda sekitar 15%, yang lebih tinggi dari rata-rata 12,5%.SlowStart
Bot ini mengimplementasikan algoritma TCP Slow Start. Ini menyesuaikan jumlah gulungan ( atau ) sesuai dengan belokan sebelumnya: jika tidak menggulung 6 pada belokan sebelumnya, tingkatkan juga untuk putaran ini; sedangkan mengurangi atau jika itu terjadi.
sumber
def updateValues():
harusdef updateValues(self):
(ataudef update_values(self):
jika Anda ingin mengikuti PEP8). Kedua, panggilan ituupdateValues()
seharusnyaself.updateValues()
(atauself.update_vales()
).i
variabel Anda di loop sementara. Saat ini bot Anda melewati loop sementara seluruhnya atau macet di loop sementara hingga menyentuh 6.self.nor
dan melihat bagaimana itu mempengaruhi kinerja bot Anda.KwisatzHaderach
Kembali pada hari-hari awal tantangan ini (yaitu sebelum
NeoBot
diposting), saya menulisOracle
bot yang hampir sepele :tetapi tidak mempostingnya karena saya pikir itu tidak cukup menarik;) Tetapi begitu
NeoBot
mulai memimpin saya mulai berpikir tentang bagaimana cara mengalahkan kemampuannya yang sempurna untuk memprediksi masa depan. Jadi, inilah kutipan Dune; ketika Paul Atreides, Kwisatz Haderach, berdiri di nexus di mana tak terhingga masa depan yang berbeda dapat dibuka:Jadi, inilah jawabannya: meramalkan masa depan berarti mengubahnya; dan jika Anda sangat berhati-hati, maka dengan tindakan selektif atau tidak bertindak, Anda dapat mengubahnya dengan cara yang menguntungkan - setidaknya sebagian besar waktu. Bahkan mereka
KwisatzHaderach
tidak bisa mendapatkan tingkat kemenangan 100%!sumber
NeoBot
tetapi juga lebih baik! Saya juga suka bagaimana Anda memberikan contoh apa yang semuanya harus lakukan dengan keacakan (terutama controller) di sini harus dilakukan: gunakanrandom.Random
contoh Anda sendiri . SepertiNeoBot
, ini tampaknya sedikit sensitif terhadap perubahan detail implementasi yang tidak ditentukan dari controller.HarkonnenBot
tidak menyentuh RNG; sama sekali tidak peduli tentang angka acak. Itu hanya meracuni semua bot lainnya, lalu berjalan ke garis finish selambat mungkin. Seperti banyak hidangan kuliner lainnya, balas dendam adalah hidangan yang paling baik dinikmati perlahan, setelah persiapan yang panjang dan rumit.NeoBot
(danHarkonnenBot
),KwisatzHaderach
hanya bergantung pada satu detail implementasi; khususnya tidak perlu tahu bagaimana random.random () diimplementasikan, hanya saja controller menggunakannya; DKwisatzHaderach
danHarkonnenBot
dengan cara yang samaNeoBot
. Mereka akan menerima skor mereka dari simulasi dengan lebih sedikit game, dan tidak akan ada dalam simulasi resmi. Namun, mereka akan berakhir pada daftar skor tertinggi sepertiNeoBot
. Alasan utama mereka tidak berada dalam simulasi resmi adalah bahwa mereka akan mengacaukan strategi bot lainnya. Namun.WisdomOfCrowds
harus cocok untuk partisipasi, dan saya ingin tahu tentang perubahan baru yang telah Anda buat itu!Ya, yang itu sudah jelas
sumber
LastRound bertindak seperti itu selalu merupakan putaran terakhir dan itu adalah bot terakhir: itu terus bergulir sampai memimpin. Itu juga tidak ingin puas kurang dari 15 poin kecuali itu sebenarnya adalah babak terakhir atau mencapai 40 poin.
sumber
QuotaBot
Sistem "kuota" naif yang saya laksanakan, yang nampaknya skor keseluruhannya sangat tinggi.
sumber
if own_score mean + 5:
memberi saya kesalahan. Jugawhile sum(self.current_throws)
<
dan>
simbol yang mengganggu<pre>
tag yang saya gunakanHarapanBot
Mainkan langsung, menghitung nilai yang diharapkan untuk lemparan dadu dan hanya membuatnya jika itu positif.
Saya mengalami kesulitan menjalankan controller, mendapat "NameError: name 'bots_per_game' tidak didefinisikan" pada yang multithreaded, jadi benar-benar tidak tahu bagaimana performanya.
sumber
BlessRNG
BlessRNG FrankerZ GabeN BlessRNG
sumber
FortyTeen
Cobalah 14 poin hingga ronde terakhir, lalu anggap semua orang akan mencoba 14 poin dan mencoba mengikat skor itu.
sumber
TypeError: unsupported operand type(s) for -: 'list' and 'int'
dengan bot Anda.max_projected_score
harus menjadi maksimal daftar daripada seluruh daftar, apakah saya benar? Kalau tidak, saya mendapatkan masalah yang sama dengan tsh.Ragu-ragu
Lakukan dua langkah sederhana, lalu tunggu orang lain melewati batas. Versi yang diperbarui tidak lagi mencoba untuk mengalahkan skor tertinggi, hanya ingin mencapainya - meningkatkan kinerja dengan menghapus dua byte kode sumber!
sumber
Pemberontak
Bot ini menggabungkan strategi sederhana
Hesitate
dengan strategi putaran terakhir lanjutanBotFor2X
, mencoba mengingat siapa itu dan menjadi liar ketika menemukannya hidup dalam ilusi.sumber
HarkonnenBot
sehinggaRebel
tidak bisa lagi membatalkan sendiri;) dan saya juga telah mengubahTleilaxuBot
sehinggaRebel
tidak mendeteksi lagi!Ambil Lima
Separuh dari waktu, kita akan roll 5 sebelum 6. Ketika kita lakukan, uang tunai.
sumber
Pemburu
Chaser mencoba mengejar ketinggalan untuk menempati posisi pertama. Jika ini adalah putaran terakhir, dia mati-matian berusaha untuk mencapai setidaknya 50 poin. Hanya untuk ukuran yang baik dia melempar setidaknya empat kali tidak peduli apa pun yang terjadi.
[edit 1: menambahkan strategi go-for-gold di babak terakhir]
[edit 2: logika yang diperbarui karena saya keliru mengira bot akan mendapat skor di 40 daripada hanya skor bot tertinggi]
[edit 3: membuat pemburu sedikit lebih defensif di akhir permainan]
sumber
FutureBot
OneStepAheadBot
Sepasang bot, mereka membawa set dadu mereka sendiri dan menggulungnya untuk memprediksi masa depan. Jika salah satunya adalah 6 mereka berhenti, FutureBot tidak dapat mengingat yang mana dari 2 dadu itu untuk roll berikutnya sehingga menyerah.
Saya ingin tahu mana yang lebih baik.
OneStepAhead sedikit terlalu mirip dengan OneInFive untuk selera saya, tetapi saya juga ingin melihat bagaimana membandingkannya dengan FutureBot dan OneInFive.
Sunting: Sekarang mereka berhenti setelah mencapai 45
sumber