Permainan dadu, tetapi hindari nomor 6 [tertutup]


58

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.3108

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.

Dadu

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_rounddisetel 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 berakhir40
  • 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_statedisebut. 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_scorepermainan 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 Botkelas induk, misalnya jika Anda ingin menambahkan lebih banyak properti kelas. Anda tidak diizinkan untuk mengubah keadaan permainan dengan cara apa pun selain menghasilkan Trueatau 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_throwadalah tidak pernah menelepon, karena putaran Anda segera berakhir.

Bagi mereka yang baru mengenal Python (dan baru dalam yieldkonsep), tetapi ingin mencoba, yieldkata 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 yieldedit 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_throwfungsi Anda akan terus mengeksekusi tepat di mana jika berhenti sebelumnya, pada dasarnya di telepon setelah yieldpernyataan 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 -dflag. 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 importpernyataan 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.pydan forty_game_bots.py. Kemudian Anda cukup menggunakan python forty_game_controller.pyatau python3 forty_game_controller.pybergantung 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 ThrowTwiceBotdan GoToTenBotbot 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 NeoBotdan KwisatzHaderachmemang 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 HarkonnenBotmencapai 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|
    +----------+-----+

2
Jadi mungkin aturannya akan sedikit lebih jelas jika mereka mengatakan "ketika seorang pemain mengakhiri giliran mereka dengan skor setidaknya 40, semua orang mendapat giliran terakhir". Ini menghindari konflik nyata dengan menunjukkan bahwa itu tidak mencapai 40 yang benar-benar memicu putaran terakhir, itu berhenti dengan setidaknya 40.
aschepler

1
@aschepler itu formulasi yang baik, saya akan mengedit posting ketika saya di komputer saya
maxb

2
@maxb Saya telah memperpanjang controller untuk menambahkan lebih banyak statistik yang relevan dengan proses pengembangan saya: skor tertinggi tercapai, skor rata-rata tercapai dan skor kemenangan rata-rata gist.github.com/AwK/91446718a46f3e001c19533298b5756c
AKroell

2
Ini terdengar sangat mirip dengan permainan dadu yang sangat menyenangkan yang disebut Farkled en.wikipedia.org/wiki/Farkle
Caleb Jay

5
Saya memberikan suara untuk menutup pertanyaan ini karena sudah ditutup secara de facto untuk jawaban baru ("Turnamen sudah berakhir! Simulasi terakhir dijalankan pada malam hari, total 3 ∗ 108 pertandingan")
pppery

Jawaban:


6

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.

class OptFor2X(Bot):

    _r = []
    _p = []

    def _u(self,l):
        res = []
        for x in l:
            if isinstance(x,int):
                if x>0:
                    a=b=x
                else:
                    a,b=-2,-x
            else:
                if len(x)==1:
                    a = x[0]
                    if a<0:
                        a,b=-3,-a
                    else:
                        b=a+2
                else:
                    a,b=x
            if a<0:
                res.extend((b for _ in range(-a)))
            else:
                res.extend(range(a,b+1))
        res.extend((res[-1] for _ in range(40-len(res))))
        return res


    def __init__(self,*args):
        super().__init__(*args)
        if self._r:
            return
        self._r.append(self._u([[-8, 14], -15, [-6, 17], [18, 21], [21],
                                 -23, -24, 25, [-3, 21], [22, 29]]))
        self._r.extend((None for _ in range(13)))
        self._r.extend((self._u(x) for x in
                   ([[-19, 13], [-4, 12], -13, [-14], [-5, 15], [-4, 16],
                     -17, 18],
                    [[-6, 12], [-11, 13], [-4, 12], -11, -12, [-13], [-14],
                     [-5, 15], -16, 17],
                    [11, 11, [-10, 12], -13, [-24], 13, 12, [-6, 11], -12,
                     [-13], [-6, 14], -15, 16],
                    [[-8, 11], -12, 13, [-9, 23], 11, [-10], [-11], [-12],
                     [-5, 13], -14, [14]],
                    [[-4, 10], [-11], 12, [-14, 22], 10, 9, -10, [-4, 11],
                     [-5, 12], -13, -14, 15],
                    [[-4, 10], 11, [-18, 21], [-9], [-10], [-5, 11], [-12],
                     -13, 14],
                    [[-24, 20], [-5, 9], [-4, 10], [-4, 11], -12, 13],
                    [[-25, 19], [-8], [-4, 9], [-4, 10], -11, 12],
                    [[-26, 18], [-5, 8], [-5, 9], 10, [10]],
                    [[-27, 17], [-4, 7], [-5, 8], 9, [9]],
                    [[-28, 16], -6, [-5, 7], -8, -9, 10],
                    [[-29, 15], [-5, 6], [-7], -8, 9],
                    [[-29, 14], [-4, 5], [-4, 6], [7]],
                    [[-30, 13], -4, [-4, 5], 6, [6]], 
                    [[-31, 12], [-5, 4], 5, [5]],
                    [[-31, 11], [-4, 3], [3], 5, 6],
                    [[-31, 10], 11, [-2], 3, [3]],
                    [[-31, 9], 10, 2, -1, 2, [2]],
                    [[-31, 8], 9, [-4, 1], [1]],
                    [[-30, 7], [7], [-5, 1], 2],
                    [[-30, 6], [6], 1],
                    [[-31, 5], [6], 1],
                    [[-31, 4], [5, 8], 1],
                    [[-31, 3], [4, 7], 1],
                    [[-31, 2], [3, 6], 1],
                    [[-31, 1], [2, 10]] ) ))
        l=[0.0,0.0,0.0,0.0,1.0]
        for i in range(300):
            l.append(sum([a/6 for a in l[i:]]))
        m=[i/6 for i in range(1,5)]
        self._p.extend((1-sum([a*b for a,b in zip(m,l[i:])])
                                           for i in range(300)))

    def update_state(self,*args):
        super().update_state(*args)
        self.current_sum = sum(self.current_throws)

    def expect(self,mts,ops):
        p = 1.0
        for s in ops:
            p *= self._p[mts-s]
        return p

    def throw_again(self,mts,ops):
        ps = self.expect(mts,ops)
        pr = sum((self.expect(mts+d,ops) for d in range(1,6)))/6
        return pr>ps

    def make_throw(self,scores,last_round):
        myscore=scores[self.index]
        if last_round:
            target=max(scores)-myscore
            if max(scores)<40:
                opscores = scores[self.index+1:]
            else:
                opscores = []
                i = (self.index + 1) % len(scores)
                while scores[i] < 40:
                    opscores.append(scores[i])
                    i = (i+1) % len(scores)
        else:
            opscores = [s for i,s in enumerate(scores) if i!=self.index]
            bestop = max(opscores)
            target = min(self._r[myscore][bestop],40-myscore)
            # (could change the table instead of using min)
        while self.current_sum < target:
            yield True
        lr = last_round or myscore+self.current_sum >= 40
        while lr and self.throw_again(myscore+self.current_sum,opscores):
            yield True
        yield False

Saya akan melihat implementasinya secepat mungkin. Dengan perayaan Natal, mungkin tidak sampai tanggal 25
maks

Bot Anda ada di depan! Juga, Tidak perlu membuatnya berjalan lebih cepat, ini kira-kira sama cepatnya dengan semua bot lain dalam mengambil keputusan.
Maks

Saya tidak ingin membuatnya lebih cepat. Saya sudah melakukan apa yang ingin saya lakukan - menginisialisasi hanya sekali -, tetapi mencari cara yang lebih baik untuk melakukannya, terutama tanpa mendefinisikan fungsi di luar kelas. Saya pikir sekarang lebih baik.
Christian Sievers

Terlihat jauh lebih baik sekarang, kerja bagus!
Maks.

Selamat mengamankan tempat pertama dan kedua!
Maks

20

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.

class NeoBot(Bot):
    def __init__(self, index, end_score):
        self.random = None
        self.last_scores = None
        self.last_state = None
        super().__init__(index,end_score)

    def make_throw(self, scores, last_round):
        while True:
            if self.random is None:
                self.random = inspect.stack()[1][0].f_globals['random']
            tscores = scores[:self.index] + scores[self.index+1:]
            if self.last_scores != tscores:
                self.last_state = None
                self.last_scores = tscores
            future = self.predictnext_randint(self.random)
            if future == 6:
                yield False
            else:
                yield True

    def genrand_int32(self,base):
        base ^= (base >> 11)
        base ^= (base << 7) & 0x9d2c5680
        base ^= (base << 15) & 0xefc60000
        return base ^ (base >> 18)

    def predictnext_randint(self,cls):
        if self.last_state is None:
            self.last_state = list(cls.getstate()[1])
        ind = self.last_state[-1]
        width = 6
        res = width + 1
        while res >= width:
            y = self.last_state[ind]
            r = self.genrand_int32(y)
            res = r >> 29
            ind += 1
            self.last_state[-1] = (self.last_state[-1] + 1) % (len(self.last_state))
        return 1 + res

1
Selamat datang di PPCG! Ini jawaban yang sangat mengesankan. Ketika saya pertama kali menjalankannya, saya merasa terganggu oleh fakta bahwa ia menggunakan jumlah runtime yang sama dengan semua bot lainnya digabungkan. Kemudian saya melihat persentase kemenangan. Cara yang benar-benar cerdas untuk melewati aturan. Saya akan mengizinkan bot Anda untuk berpartisipasi dalam turnamen, tetapi saya berharap orang lain menahan diri dari menggunakan taktik yang sama seperti ini, karena itu melanggar semangat permainan.
Maks

2
Karena ada celah yang sangat besar antara bot ini dan tempat kedua, dikombinasikan dengan fakta bahwa bot Anda membutuhkan banyak komputasi, apakah Anda akan menerima bahwa saya menjalankan simulasi dengan iterasi yang lebih sedikit untuk menemukan tingkat kemenangan Anda, dan kemudian menjalankan official simulasi tanpa bot Anda?
Maks

3
Baik oleh saya, saya pikir masuk bahwa ini mungkin didiskualifikasi dan jelas tidak cukup dalam semangat permainan. Yang sedang berkata, itu adalah ledakan untuk bekerja dan alasan yang menyenangkan untuk melihat-lihat kode sumber python.
Mostly Harmless

2
Terima kasih! Saya tidak berpikir bot lain akan mendekati skor Anda. Dan bagi siapa pun yang berpikir untuk menerapkan strategi ini, jangan. Mulai sekarang strategi ini melanggar aturan, dan NeoBot adalah satu-satunya yang diizinkan menggunakannya demi menjaga turnamen tetap adil.
Maks

1
Yah, myBot mengalahkan semuanya, tapi ini jauh lebih baik - saya pikir jika saya memposting bot seperti ini, saya akan mendapatkan -100 dan bukan skor terbaik.
Jan Ivan

15

Kerumunan Koperasi

Strategi

Saya tidak berpikir ada orang lain yang menyadari pentingnya aturan ini:

Jika permainan berlangsung hingga 200 putaran, bot (atau bot) dengan skor tertinggi adalah pemenang, bahkan jika mereka tidak memiliki 40 poin atau lebih.

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

class CooperativeSwarmBot(Bot):
    def defection_strategy(self, scores, last_round):
        yield False

    def make_throw(self, scores, last_round):
        cooperate = max(scores) == 0
        if (cooperate):
            while True:
                yield True
        else:
            yield from self.defection_strategy(scores, last_round)

class CooperativeThrowTwice(CooperativeSwarmBot):
    def defection_strategy(self, scores, last_round):
        yield True
        yield False

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:

100130991299812897127961269512594124931230,115

Lebih umum, kemungkinan memilih bot koperasi dari kolam bot koperasi dan bot nonkooperatif adalah:sayacn

c!÷(c-saya)!(c+n)!÷(c+n-saya)!

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 ).saya=8n=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:

class CooperativeSwarm_1234(CooperativeSwarmBot):
    pass

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!

+---------------------+----+--------+--------+------+------+-------+------+--------+
|Bot                  |Win%|    Wins|  Played|   Max|   Avg|Avg win|Throws|Success%|
+---------------------+----+--------+--------+------+------+-------+------+--------+
|AggressiveStalker    |100.0|      21|      21|    42| 40.71|  40.71|  3.48|   46.32|
|PointsAreForNerdsBot |100.0|      31|      31|     0|  0.00|   0.00|  6.02|    0.00|
|TakeFive             |100.0|      18|      18|    44| 41.94|  41.94|  2.61|   50.93|
|Hesitate             |100.0|      26|      26|    44| 41.27|  41.27|  3.32|   41.89|
|Crush                |100.0|      34|      34|    44| 41.15|  41.15|  5.38|    6.73|
|StepBot              |97.0|      32|      33|    46| 41.15|  42.44|  4.51|   24.54|
|LastRound            |96.8|      30|      31|    44| 40.32|  41.17|  3.54|   45.05|
|Chaser               |96.8|      30|      31|    47| 42.90|  44.33|  3.04|   52.16|
|GoHomeBot            |96.8|      30|      31|    44| 40.32|  41.67|  5.60|    9.71|
|Stalker              |96.4|      27|      28|    44| 41.18|  41.44|  2.88|   57.53|
|ClunkyChicken        |96.2|      25|      26|    44| 40.96|  41.88|  2.32|   61.23|
|AdaptiveRoller       |96.0|      24|      25|    44| 39.32|  40.96|  4.49|   27.43|
|GoTo20Bot            |95.5|      21|      22|    44| 40.36|  41.33|  4.60|   30.50|
|FortyTeen            |95.0|      19|      20|    48| 44.15|  45.68|  3.71|   43.97|
|BinaryBot            |94.3|      33|      35|    44| 41.29|  41.42|  2.87|   53.07|
|EnsureLead           |93.8|      15|      16|    55| 42.56|  42.60|  4.04|   26.61|
|Roll6Timesv2         |92.9|      26|      28|    45| 40.71|  42.27|  4.07|   29.63|
|BringMyOwn_dice      |92.1|      35|      38|    44| 40.32|  41.17|  4.09|   28.40|
|LizduadacBot         |92.0|      23|      25|    54| 47.32|  51.43|  5.70|    5.18|
|FooBot               |91.7|      22|      24|    44| 39.67|  41.45|  3.68|   51.80|
|Alpha                |91.7|      33|      36|    48| 38.89|  42.42|  2.16|   65.34|
|QuotaBot             |90.5|      19|      21|    53| 38.38|  42.42|  3.88|   24.65|
|GoBigEarly           |88.5|      23|      26|    47| 41.35|  42.87|  3.33|   46.38|
|ExpectationsBot      |88.0|      22|      25|    44| 39.08|  41.55|  3.57|   45.34|
|LeadBy5Bot           |87.5|      21|      24|    50| 37.46|  42.81|  2.20|   63.88|
|GamblersFallacy      |86.4|      19|      22|    44| 41.32|  41.58|  2.05|   63.11|
|BePrepared           |86.4|      19|      22|    59| 39.59|  44.79|  3.81|   35.96|
|RollForLuckBot       |85.7|      18|      21|    54| 41.95|  47.67|  4.68|   25.29|
|OneStepAheadBot      |84.6|      22|      26|    50| 41.35|  46.00|  3.34|   42.97|
|FlipCoinRollDice     |78.3|      18|      23|    51| 37.61|  44.72|  1.67|   75.42|
|BlessRNG             |77.8|      28|      36|    47| 40.69|  41.89|  1.43|   83.66|
|FutureBot            |77.4|      24|      31|    49| 40.16|  44.38|  2.41|   63.99|
|SlowStart            |68.4|      26|      38|    57| 38.53|  45.31|  1.99|   66.15|
|NotTooFarBehindBot   |66.7|      20|      30|    50| 37.27|  42.00|  1.29|   77.61|
|ThrowThriceBot       |63.0|      17|      27|    51| 39.63|  44.76|  2.50|   55.67|
|OneInFiveBot         |58.3|      14|      24|    54| 33.54|  44.86|  2.91|   50.19|
|MatchLeaderBot       |48.1|      13|      27|    49| 40.15|  44.15|  1.22|   82.26|
|StopBot              | 0.0|       0|      27|    43| 30.26|   0.00|  1.00|   82.77|
+---------------------+----+--------+--------+------+------+-------+------+--------+

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.


4
Wow. Dan selamat datang di PPCG. Ini jawaban yang cukup pertama. Saya tidak benar-benar merencanakan situasi seperti ini. Anda tentu menemukan celah dalam aturan. Saya tidak begitu yakin bagaimana saya harus menilai ini, karena jawaban Anda adalah kumpulan bot daripada bot tunggal. Namun, satu-satunya hal yang akan saya katakan saat ini adalah rasanya tidak adil bahwa satu peserta akan mengendalikan 98,7% dari semua bot.
Maks.

2
Saya sebenarnya tidak ingin duplikat bot berada di kompetisi resmi; itu sebabnya saya menjalankan simulasi sendiri daripada mengirimkan ribuan bot yang hampir identik. Saya akan merevisi kiriman saya untuk membuatnya lebih jelas.
Einhaender

1
Seandainya saya mengantisipasi jawaban seperti ini, saya akan mengubah permainan yang berubah menjadi 200 putaran sehingga mereka tidak memberikan skor kepada pemain. Namun, seperti yang Anda perhatikan, ada aturan tentang membuat bot identik yang akan membuat strategi ini melanggar aturan. Saya tidak akan mengubah aturan, karena tidak adil bagi semua orang yang membuat bot. Namun, konsep kerja sama sangat menarik, Dan saya berharap ada bot lain yang diajukan yang menerapkan strategi kerja sama dengan strategi uniknya.
Maks.

1
Saya pikir posting Anda jelas setelah membacanya lebih teliti.
Maks.

Berapa banyak bot yang ada perlu untuk membungkus kode mereka dalam kerangka kerja sama ini agar mayoritas dari mereka dapat melihat keuntungan bersih dalam penempatan papan peringkat mereka? Tebakan naif saya adalah 50%.
Sparr

10

GoTo20Bot

class GoTo20Bot(Bot):

    def make_throw(self, scores, last_round):
        target = min(20, 40 - scores[self.index])
        if last_round:
            target = max(scores) - scores[self.index] + 1
        while sum(self.current_throws) < target:
            yield True
        yield False

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.


Saya juga telah bereksperimen dengan bot semacam itu. Skor rata-rata tertinggi per putaran ditemukan saat bot mencapai 16, tapi saya berasumsi bahwa "permainan akhir" membuat 20-bot menang lebih sering.
Maks

@ Maxbet Tidak begitu, 20 masih menjadi yang terbaik tanpa "permainan akhir" dalam pengujian saya. Mungkin Anda telah mengujinya pada versi lama controller.
tsh

Saya menjalankan tes terpisah sebelum merancang tantangan ini, di mana saya menghitung skor rata-rata per putaran untuk dua taktik di pos saya ("lempar x kali" dan "lempar sampai x skor"), dan maksimum yang saya temukan adalah untuk 15-16 . Meskipun ukuran sampel saya mungkin terlalu kecil, saya memang melihat ketidakstabilan.
Maks

2
Saya telah melakukan beberapa pengujian dengan ini, dan kesimpulan saya hanyalah bahwa 20 berfungsi dengan baik karena 40/2. Meskipun saya tidak sepenuhnya yakin. Ketika saya menetapkan end_scoreke 4000 (dan mengubah bot Anda untuk menggunakan ini dalam targetperhitungan), 15-16 bot itu jauh lebih baik. Tetapi jika permainan itu hanya tentang meningkatkan skor Anda itu akan sepele.
Maks.

1
@maxb Jika end_score4000, 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.
tsh

10

Roller Adaptif

Mulai lebih agresif dan tenang menjelang akhir putaran.
Jika yakin itu menang, putar waktu ekstra untuk keselamatan.

class AdaptiveRoller(Bot):

    def make_throw(self, scores, last_round):
        lim = min(self.end_score - scores[self.index], 22)
        while sum(self.current_throws) < lim:
            yield True
        if max(scores) == scores[self.index] and max(scores) >= self.end_score:
            yield True
        while last_round and scores[self.index] + sum(self.current_throws) <= max(scores):
            yield True
        yield False

Pengiriman pertama yang bagus! Saya akan menjalankannya terhadap bot yang saya tulis untuk pengujian, tetapi saya akan memperbarui skor tertinggi ketika lebih banyak bot telah diposting.
Maks.

Saya menjalankan beberapa tes dengan sedikit modifikasi pada bot Anda. 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.
AKroell

@AKroell: Keren! Saya bermaksud melakukan sesuatu yang serupa untuk memastikannya berputar beberapa kali pada akhirnya, tetapi saya belum mengambil waktu untuk melakukannya. Anehnya, tampaknya berkinerja lebih buruk dengan nilai-nilai itu ketika saya melakukan 100k berjalan. Saya hanya diuji dengan 18 bot. Mungkin saya harus melakukan beberapa tes dengan semua bot.
Emigna

5

Alfa

class Alpha(Bot):
    def make_throw(self, scores, last_round):
        # Throw until we're the best.
        while scores[self.index] + sum(self.current_throws) <= max(scores):
            yield True

        # Throw once more to assert dominance.
        yield True
        yield False

Alpha menolak untuk menjadi yang kedua bagi siapa pun. Selama ada bot dengan skor lebih tinggi, itu akan terus bergulir.


Karena cara yieldkerjanya, jika mulai bergulir tidak akan pernah berhenti. Anda ingin memperbarui my_scoredalam loop.
Spitemaster

@Spitemaster Tetap, terima kasih.
Mnemonic

5

NotTooFarBehindBot

class NotTooFarBehindBot(Bot):
    def make_throw(self, scores, last_round):
        while True:
            current_score = scores[self.index] + sum(self.current_throws)
            number_of_bots_ahead = sum(1 for x in scores if x > current_score)
            if number_of_bots_ahead > 1:
                yield True
                continue
            if number_of_bots_ahead != 0 and last_round:
                yield True
                continue
            break
        yield False

Idenya adalah bahwa bot lain mungkin kehilangan poin, jadi menjadi 2 tidak buruk - tetapi jika Anda sangat ketinggalan, Anda mungkin juga bangkrut.


1
Selamat datang di PPCG! Saya sedang melihat kiriman Anda, dan tampaknya semakin banyak pemain dalam permainan, semakin rendah persentase kemenangan untuk bot Anda. Saya tidak bisa langsung tahu mengapa. Dengan bot dicocokkan 1vs1 Anda mendapatkan 10% kemenangan. Idenya terdengar menjanjikan, dan kodenya terlihat benar, jadi saya tidak bisa memastikan mengapa tingkat kemenangan Anda tidak lebih tinggi.
Maks.

6
Saya telah melihat ke perilaku, dan garis ini telah saya bingung: 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- scoressatunya berisi total skor, bukan kasus mati untuk putaran saat ini. Anda harus memodifikasinya current_score = scores[self.index] + sum(self.current_throws).
Maks

Terima kasih - akan membuat perubahan itu!
Stuart Moore

5

GoHomeBot

class GoHomeBot(Bot):
    def make_throw(self, scores, last_round):
        while scores[self.index] + sum(self.current_throws) < 40:
            yield True
        yield False

Kami ingin menjadi besar atau pulang, kan? GoHomeBot sebagian besar baru saja pulang. (Tapi ternyata sangat baik!)


Karena bot ini selalu berlaku untuk 40 poin, itu tidak akan pernah memiliki poin dalam scoresdaftar. Ada bot seperti ini sebelumnya (bot GoToEnd), tetapi david menghapus jawaban mereka. Saya akan mengganti bot itu dengan milik Anda.
Maks.

1
Ini cukup lucu, melihat statistik bot ini yang diperluas: Kecuali untuk pointsAreForNerds dan StopBot, bot ini memiliki poin rata-rata terendah, namun ia memiliki rasio kemenangan yang bagus
Belhenix

5

PastikanLead

class EnsureLead(Bot):

    def make_throw(self, scores, last_round):
        otherScores = scores[self.index+1:] + scores[:self.index]
        maxOtherScore = max(otherScores)
        maxOthersToCome = 0
        for i in otherScores:
            if (i >= 40): break
            else: maxOthersToCome = max(maxOthersToCome, i)
        while True:
            currentScore = sum(self.current_throws)
            totalScore = scores[self.index] + currentScore
            if not last_round:
                if totalScore >= 40:
                    if totalScore < maxOtherScore + 10:
                        yield True
                    else:
                        yield False
                elif currentScore < 20:
                    yield True
                else:
                    yield False
            else:
                if totalScore < maxOtherScore + 1:
                    yield True
                elif totalScore < maxOthersToCome + 10:
                    yield True
                else:
                    yield False

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.


4

Roll6TimesV2

Tidak mengalahkan yang terbaik saat ini, tapi saya pikir itu akan lebih baik dengan lebih banyak bot bermain.

class Roll6Timesv2(Bot):
    def make_throw(self, scores, last_round):

        if not last_round:
            i = 0
            maximum=6
            while ((i<maximum) and sum(self.current_throws)+scores[self.index]<=40 ):
                yield True
                i=i+1

        if last_round:
            while scores[self.index] + sum(self.current_throws) < max(scores):
                yield True
        yield False

Game yang benar-benar luar biasa.


Selamat datang di PPCG! Sangat mengesankan untuk tidak hanya tantangan KotH pertama Anda, tetapi jawaban pertama Anda. Senang bahwa Anda menyukai permainan! Saya telah banyak berdiskusi tentang taktik terbaik untuk permainan setelah malam ketika saya memainkannya, jadi sepertinya sempurna untuk tantangan. Saat ini Anda berada di tempat ketiga dari 18.
Maks

4

StopBot

class StopBot(Bot):
    def make_throw(self, scores, last_round):
        yield False

Secara harfiah hanya satu lemparan.

Ini setara dengan Botkelas dasar .


1
Jangan minta maaf! Anda mengikuti semua aturan, meskipun saya khawatir bot Anda tidak terlalu efektif dengan rata-rata 2,5 poin per putaran.
Maks

1
Saya tahu, seseorang harus mengirim bot itu. Bot degenerasi untuk kerugian.
Zacharý

5
Saya akan mengatakan bahwa saya terkesan dengan bot Anda yang mengamankan tepat satu kemenangan dalam simulasi terakhir, membuktikan bahwa itu tidak sepenuhnya tidak berguna.
Maks.

2
ITU MENANG PERMAINAN ?! Itu mengejutkan.
Zacharý

3

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.

class BringMyOwn_dice(Bot):

    def __init__(self, *args):
        import random as rnd
        self.die = lambda: rnd.randint(1,6)
        super().__init__(*args)

    def make_throw(self, scores, last_round):

        nfaces = self.die() + self.die()

        s = scores[self.index]
        max_scores = max(scores)

        for _ in range(nfaces):
            if s + sum(self.current_throws) > 39:
                break
            yield True

        yield False

2
Saya sedang memikirkan bot acak menggunakan flip koin, tapi ini lebih semangat dengan tantangan! Saya pikir dua dadu melakukan yang terbaik, karena Anda mendapatkan poin terbanyak per babak saat Anda melemparkan dadu 5-6 kali, dekat dengan skor rata-rata saat melemparkan dua dadu.
Maks

3

FooBot

class FooBot(Bot):
    def make_throw(self, scores, last_round):
        max_score = max(scores)

        while True:
            round_score = sum(self.current_throws)
            my_score = scores[self.index] + round_score

            if last_round:
                if my_score >= max_score:
                    break
            else:
                if my_score >= self.end_score or round_score >= 16:
                    break

            yield True

        yield False

# Must throw at least oncetidak dibutuhkan - melempar sekali sebelum memanggil bot Anda. Bot Anda akan selalu membuang minimal dua kali.
Spitemaster

Terima kasih. Saya disesatkan oleh nama metode.
Peter Taylor

@PeterTaylor Terima kasih atas kiriman Anda! Saya memberi nama make_throwmetode itu sejak awal, ketika saya ingin para pemain dapat melewati giliran mereka. Saya kira nama yang lebih tepat keep_throwing. Terima kasih atas umpan balik di kotak pasir, ini benar-benar membantu menjadikan ini tantangan yang tepat!
Maks

3

Go Big Early

class GoBigEarly(Bot):
    def make_throw(self, scores, last_round):
        yield True  # always do a 2nd roll
        while scores[self.index] + sum(self.current_throws) < 25:
            yield True
        yield False

Konsep: Cobalah untuk menang besar pada roll awal (mencapai 25) kemudian merangkak naik dari sana 2 roll sekaligus.


3

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.

class BinaryBot(Bot):

    def make_throw(self, scores, last_round):
        target = (self.end_score + scores[self.index]) / 2
        if last_round:
            target = max(scores)

        while scores[self.index] + sum(self.current_throws) < target:
            yield True

        yield False

Menarik, Hesitatejuga menolak untuk melewati batas terlebih dahulu. Anda harus mengelilingi fungsi Anda dengan classbarang - barang.
Christian Sievers

3

PointsAreForNerdsBot

class PointsAreForNerdsBot(Bot):
    def make_throw(self, scores, last_round):
        while True:
            yield True

Yang ini tidak perlu penjelasan.

OneInFiveBot

class OneInFiveBot(Bot):
    def make_throw(self, scores, last_round):
        while random.randint(1,5) < 5:
            yield True
        yield False

Terus bergulir sampai gulungan lima pada mati itu sendiri 5-sisi. Lima kurang dari enam, jadi HARUS MENANG!


2
Selamat datang di PPCG! Saya yakin Anda sadar, tetapi bot pertama Anda secara harfiah adalah bot terburuk dalam kompetisi ini! Itu OneInFiveBotadalah ide yang rapi, tetapi saya pikir itu menderita di akhir permainan dibandingkan dengan beberapa bot yang lebih maju. Masih pengiriman yang bagus!
Maks.

2
yang OneInFiveBotcukup menarik dalam cara bahwa ia secara konsisten memiliki keseluruhan nilai tertinggi tercapai.
AKroell

1
Terima kasih telah memberikan StopBotkarung tinju: P. OneInFiveBot sebenarnya sangat rapi, pekerjaan yang bagus!
Zacharý

@ Maxb Yap, di situlah saya mendapat nama. Jujur saya tidak menguji OneInFiveBotdan itu melakukan jauh lebih baik daripada yang saya harapkan
The_Bob


3

LizduadacBot

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!

class LizduadacBot(Bot):

    def make_throw(self, scores, last_round):
        while scores[self.index] + sum(self.current_throws) < 50 or scores[self.index] + sum(self.current_throws) < max(scores):
            yield True
        yield False

Selamat datang di PPCG (dan selamat datang di Python)! Anda akan mengalami kesulitan kalah melawan 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%.
Maks

Dengan "masa sulit", maksud mereka tidak mungkin (kecuali saya sangat salah paham)
Zacharý

@ Maxb Saya sebenarnya tidak berpikir tingkat menang akan setinggi itu! (Saya tidak mengujinya secara lokal). Saya ingin tahu apakah mengubah 50 menjadi sedikit lebih tinggi / lebih rendah akan meningkatkan tingkat kemenangan.
lizduadac

3

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.

class SlowStart(Bot):
    def __init__(self, *args):
        super().__init__(*args)
        self.completeLastRound = False
        self.nor = 1
        self.threshold = 8

    def updateValues(self):
        if self.completeLastRound:
            if self.nor < self.threshold:
                self.nor *= 2
            else:
                self.nor += 1
        else:
            self.threshold = self.nor // 2
            self.nor = 1


    def make_throw(self, scores, last_round):

        self.updateValues()
        self.completeLastRound = False

        i = 1
        while i < self.nor:
            yield True

        self.completeLastRound = True        
        yield False

Selamat datang di PPCG! Pendekatan yang menarik, saya tidak tahu seberapa sensitifnya terhadap fluktuasi acak. Dua hal yang diperlukan untuk menjalankan ini: def updateValues():harus def updateValues(self):(atau def update_values(self):jika Anda ingin mengikuti PEP8). Kedua, panggilan itu updateValues()seharusnya self.updateValues()(atau self.update_vales()).
Maks.

2
Juga, saya pikir Anda perlu memperbarui ivariabel Anda di loop sementara. Saat ini bot Anda melewati loop sementara seluruhnya atau macet di loop sementara hingga menyentuh 6.
maks

Dalam skor tertinggi saat ini, saya mengambil kebebasan untuk mengimplementasikan perubahan ini. Saya pikir Anda bisa bereksperimen dengan nilai awal untuk self.nordan melihat bagaimana itu mempengaruhi kinerja bot Anda.
Maks.

3

KwisatzHaderach

import itertools
class KwisatzHaderach(Bot):
    """
    The Kwisatz Haderach foresees the time until the coming
    of Shai-Hulud, and yields True until it is immanent.
    """
    def __init__(self, *args):
        super().__init__(*args)
        self.roller = random.Random()
        self.roll = lambda: self.roller.randint(1, 6)
        self.ShaiHulud = 6

    def wormsign(self):
        self.roller.setstate(random.getstate())
        for i in itertools.count(0):
            if self.roll() == self.ShaiHulud:
                return i

    def make_throw(self, scores, last_round):
        target = max(scores) if last_round else self.end_score
        while True:
            for _ in range(self.wormsign()):
                yield True
            if sum(self.current_throws) > target + random.randint(1, 6):
                yield False                                               

Prescience biasanya menang - tetapi takdir tidak selalu dapat dihindari.
Hebat dan misterius adalah cara Shai-Hulud!


Kembali pada hari-hari awal tantangan ini (yaitu sebelum NeoBotdiposting), saya menulis Oraclebot yang hampir sepele :

    class Oracle(Bot):
        def make_throw(self, scores, last_round):
        randơm = random.Random()
        randơm.setstate(random.getstate())
        while True:
            yield randơm.randint(1, 6) != 6

tetapi tidak mempostingnya karena saya pikir itu tidak cukup menarik;) Tetapi begitu NeoBotmulai 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:

Dia menyadari, kesadaran itu adalah iluminasi yang memasukkan batas-batas dari apa yang diungkapkannya - sekaligus sumber keakuratan dan kesalahan yang berarti. Semacam ketidakpastian Heisenberg campur tangan: pengeluaran energi yang mengungkapkan apa yang dilihatnya, mengubah apa yang dilihatnya ... ... aksi paling singkat - kedipan mata, kata yang ceroboh, sebutir pasir yang salah tempat, menggerakkan tuas raksasa melintasi tuas raksasa alam semesta yang dikenal. Dia melihat kekerasan dengan hasil tunduk pada begitu banyak variabel sehingga gerakan sekecil apa pun menciptakan pergeseran besar dalam pola.

Visi itu membuatnya ingin membeku menjadi imobilitas, tetapi ini juga merupakan tindakan dengan konsekuensinya.

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 KwisatzHaderachtidak bisa mendapatkan tingkat kemenangan 100%!


Tampaknya bot ini mengubah keadaan generator angka acak, untuk memastikan bahwa ia menghindari rolling 6, atau setidaknya mengantisipasinya. Sama berlaku untuk HarkonnenBot. Namun, saya perhatikan bahwa tingkat kemenangan dari bot ini jauh lebih tinggi daripada NeoBot. Apakah Anda secara aktif memanipulasi generator angka acak untuk mencegahnya bergulir 6?
Maks

Oh, pada bacaan pertama saya, saya tidak melihat bahwa ini tidak hanya lebih baik daripada NeoBottetapi juga lebih baik! Saya juga suka bagaimana Anda memberikan contoh apa yang semuanya harus lakukan dengan keacakan (terutama controller) di sini harus dilakukan: gunakan random.Randomcontoh Anda sendiri . Seperti NeoBot, ini tampaknya sedikit sensitif terhadap perubahan detail implementasi yang tidak ditentukan dari controller.
Christian Sievers

@maxb: HarkonnenBottidak 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.
Dani O

@ChristianSievers: tidak seperti NeoBot (dan HarkonnenBot), KwisatzHaderachhanya bergantung pada satu detail implementasi; khususnya tidak perlu tahu bagaimana random.random () diimplementasikan, hanya saja controller menggunakannya; D
Dani O

1
Saya telah melihat semua bot Anda. Saya telah memutuskan untuk mengobati KwisatzHaderachdan HarkonnenBotdengan cara yang sama NeoBot. 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 seperti NeoBot. Alasan utama mereka tidak berada dalam simulasi resmi adalah bahwa mereka akan mengacaukan strategi bot lainnya. Namun. WisdomOfCrowdsharus cocok untuk partisipasi, dan saya ingin tahu tentang perubahan baru yang telah Anda buat itu!
Maks

2
class ThrowThriceBot(Bot):

    def make_throw(self, scores, last_round):
        yield True
        yield True
        yield False 

Ya, yang itu sudah jelas


Saya telah melakukan beberapa percobaan dengan kelas bot itu (itu adalah taktik yang saya gunakan ketika saya memainkan permainan untuk pertama kalinya). Saya pergi dengan 4 lemparan saat itu, meskipun 5-6 memiliki skor rata-rata yang lebih tinggi per putaran.
Maks.

Juga, selamat atas jawaban KotH pertama Anda!
Maks

2
class LastRound(Bot):
    def make_throw(self, scores, last_round):
        while sum(self.current_throws) < 15 and not last_round and scores[self.index] + sum(self.current_throws) < 40:
            yield True
        while max(scores) > scores[self.index] + sum(self.current_throws):
            yield True
        yield False

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.


Pendekatan yang menarik. Saya pikir bot Anda menderita jika mulai tertinggal. Karena kemungkinan mendapatkan> 30 poin dalam satu putaran rendah, bot Anda lebih cenderung tetap pada skor saat ini.
Maks.

1
Saya menduga ini menderita karena kesalahan yang sama yang saya buat (lihat komentar NotTooFarBehindBot) - seperti di babak terakhir, jika Anda tidak menang, Anda akan terus melempar sampai Anda mendapatkan 6 (skor [self.index] tidak pernah diperbarui) Sebenarnya - apakah Anda memiliki ketidaksetaraan itu dengan cara yang salah? maks (skor) akan selalu> = skor [self.index]
Stuart Moore

@StuartMoore Haha, ya, saya pikir Anda benar. Terima kasih!
Spitemaster

Saya curiga Anda ingin "dan last_round" pada jam ke-2 untuk melakukan apa yang Anda inginkan - sebaliknya jam ke-2 akan digunakan apakah last_round benar atau tidak
Stuart Moore

3
Itu disengaja. Selalu mencoba untuk menjadi yang terdepan saat mengakhiri gilirannya.
Spitemaster

2

QuotaBot

Sistem "kuota" naif yang saya laksanakan, yang nampaknya skor keseluruhannya sangat tinggi.

class QuotaBot(Bot):
    def __init__(self, *args):
        super().__init__(*args)
        self.quota = 20
        self.minquota = 15
        self.maxquota = 35

    def make_throw(self, scores, last_round):
        # Reduce quota if ahead, increase if behind
        mean = sum(scores) / len(scores)
        own_score = scores[self.index]

        if own_score < mean - 5:
            self.quota += 1.5
        if own_score > mean + 5:
            self.quota -= 1.5

        self.quota = max(min(self.quota, self.maxquota), self.minquota)

        if last_round:
            self.quota = max(scores) - own_score + 1

        while sum(self.current_throws) < self.quota:
            yield True

        yield False


if own_score mean + 5:memberi saya kesalahan. Jugawhile sum(self.current_throws)
Spitemaster

@Spitemaster adalah kesalahan menempel ke pertukaran stack, harus bekerja sekarang.
FlipTack

@Spitemaster itu karena ada <dan >simbol yang mengganggu <pre>tag yang saya gunakan
FlipTack

2

HarapanBot

Mainkan langsung, menghitung nilai yang diharapkan untuk lemparan dadu dan hanya membuatnya jika itu positif.

class ExpectationsBot(Bot):

    def make_throw(self, scores, last_round):
        #Positive average gain is 2.5, is the chance of loss greater than that?
        costOf6 = sum(self.current_throws) if scores[self.index] + sum(self.current_throws) < 40  else scores[self.index] + sum(self.current_throws)
        while 2.5 > (costOf6 / 6.0):
            yield True
            costOf6 = sum(self.current_throws) if scores[self.index] + sum(self.current_throws) < 40  else scores[self.index] + sum(self.current_throws)
        yield False

Saya mengalami kesulitan menjalankan controller, mendapat "NameError: name 'bots_per_game' tidak didefinisikan" pada yang multithreaded, jadi benar-benar tidak tahu bagaimana performanya.


1
Saya pikir ini akhirnya setara dengan bot "Go to 16", tapi kami belum memilikinya
Stuart Moore

1
@StuartMoore Itu ... adalah poin yang sangat benar, ya
Kain

Saya mengalami masalah Anda dengan controller ketika saya menjalankannya di mesin Windows saya. Entah bagaimana itu berjalan dengan baik di mesin Linux saya. Saya memperbarui pengontrol, dan akan memperbarui posting setelah selesai.
Maks

@ Maxb Terima kasih, mungkin sesuatu tentang variabel mana yang tersedia dalam proses yang berbeda. FYI juga memperbarui ini, saya membuat kesalahan konyol sekitar menghasilkan: /
Kain

2

BlessRNG

class BlessRNG(Bot):
    def make_throw(self, scores, last_round):
        if random.randint(1,2) == 1 :
            yield True
        yield False

BlessRNG FrankerZ GabeN BlessRNG


2

FortyTeen

class FortyTeen(Bot):
    def make_throw(self, scores, last_round):
        if last_round:
            max_projected_score = max([score+14 if score<self.end_score else score for score in scores])
            target = max_projected_score - scores[self.index]
        else:
            target = 14

        while sum(self.current_throws) < target:
            yield True
        yield False

Cobalah 14 poin hingga ronde terakhir, lalu anggap semua orang akan mencoba 14 poin dan mencoba mengikat skor itu.


Saya dapatkan TypeError: unsupported operand type(s) for -: 'list' and 'int'dengan bot Anda.
tsh

Saya berasumsi bahwa Anda max_projected_scoreharus menjadi maksimal daftar daripada seluruh daftar, apakah saya benar? Kalau tidak, saya mendapatkan masalah yang sama dengan tsh.
Maks.

Ups, diedit untuk diperbaiki.
histokrat

2

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!

class Hesitate(Bot):
    def make_throw(self, scores, last_round):
        myscore = scores[self.index]
        if last_round:
            target = max(scores)
        elif myscore==0:
            target = 17
        else:
            target = 35
        while myscore+sum(self.current_throws) < target:
            yield True
        yield False

2

Pemberontak

Bot ini menggabungkan strategi sederhana Hesitate dengan strategi putaran terakhir lanjutan BotFor2X, mencoba mengingat siapa itu dan menjadi liar ketika menemukannya hidup dalam ilusi.

class Rebel(Bot):

    p = []

    def __init__(self,*args):
        super().__init__(*args)
        self.hide_from_harkonnen=self.make_throw
        if self.p:
            return
        l = [0]*5+[1]
        for i in range(300):
            l.append(sum(l[i:])/6)
        m=[i/6 for i in range(1,5)]
        self.p.extend((1-sum([a*b for a,b in zip(m,l[i:])])
                                          for i in range(300) ))

    def update_state(self,*args):
        super().update_state(*args)
        self.current_sum = sum(self.current_throws)
        # remember who we are:
        self.make_throw=self.hide_from_harkonnen

    def expect(self,mts,ops):
        p = 1
        for s in ops:
            p *= self.p[mts-s]
        return p

    def throw_again(self,mts,ops):
        ps = self.expect(mts,ops)
        pr = sum((self.expect(mts+d,ops) for d in range(1,6)))/6
        return pr>ps

    def make_throw(self, scores, last_round):
        myscore = scores[self.index]
        if len(self.current_throws)>1:
            # hello Tleilaxu!
            target = 666
        elif last_round:
            target = max(scores)
        elif myscore==0:
            target = 17
        else:
            target = 35
        while myscore+self.current_sum < target:
            yield True
        if myscore+self.current_sum < 40:
            yield False
        opscores = scores[self.index+1:] + scores[:self.index]
        for i in range(len(opscores)):
            if opscores[i]>=40:
                opscores = opscores[:i]
                break
        while True:
            yield self.throw_again(myscore+self.current_sum,opscores)

Yah ini cukup elegan :) Juga, selamat karena mendapatkan tempat pertama dan kedua dalam kompetisi utama!
Dani O

Secara alami saya telah men-tweak HarkonnenBotsehingga Rebeltidak bisa lagi membatalkan sendiri;) dan saya juga telah mengubah TleilaxuBotsehingga Rebeltidak mendeteksi lagi!
Dani O

1

Ambil Lima

class TakeFive(Bot):
    def make_throw(self, scores, last_round):
        # Throw until we hit a 5.
        while self.current_throws[-1] != 5:
            # Don't get greedy.
            if scores[self.index] + sum(self.current_throws) >= self.end_score:
                break
            yield True

        # Go for the win on the last round.
        if last_round:
            while scores[self.index] + sum(self.current_throws) <= max(scores):
                yield True

        yield False

Separuh dari waktu, kita akan roll 5 sebelum 6. Ketika kita lakukan, uang tunai.


Jika kita berhenti di 1 saja, itu membuat kemajuan lebih lambat, tetapi lebih cenderung mencapai 40 dalam satu ikatan.
Mnemonic

Dalam tes saya, TakeOne mendapat 20,868 poin per putaran dibandingkan dengan TakeFive yang 24,262 poin per putaran (dan juga membawa kemenangan dari 0,291 menjadi 0,259). Jadi saya tidak berpikir itu sepadan.
Spitemaster

1

Pemburu

class Chaser(Bot):
    def make_throw(self, scores, last_round):
        while max(scores) > (scores[self.index] + sum(self.current_throws)):
            yield True
        while last_round and (scores[self.index] + sum(self.current_throws)) < 44:
            yield True
        while self.not_thrown_firce() and sum(self.current_throws, scores[self.index]) < 44:
            yield True
        yield False

    def not_thrown_firce(self):
        return len(self.current_throws) < 4

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]


Selamat datang di PPCG! Gagasan rapi untuk tidak hanya mengejar ketinggalan, tetapi juga melewati tempat pertama. Saya sedang menjalankan simulasi sekarang, dan saya berharap Anda beruntung!
Maks

Terima kasih! Awalnya saya mencoba untuk melampaui pemimpin sebelumnya dengan jumlah yang tetap (mencoba nilai antara 6 dan 20) tetapi ternyata hanya melemparkan dua kali lebih banyak pameran lebih baik.
AKroell

@JonathanFrech terima kasih, telah diperbaiki
AKroell

1

FutureBot

class FutureBot(Bot):
    def make_throw(self, scores, last_round):
        while (random.randint(1,6) != 6) and (random.randint(1,6) != 6):
            current_score = scores[self.index] + sum(self.current_throws)
            if current_score > (self.end_score+5):
                break
            yield True
        yield False

OneStepAheadBot

class OneStepAheadBot(Bot):
    def make_throw(self, scores, last_round):
        while random.randint(1,6) != 6:
            current_score = scores[self.index] + sum(self.current_throws)
            if current_score > (self.end_score+5):
                break
            yield True
        yield False

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


Selamat datang di PPCG! Bot Anda pasti bermain dengan semangat permainan! Saya akan menjalankan simulasi nanti malam.
Maks.

Terima kasih! Saya ingin tahu seberapa baik itu akan dilakukan, tetapi saya kira itu akan berada di sisi rendah.
william porter
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.