Một trò chơi súc sắc, nhưng tránh số 6 [đóng]


58

Giải đấu kết thúc!

Giải đấu đã kết thúc! Mô phỏng cuối cùng được thực hiện trong đêm, tổng cộng trò chơi. Người chiến thắng là Christian Sievers với bot OptFor2X của mình . Christian Sievers cũng cố gắng đảm bảo vị trí thứ hai với Rebel . Xin chúc mừng! Dưới đây bạn có thể xem danh sách điểm cao chính thức cho giải đấu.3*10số 8

Nếu bạn vẫn muốn chơi trò chơi, bạn nên sử dụng bộ điều khiển được đăng bên dưới và sử dụng mã trong đó để tạo trò chơi của riêng bạn.

Xúc xắc

Tôi được mời chơi một trò chơi súc sắc mà tôi chưa bao giờ nghe nói đến. Các quy tắc rất đơn giản, nhưng tôi nghĩ nó sẽ hoàn hảo cho một thử thách KotH.

Những quy định

Bắt đầu trò chơi

Con súc sắc đi vòng quanh bàn, và mỗi khi đến lượt của bạn, bạn có thể ném con súc vật nhiều lần như bạn muốn. Tuy nhiên, bạn phải ném nó ít nhất một lần. Bạn theo dõi tổng của tất cả các cú ném cho vòng của bạn. Nếu bạn chọn dừng lại, điểm cho vòng được thêm vào tổng số điểm của bạn.

Vậy tại sao bạn lại ngừng ném chết? Bởi vì nếu bạn nhận được 6, điểm số của bạn cho toàn bộ vòng sẽ bằng 0 và điểm chết được truyền lại. Do đó, mục tiêu ban đầu là tăng điểm của bạn càng nhanh càng tốt.

Ai là người chiến thắng?

Khi người chơi đầu tiên quanh bàn đạt 40 điểm trở lên, vòng cuối cùng bắt đầu. Khi vòng cuối cùng bắt đầu, tất cả mọi người ngoại trừ người bắt đầu vòng cuối cùng được thêm một lượt.

Các quy tắc cho vòng cuối cùng giống như bất kỳ vòng nào khác. Bạn chọn tiếp tục ném hoặc dừng lại. Tuy nhiên, bạn biết rằng bạn không có cơ hội chiến thắng nếu bạn không đạt được điểm cao hơn so với những người trước bạn vào vòng cuối cùng. Nhưng nếu bạn tiếp tục đi quá xa, thì bạn có thể nhận được 6.

Tuy nhiên, có một quy tắc nữa để xem xét. Nếu tổng số điểm hiện tại của bạn (điểm trước đó + điểm hiện tại của bạn cho vòng đấu) là 40 trở lên và bạn đạt 6, tổng điểm của bạn được đặt thành 0. Điều đó có nghĩa là bạn phải bắt đầu lại tất cả. Nếu bạn đạt 6 điểm khi tổng điểm hiện tại của bạn là 40 trở lên, trò chơi sẽ tiếp tục như bình thường, ngoại trừ việc bạn đang ở vị trí cuối cùng. Vòng cuối cùng không được kích hoạt khi tổng số điểm của bạn được đặt lại. Bạn vẫn có thể giành chiến thắng trong vòng này, nhưng nó trở nên khó khăn hơn.

Người chiến thắng là người chơi có số điểm cao nhất khi vòng cuối cùng kết thúc. Nếu hai hoặc nhiều người chơi chia sẻ cùng một số điểm, tất cả họ sẽ được tính là người chiến thắng.

Một quy tắc được thêm vào là trò chơi tiếp tục tối đa 200 vòng. Điều này là để ngăn chặn các trường hợp nhiều bot về cơ bản tiếp tục ném cho đến khi chúng đạt 6 điểm để giữ nguyên số điểm hiện tại. Khi vòng thứ 199 được thông qua, last_roundđược đặt thành đúng và một vòng nữa được phát. Nếu trò chơi đi được 200 vòng, bot (hoặc bot) có số điểm cao nhất sẽ là người chiến thắng, ngay cả khi họ không có 40 điểm trở lên.

Tóm tắt

  • Mỗi vòng bạn tiếp tục ném chết cho đến khi bạn chọn dừng lại hoặc bạn nhận được 6
  • Bạn phải ném chết một lần (nếu lần ném đầu tiên của bạn là 6, vòng của bạn ngay lập tức kết thúc)
  • Nếu bạn được điểm 6, điểm số hiện tại của bạn được đặt thành 0 (không phải tổng điểm của bạn)
  • Bạn thêm số điểm hiện tại của bạn vào tổng số điểm của bạn sau mỗi vòng
  • Khi một bot kết thúc lượt của họ dẫn đến tổng số điểm ít nhất là 40, mọi người khác sẽ có lượt cuối cùng
  • Nếu tổng số điểm hiện tại của bạn là và bạn nhận được 6, tổng số điểm của bạn được đặt thành 0 và vòng của bạn kết thúc40
  • Vòng cuối cùng không được kích hoạt khi xảy ra ở trên
  • Người có tổng điểm cao nhất sau vòng cuối cùng là người chiến thắng
  • Trong trường hợp có nhiều người chiến thắng, tất cả sẽ được tính là người chiến thắng
  • Trò chơi kéo dài tối đa 200 vòng

Làm rõ điểm số

  • Tổng số điểm: số điểm mà bạn đã lưu từ các vòng trước
  • Điểm hiện tại: điểm cho vòng hiện tại
  • Tổng điểm hiện tại: tổng của hai điểm trên

Bạn tham gia như thế nào

Để tham gia vào thử thách KotH này, bạn nên viết một lớp Python kế thừa từ đó Bot. Bạn nên thực hiện chức năng : make_throw(self, scores, last_round). Hàm đó sẽ được gọi khi đến lượt của bạn và lần ném đầu tiên của bạn không phải là 6. Để tiếp tục ném, bạn nên thực hiện yield True. Để ngừng ném, bạn nên yield False. Sau mỗi lần ném, hàm cha update_stateđược gọi. Vì vậy, bạn có quyền truy cập vào các cú ném của mình cho vòng hiện tại bằng cách sử dụng biến self.current_throws. Bạn cũng có quyền truy cập vào chỉ mục của riêng bạn bằng cách sử dụng self.index. Vì vậy, để xem tổng số điểm của riêng bạn, bạn sẽ sử dụng scores[self.index]. Bạn cũng có thể truy cập vào end_scoretrò chơi bằng cách sử dụng self.end_score, nhưng bạn có thể cho rằng nó sẽ là 40 cho thử thách này.

Bạn được phép tạo các hàm trợ giúp trong lớp. Bạn cũng có thể ghi đè các hàm hiện có trong Botlớp cha, ví dụ nếu bạn muốn thêm nhiều thuộc tính lớp. Bạn không được phép sửa đổi trạng thái của trò chơi theo bất kỳ cách nào ngoại trừ năng suất Truehoặc False.

Bạn có thể tự do tìm kiếm nguồn cảm hứng từ bài đăng này và sao chép bất kỳ hai bot mà tôi đã đưa vào đây. Tuy nhiên, tôi sợ rằng chúng không đặc biệt hiệu quả ...

Cho phép các ngôn ngữ khác

Trong cả hộp cát và trên Mười chín Byte, chúng tôi đã có các cuộc thảo luận về việc cho phép gửi bằng các ngôn ngữ khác. Sau khi đọc về các triển khai như vậy và nghe các đối số từ cả hai phía, tôi đã quyết định chỉ giới hạn thử thách này với Python. Điều này là do hai yếu tố: thời gian cần thiết để hỗ trợ nhiều ngôn ngữ và tính ngẫu nhiên của thử thách này đòi hỏi số lần lặp cao để đạt được sự ổn định. Tôi hy vọng rằng bạn vẫn sẽ tham gia và nếu bạn muốn tìm hiểu một số Python cho thử thách này, tôi sẽ cố gắng có mặt trong cuộc trò chuyện thường xuyên nhất có thể.

Đối với bất kỳ câu hỏi mà bạn có thể có, bạn có thể viết trong phòng trò chuyện cho thử thách này . Hẹn gặp bạn ở đó

Quy tắc

  • Phá hoại được cho phép, và khuyến khích. Đó là, phá hoại đối với người chơi khác
  • Bất kỳ nỗ lực nào để sửa lại bộ điều khiển, thời gian chạy hoặc các bài nộp khác sẽ bị loại. Tất cả các đệ trình chỉ nên làm việc với các đầu vào và lưu trữ mà chúng được đưa ra.
  • Bất kỳ bot nào sử dụng bộ nhớ lớn hơn 500 MB để đưa ra quyết định sẽ bị loại (nếu bạn cần nhiều bộ nhớ đó, bạn nên suy nghĩ lại về lựa chọn của mình)
  • Một bot không được thực hiện chiến lược chính xác giống như một chiến lược hiện có, cố ý hay vô tình.
  • Bạn được phép cập nhật bot của mình trong thời gian thử thách. Tuy nhiên, bạn cũng có thể đăng một bot khác nếu cách tiếp cận của bạn khác.

Thí dụ

class GoToTenBot(Bot):
    def make_throw(self, scores, last_round):
        while sum(self.current_throws) < 10:
            yield True
        yield False

Bot này sẽ tiếp tục cho đến khi nó có số điểm ít nhất là 10 cho vòng, hoặc nó ném 6. Lưu ý rằng bạn không cần bất kỳ logic nào để xử lý ném 6. Ngoài ra, hãy lưu ý rằng nếu lần ném đầu tiên của bạn là 6, make_throwlà không bao giờ được gọi, vì vòng của bạn là ngay lập tức kết thúc.

Đối với những người chưa quen với Python (và mới biết về yieldkhái niệm này), nhưng muốn thử, yieldtừ khóa này tương tự như trả về theo một số cách, nhưng khác về các cách khác. Bạn có thể đọc về khái niệm ở đây . Về cơ bản, một khi bạn yield, chức năng của bạn sẽ dừng lại và giá trị bạn yielded sẽ được gửi lại cho bộ điều khiển. Ở đó, bộ điều khiển xử lý logic của nó cho đến khi bot của bạn đưa ra quyết định khác. Sau đó, bộ điều khiển gửi cho bạn cú ném xúc xắc, và make_throwchức năng của bạn sẽ tiếp tục thực thi ngay khi dừng trước, về cơ bản trên dòng sau yieldcâu lệnh trước .

Bằng cách này, bộ điều khiển trò chơi có thể cập nhật trạng thái mà không yêu cầu một lệnh gọi chức năng bot riêng cho mỗi lần ném xúc xắc.

Đặc điểm kỹ thuật

Bạn có thể sử dụng bất kỳ thư viện Python nào có sẵn trong pip. Để đảm bảo rằng tôi có thể đạt được mức trung bình tốt, bạn có giới hạn thời gian 100 mili giây mỗi vòng. Tôi thực sự rất vui nếu kịch bản của bạn nhanh hơn thế, để tôi có thể chạy nhiều vòng hơn.

Đánh giá

Để tìm ra người chiến thắng, tôi sẽ lấy tất cả các bot và chạy chúng trong các nhóm ngẫu nhiên 8. Nếu có ít hơn 8 lớp được gửi, tôi sẽ chạy chúng trong các nhóm 4 ngẫu nhiên để tránh luôn có tất cả các bot trong mỗi vòng. Tôi sẽ chạy mô phỏng trong khoảng 8 giờ và người chiến thắng sẽ là bot có tỷ lệ thắng cao nhất. Tôi sẽ chạy bắt đầu các mô phỏng cuối cùng vào đầu năm 2019, mang đến cho bạn tất cả Giáng sinh để mã hóa bot của bạn! Ngày cuối cùng sơ bộ là ngày 4 tháng 1, nhưng nếu đó là quá ít thời gian tôi có thể thay đổi nó thành một ngày sau đó.

Cho đến lúc đó, tôi sẽ cố gắng thực hiện một mô phỏng hàng ngày với thời gian CPU 30-60 phút và cập nhật bảng điểm. Đây sẽ không phải là điểm chính thức, nhưng nó sẽ phục vụ như một hướng dẫn để xem bot nào hoạt động tốt nhất. Tuy nhiên, với Giáng sinh sắp tới, tôi hy vọng bạn có thể hiểu rằng tôi sẽ không có mặt mọi lúc. Tôi sẽ làm hết sức mình để chạy mô phỏng và trả lời bất kỳ câu hỏi nào liên quan đến thử thách.

Tự kiểm tra

Nếu bạn muốn chạy mô phỏng của riêng mình, đây là mã đầy đủ cho bộ điều khiển chạy mô phỏng, bao gồm hai bot mẫu.

Bộ điều khiển

Đây là bộ điều khiển cập nhật cho thử thách này. Nó hỗ trợ đầu ra ANSI, đa luồng và thu thập các số liệu thống kê bổ sung nhờ AKroell ! Khi tôi thay đổi bộ điều khiển, tôi sẽ cập nhật bài đăng sau khi tài liệu hoàn tất.

Nhờ BMO , bộ điều khiển hiện có thể tải xuống tất cả các bot từ bài đăng này bằng -dcờ. Chức năng khác là không thay đổi trong phiên bản này. Điều này sẽ đảm bảo rằng tất cả các thay đổi mới nhất của bạn được mô phỏng càng sớm càng tốt!

#!/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)

Nếu bạn muốn truy cập vào bộ điều khiển gốc cho thử thách này, nó có sẵn trong lịch sử chỉnh sửa. Bộ điều khiển mới có logic chính xác tương tự để chạy trò chơi, sự khác biệt duy nhất là hiệu suất, bộ sưu tập chỉ số và in ấn đẹp hơn.

Bots

Trên máy của tôi, các bot được giữ trong tệp forty_game_bots.py. Nếu bạn sử dụng bất kỳ tên nào khác cho tệp, bạn phải cập nhật importcâu lệnh ở đầu bộ điều khiển.

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

Chạy mô phỏng

Để chạy một mô phỏng, lưu cả hai đoạn mã được đăng ở trên vào hai tệp riêng biệt. Tôi đã cứu họ như forty_game_controller.pyforty_game_bots.py. Sau đó, bạn chỉ cần sử dụng python forty_game_controller.pyhoặc python3 forty_game_controller.pytùy thuộc vào cấu hình Python của bạn. Làm theo các hướng dẫn từ đó nếu bạn muốn định cấu hình mô phỏng của mình hơn nữa hoặc thử sửa đổi mã nếu bạn muốn.

Thống kê trò chơi

Nếu bạn đang tạo một bot nhắm đến một số điểm nhất định mà không xem xét các bot khác, thì đây là các phần trăm điểm chiến thắng:

+----------+-----+
|Percentile|Score|
+----------+-----+
|     50.00|   44|
|     75.00|   48|
|     90.00|   51|
|     95.00|   54|
|     99.00|   58|
|     99.90|   67|
|     99.99|  126|
+----------+-----+

Điểm cao

Khi có nhiều câu trả lời được đăng, tôi sẽ cố gắng cập nhật danh sách này. Nội dung của danh sách sẽ luôn là từ mô phỏng mới nhất. Các bot ThrowTwiceBotGoToTenBotlà các bot từ mã ở trên, và được sử dụng làm tài liệu tham khảo. Tôi đã thực hiện một mô phỏng với 10 ^ 8 trò chơi, mất khoảng 1 giờ. Sau đó, tôi thấy rằng trò chơi đạt đến sự ổn định so với các lần chạy của tôi với 10 ^ 7 trò chơi. Tuy nhiên, với những người vẫn đăng bot, tôi sẽ không thực hiện mô phỏng nào nữa cho đến khi tần suất phản hồi giảm.

Tôi cố gắng thêm tất cả các bot mới và thêm bất kỳ thay đổi nào bạn đã thực hiện cho các bot hiện có. Nếu có vẻ như tôi đã bỏ lỡ bot của bạn hoặc bất kỳ thay đổi mới nào bạn có, hãy viết trong cuộc trò chuyện và tôi chắc chắn sẽ có phiên bản mới nhất của bạn trong mô phỏng tiếp theo.

Bây giờ chúng tôi có nhiều số liệu thống kê hơn cho mỗi bot nhờ AKroell ! Ba cột mới chứa điểm tối đa trên tất cả các trò chơi, điểm trung bình cho mỗi trò chơi và điểm trung bình khi chiến thắng cho mỗi bot.

Như đã chỉ ra trong các bình luận, có một vấn đề với logic trò chơi khiến các bot có chỉ số cao hơn trong trò chơi có thêm một vòng trong một số trường hợp. Điều này đã được sửa chữa và điểm số dưới đây phản ánh điều này.

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|
+-----------------------+----+--------+--------+------+------+-------+------+--------+

Các bot sau (ngoại trừ Rebel) được tạo ra để bẻ cong các quy tắc và những người sáng tạo đã đồng ý không tham gia giải đấu chính thức. Tuy nhiên, tôi vẫn nghĩ rằng ý tưởng của họ là sáng tạo, và họ xứng đáng được đề cập đến. Rebel cũng nằm trong danh sách này vì nó sử dụng một chiến lược thông minh để tránh phá hoại, và thực sự hoạt động tốt hơn với bot phá hoại đang chơi.

Các bot NeoBotKwisatzHaderachkhông tuân theo các quy tắc, nhưng sử dụng kẽ hở bằng cách dự đoán trình tạo ngẫu nhiên. Vì các bot này cần rất nhiều tài nguyên để mô phỏng, tôi đã thêm số liệu thống kê của nó từ một mô phỏng với ít trò chơi hơn. Bot HarkonnenBotđạt được chiến thắng bằng cách vô hiệu hóa tất cả các bot khác, điều này hoàn toàn trái với quy tắc.

    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
Vì vậy, có thể các quy tắc sẽ rõ ràng hơn một chút nếu họ nói "khi người chơi kết thúc lượt của mình với số điểm ít nhất là 40, mọi người khác sẽ có lượt cuối". Điều này tránh sự xung đột rõ ràng bằng cách chỉ ra nó không đạt 40 mà thực sự gây nên những vòng đấu cuối cùng, nó dừng lại với ít nhất 40.
aschepler

1
@aschepler đó là một công thức tốt, tôi sẽ chỉnh sửa bài đăng khi tôi ở trên máy tính của mình
maxb

2
@maxb Tôi đã mở rộng bộ điều khiển để thêm nhiều số liệu thống kê phù hợp với quá trình phát triển của mình: đạt điểm cao nhất, điểm trung bình đạt được và điểm chiến thắng trung bình gist.github.com/AwK/91446718a46f3e001c19533298b5756c
AKroell

2
Điều này nghe có vẻ rất giống với một trò chơi súc sắc rất thú vị có tên là Farkled en.wikipedia.org/wiki/Farkle
Caleb Jay

5
Tôi đang bỏ phiếu để đóng câu hỏi này vì nó đã thực sự đóng lại với câu trả lời mới ("Giải đấu đã kết thúc! Mô phỏng cuối cùng đã được thực hiện trong đêm, tổng cộng 3 ∗ 108 trò chơi")
pppery

Câu trả lời:


6

OptFor2X

Bot này tuân theo một chiến lược gần đúng với chiến lược tối ưu cho phiên bản hai người chơi của trò chơi này, chỉ sử dụng điểm số của nó và điểm của đối thủ tốt nhất. Trong vòng cuối cùng, phiên bản cập nhật xem xét tất cả các điểm.

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

Tôi sẽ xem xét việc thực hiện sớm nhất có thể. Với lễ kỷ niệm Giáng sinh, có thể phải đến ngày 25
maxb

Bot của bạn đang dẫn đầu! Ngoài ra, không cần phải làm cho nó chạy nhanh hơn, nó gần như nhanh như tất cả các bot khác khi đưa ra quyết định.
maxb

Tôi không muốn làm cho nó nhanh hơn. Tôi đã làm những gì tôi muốn làm - chỉ khởi tạo một lần - nhưng đang tìm kiếm một cách tốt hơn để làm điều đó, đặc biệt là không xác định các chức năng bên ngoài lớp. Tôi nghĩ rằng nó là tốt hơn bây giờ.
Christian Sievers

Bây giờ có vẻ tốt hơn rất nhiều, làm tốt lắm!
maxb

Chúc mừng bạn đã đảm bảo cả vị trí thứ nhất và thứ hai!
maxb

20

NeoBot

Thay vào đó, chỉ cố gắng nhận ra sự thật - không có thìa

NeoBot nhìn trộm vào ma trận (hay còn gọi là ngẫu nhiên) và dự đoán liệu cuộn tiếp theo có phải là 6 hay không - không thể làm gì khi được trao số 6 để bắt đầu nhưng thật hạnh phúc khi tránh được một chuỗi kết thúc.

NeoBot không thực sự sửa đổi bộ điều khiển hoặc thời gian chạy, chỉ cần lịch sự hỏi thư viện để biết thêm thông tin.

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
Chào mừng đến với PPCG! Đây là một câu trả lời thực sự ấn tượng. Khi tôi lần đầu tiên chạy nó, tôi đã bị làm phiền bởi thực tế là nó đã sử dụng cùng một lượng thời gian chạy như tất cả các bot khác cộng lại. Sau đó, tôi nhìn vào tỷ lệ thắng. Cách thực sự thông minh của các quy tắc. Tôi sẽ cho phép bot của bạn tham gia giải đấu, nhưng tôi hy vọng rằng những người khác không sử dụng chiến thuật tương tự như thế này, vì nó vi phạm tinh thần của trò chơi.
maxb

2
Vì có một khoảng cách rất lớn giữa bot này và vị trí thứ hai, kết hợp với thực tế là bot của bạn đòi hỏi nhiều máy tính, bạn có chấp nhận rằng tôi chạy mô phỏng với số lần lặp ít hơn để tìm tỷ lệ thắng của bạn và sau đó chạy chính thức mô phỏng mà không có bot của bạn?
maxb

3
Tốt với tôi, tôi nhận ra rằng điều này có khả năng không đủ tiêu chuẩn và chắc chắn không hoàn toàn theo tinh thần của trò chơi. Điều đó đã được nói, đó là một vụ nổ để làm việc và một cái cớ thú vị để chọc vào mã nguồn của con trăn.
Chủ yếu là vô hại

2
cảm ơn! Tôi không nghĩ rằng bất kỳ bot khác sẽ gần với điểm số của bạn. Và đối với bất cứ ai khác nghĩ về việc thực hiện chiến lược này, đừng. Kể từ bây giờ, chiến lược này là trái với quy tắc và NeoBot là người duy nhất được phép sử dụng nó vì mục đích giữ cho giải đấu công bằng.
maxb

1
Chà, myBot đánh bại mọi người, nhưng điều này tốt hơn nhiều - tôi mặc dù nếu tôi đăng bot như thế này, tôi sẽ nhận được -100 và không đạt điểm cao nhất.
Tháng 1 Ivan

15

Hợp tác xã Swarm

Chiến lược

Tôi không nghĩ có ai khác nhận thấy tầm quan trọng của quy tắc này:

Nếu trò chơi đi được 200 vòng, bot (hoặc bot) có số điểm cao nhất sẽ là người chiến thắng, ngay cả khi họ không có 40 điểm trở lên.

Nếu mọi bot luôn lăn cho đến khi chúng bị đánh bại, thì mọi người sẽ có điểm 0 ở cuối vòng 200 và mọi người sẽ giành chiến thắng! Do đó, chiến lược của Hợp tác xã Swarm là hợp tác miễn là tất cả người chơi có điểm 0, nhưng chơi bình thường nếu có ai ghi được điểm nào.

Trong bài đăng này, tôi đang gửi hai bot: đầu tiên là CooperativeSwarmBot và thứ hai là CooperativeThrowTwice. CooperativeSwarmBot đóng vai trò là lớp cơ sở cho tất cả các bot chính thức là một phần của bầy hợp tác và có hành vi giữ chỗ chỉ đơn giản là chấp nhận cuộn thành công đầu tiên của nó khi hợp tác thất bại. CooperativeSwarmBot có CooperativeSwarmBot là cha mẹ của nó và giống hệt nhau về mọi mặt ngoại trừ hành vi không hợp tác của nó là tạo ra hai cuộn thay vì một cuộn. Trong vài ngày tới tôi sẽ sửa đổi bài đăng này để thêm các bot mới sử dụng hành vi thông minh hơn nhiều khi chơi với các bot không hợp tác.

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

Phân tích

Khả thi

Rất khó để hợp tác trong trò chơi này vì chúng tôi cần sự hỗ trợ của cả tám người chơi để nó hoạt động. Vì mỗi lớp bot được giới hạn một thể hiện cho mỗi trò chơi, đây là một mục tiêu khó đạt được. Ví dụ: tỷ lệ chọn tám bot hợp tác từ nhóm 100 bot hợp tác và 30 bot không hợp tác là:

100130*99129*98128*97127*96126*95125*94124*931230,15

Tổng quát hơn, tỷ lệ cược của việc lựa chọn chương trình hợp tác từ một vũng chương trình hợp tác và chương trình không hợp tác là:Tôicviết sai rồi

c!÷(c-Tôi)!(c+viết sai rồi)!÷(c+viết sai rồi-Tôi)!

Từ phương trình này, chúng ta có thể dễ dàng chỉ ra rằng chúng ta sẽ cần khoảng 430 bot hợp tác để 50% số trò chơi kết thúc hợp tác, hoặc khoảng 2900 bot cho 90% (sử dụng theo quy tắc và ).Tôi= =số 8viết sai rồi= =38

Nghiên cứu điển hình

Vì một số lý do (xem chú thích 1 và 2), một nhóm hợp tác thích hợp sẽ không bao giờ cạnh tranh trong các trò chơi chính thức. Như vậy, tôi sẽ tóm tắt kết quả của một trong những mô phỏng của riêng tôi trong phần này.

Mô phỏng này đã chạy 10000 trò chơi bằng cách sử dụng 38 bot khác đã được đăng ở đây lần trước khi tôi kiểm tra và 2900 bot có CooperativeSwarmBot là lớp cha mẹ của chúng. Bộ điều khiển báo cáo rằng 9051 trong số 10000 trò chơi (90,51%) đã kết thúc sau 200 vòng, khá gần với dự đoán rằng 90% trò chơi sẽ hợp tác. Việc thực hiện các bot này là tầm thường; ngoài CooperativeSwarmBot, tất cả đều có dạng này:

class CooperativeSwarm_1234(CooperativeSwarmBot):
    pass

Ít hơn 3% số bot có tỷ lệ thắng dưới 80% và chỉ hơn 11% số bot giành được mỗi trò chơi mà chúng chơi. Tỷ lệ phần trăm chiến thắng trung bình của 2900 bot trong bầy là khoảng 86%, rất tốt. Để so sánh, những người biểu diễn hàng đầu trên bảng xếp hạng chính thức hiện tại giành được ít hơn 22% số trò chơi của họ. Tôi không thể phù hợp với danh sách đầy đủ của nhóm hợp tác trong khoảng thời gian tối đa được phép cho câu trả lời, vì vậy nếu bạn muốn xem bạn sẽ phải đến đây thay vào đó: https://pastebin.com/3Zc8m1Ex

Vì mỗi bot chơi trong trung bình khoảng 27 trò chơi, may mắn chơi một cuộn tương đối lớn khi bạn nhìn vào kết quả cho từng bot. Vì tôi chưa thực hiện chiến lược tiên tiến cho các trò chơi không hợp tác, hầu hết các bot khác được hưởng lợi rất nhiều từ việc chơi với bầy hợp tác, thực hiện tỷ lệ thắng trung bình của bầy hợp tác là 86%.

Các kết quả đầy đủ cho các bot không có trong bầy được liệt kê dưới đây; Có hai bot mà kết quả tôi nghĩ xứng đáng được chú ý. Đầu tiên, StopBot không thể thắng bất kỳ trò chơi nào cả. Điều này đặc biệt bi thảm bởi vì bầy hợp tác đã thực sự sử dụng chiến lược chính xác giống như StopBot; bạn có thể mong đợi StopBot sẽ thắng tám trò chơi của mình một cách tình cờ, và một chút nữa vì bầy hợp tác buộc phải cho đối thủ của mình di chuyển đầu tiên. Tuy nhiên, kết quả thú vị thứ hai là sự chăm chỉ của PointsAreForNerdsBot cuối cùng cũng được đền đáp: nó hợp tác với bầy đàn và đã chiến thắng mọi trò chơi mà nó chơi!

+---------------------+----+--------+--------+------+------+-------+------+--------+
|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|
+---------------------+----+--------+--------+------+------+-------+------+--------+

Lỗ hổng

Có một vài nhược điểm của phương pháp hợp tác này. Đầu tiên, khi chơi với các bot không hợp tác, các bot hợp tác không bao giờ có được lợi thế trong lượt đầu tiên bởi vì khi họ chơi trước, họ không biết liệu đối thủ của mình có sẵn sàng hợp tác hay không, và do đó không có lựa chọn nào khác ngoài việc có được điểm bằng không. Tương tự, chiến lược hợp tác này cực kỳ dễ bị khai thác bởi các bot độc hại; chẳng hạn, trong quá trình chơi hợp tác, bot chơi vòng cuối cùng có thể chọn dừng lăn ngay lập tức để khiến mọi người khác thua cuộc (tất nhiên, giả sử rằng cuộn đầu tiên của họ không phải là sáu).

Bằng cách hợp tác, tất cả các bot có thể đạt được giải pháp tối ưu với tỷ lệ thắng 100%. Như vậy, nếu tỷ lệ thắng là điều duy nhất quan trọng thì hợp tác sẽ là trạng thái cân bằng ổn định và sẽ không có gì phải lo lắng. Tuy nhiên, một số bot có thể ưu tiên các mục tiêu khác, chẳng hạn như đạt đến đỉnh của bảng xếp hạng. Điều này có nghĩa là có nguy cơ bot khác có thể bị lỗi sau lượt cuối cùng của bạn, điều này tạo ra động lực để bạn đào thoát trước. Bởi vì thiết lập của cuộc thi này không cho phép chúng tôi thấy những gì đối thủ của chúng tôi đã làm trong các trò chơi trước đó của họ, chúng tôi không thể xử phạt những cá nhân đào thoát. Do đó, hợp tác cuối cùng là một trạng thái cân bằng không ổn định cam chịu thất bại.

Chú thích

[1]: Lý do chính khiến tôi không muốn gửi hàng ngàn bot thay vì chỉ hai là vì làm như vậy sẽ làm chậm quá trình mô phỏng theo hệ số 1000 [2] và làm như vậy sẽ gây rối đáng kể giành được tỷ lệ phần trăm vì các bot khác hầu như chỉ chơi với bầy đàn chứ không phải là nhau. Tuy nhiên, điều quan trọng hơn là thực tế là ngay cả khi tôi muốn, tôi sẽ không thể tạo ra nhiều bot trong khung thời gian hợp lý mà không phá vỡ quy tắc rằng "Một bot không được thực hiện chiến lược chính xác như một hiện có, cố ý hay vô tình ".

[2]: Tôi nghĩ có hai lý do chính khiến việc mô phỏng chậm lại khi chạy một bầy hợp tác. Đầu tiên, nhiều bot hơn có nghĩa là nhiều trò chơi hơn nếu bạn muốn mỗi bot chơi trong cùng một số trò chơi (trong nghiên cứu trường hợp, số lượng trò chơi sẽ khác nhau theo hệ số khoảng 77). Thứ hai, các trò chơi hợp tác chỉ mất nhiều thời gian hơn vì chúng kéo dài đủ 200 vòng, và trong vòng một, người chơi phải tiếp tục lăn lộn vô thời hạn. Đối với thiết lập của tôi, các trò chơi mất khoảng 40 lần để mô phỏng: nghiên cứu trường hợp mất hơn ba phút để chạy 10000 trò chơi, nhưng sau khi gỡ bỏ bầy hợp tác, nó sẽ hoàn thành 10000 trò chơi chỉ trong 4,5 giây. Giữa hai lý do này, tôi ước tính sẽ mất khoảng 3100 lần để đo chính xác hiệu suất của các bot khi có một bầy cạnh tranh so với khi không có.


4
Ồ Và chào mừng bạn đến với PPCG. Đây là câu trả lời đầu tiên. Tôi đã không thực sự lên kế hoạch cho một tình huống như thế này. Bạn chắc chắn tìm thấy một lỗ hổng trong các quy tắc. Tôi không thực sự chắc chắn làm thế nào tôi nên ghi điểm này, vì câu trả lời của bạn là một bộ sưu tập các bot chứ không phải là một bot. Tuy nhiên, điều duy nhất tôi sẽ nói ngay bây giờ là cảm thấy không công bằng khi một người tham gia sẽ kiểm soát 98,7% tất cả các bot.
maxb

2
Tôi thực sự không muốn các bot trùng lặp tham gia cuộc thi chính thức; đó là lý do tại sao tôi tự chạy mô phỏng thay vì gửi hàng ngàn bot rất gần giống nhau. Tôi sẽ sửa lại trình của tôi để làm cho điều đó rõ ràng hơn.
Einhaender

1
Nếu tôi dự đoán một câu trả lời như thế này, tôi sẽ thay đổi các trò chơi thành 200 vòng để họ không cho điểm cho người chơi. Tuy nhiên, như bạn lưu ý, có một quy tắc về việc tạo ra các bot giống hệt nhau sẽ khiến chiến lược này đi ngược lại các quy tắc. Tôi sẽ không thay đổi các quy tắc, vì nó sẽ không công bằng cho tất cả những người đã tạo ra một bot. Tuy nhiên, khái niệm hợp tác rất thú vị, và tôi hy vọng rằng có những bot khác được gửi để thực hiện chiến lược hợp tác kết hợp với chiến lược độc đáo của riêng mình.
maxb

1
Tôi nghĩ rằng bài viết của bạn là rõ ràng sau khi đọc nó kỹ lưỡng hơn.
maxb

Có bao nhiêu bot hiện tại sẽ cần bọc mã của họ trong khung hợp tác này để phần lớn trong số họ thấy được lợi ích ròng trong vị trí bảng xếp hạng của họ? Dự đoán ngây thơ của tôi là 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

Chỉ cần thử với tất cả GoToNBot, và 20, 22, 24 phát tốt nhất. Tôi không biết tại sao.


Cập nhật: luôn luôn ngừng ném nếu nhận được số điểm 40 trở lên.


Tôi cũng đã thử nghiệm với các loại bot. Điểm trung bình cao nhất cho mỗi vòng được tìm thấy khi bot lên 16, nhưng tôi cho rằng "trò chơi kết thúc" khiến 20-bot giành chiến thắng thường xuyên hơn.
maxb

@maxb Không phải vậy, 20 vẫn là game hay nhất mà không có "trò chơi kết thúc" trong thử nghiệm của tôi. Có lẽ bạn đã thử nó trên phiên bản cũ của bộ điều khiển.
tsh

Tôi đã chạy thử nghiệm riêng trước khi thiết kế thử thách này, trong đó tôi đã tính điểm trung bình mỗi vòng cho hai chiến thuật trong bài của mình ("ném x lần" và "ném cho đến x điểm") và tối đa tôi tìm thấy là 15-16 . Mặc dù kích thước mẫu của tôi có thể quá nhỏ, tôi đã nhận thấy sự không ổn định.
maxb

2
Tôi đã thực hiện một số thử nghiệm với điều này, và kết luận của tôi chỉ đơn giản là 20 hoạt động tốt vì nó là 40/2. Mặc dù tôi không hoàn toàn chắc chắn. Khi tôi đặt end_scorethành 4000 (và thay đổi bot của bạn để sử dụng targettính toán này), các bot 15-16 đã khá hơn rất nhiều. Nhưng nếu trò chơi chỉ là về việc tăng điểm của bạn thì nó sẽ là chuyện nhỏ.
maxb

1
@maxb Nếu end_scorelà 4000, gần như không thể có được 4000 trước 200 lượt. Và trò chơi chỉ đơn giản là người có số điểm cao nhất trong 200 lượt. Và dừng lại ở mức 15 nên hoạt động sẽ vì lần này chiến lược cho điểm cao nhất trong một lượt cũng giống như điểm cao nhất trong 200 lượt.
tsh

10

Con lăn thích ứng

Bắt đầu mạnh mẽ hơn và bình tĩnh về cuối vòng.
Nếu nó tin rằng nó chiến thắng, hãy dành thêm thời gian cho sự an toàn.

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

Tuyệt vời trình đầu tiên! Tôi sẽ chạy nó với các bot tôi đã viết để thử nghiệm, nhưng tôi sẽ cập nhật điểm cao khi có nhiều bot được đăng.
maxb

Tôi đã chạy một số thử nghiệm với sửa đổi nhỏ cho bot của bạn. lim = max(min(self.end_score - scores[self.index], 24), 6)tăng tối đa lên 24 và thêm tối thiểu 6, cả hai đều tự tăng tỷ lệ chiến thắng và thậm chí nhiều hơn thế.
AKroell

@AKroell: Tuyệt! Tôi đã có ý định làm một cái gì đó tương tự để đảm bảo rằng nó sẽ lăn vài lần vào cuối, nhưng tôi vẫn chưa dành thời gian để làm điều đó. Thật kỳ lạ, nó dường như hoạt động kém hơn với những giá trị đó khi tôi chạy 100k. Tôi chỉ thử nghiệm với 18 bot. Có lẽ tôi nên làm một số thử nghiệm với tất cả các bot.
Emigna

5

Alpha

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 từ chối không bao giờ là thứ hai cho bất cứ ai. Miễn là có một bot với số điểm cao hơn, nó sẽ tiếp tục lăn.


Bởi vì cách thức yieldhoạt động, nếu nó bắt đầu lăn nó sẽ không bao giờ dừng lại. Bạn sẽ muốn cập nhật my_scoretrong vòng lặp.
Spitemaster 19/12/18

@Spitemaster Đã sửa, cảm ơn.
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

Ý tưởng là các bot khác có thể mất điểm, vì vậy đứng thứ 2 không phải là xấu - nhưng nếu bạn ở phía sau, bạn cũng có thể bị phá vỡ.


1
Chào mừng đến với PPCG! Tôi đang xem qua bài đăng của bạn và dường như càng có nhiều người chơi trong trò chơi, tỷ lệ thắng dành cho bot của bạn càng thấp. Tôi không thể nói tại sao ngay lập tức. Với các bot được khớp 1vs1, bạn sẽ nhận được 10% winrate. Ý tưởng nghe có vẻ hứa hẹn và mã có vẻ chính xác, vì vậy tôi thực sự không thể biết tại sao tỷ lệ thắng của bạn không cao hơn.
maxb

6
Tôi đã xem xét hành vi và dòng này khiến tôi bối rối : 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. Mặc dù bot của bạn đang dẫn đầu sau 7 lần ném, nó vẫn tiếp tục cho đến khi nó đạt 6. Khi tôi gõ cái này, tôi đã tìm ra vấn đề! Các scoreschỉ chứa tổng số điểm, không phải là trường hợp chết cho vòng hiện hành. Bạn nên sửa đổi nó để được current_score = scores[self.index] + sum(self.current_throws).
maxb

Cảm ơn - sẽ tạo ra sự thay đổi đó!
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

Chúng tôi muốn đi lớn hoặc về nhà, phải không? GoHomeBot chủ yếu chỉ về nhà. (Nhưng thật đáng ngạc nhiên!)


Vì bot này luôn đạt 40 điểm, nên nó sẽ không bao giờ có điểm nào trong scoresdanh sách. Có một bot như thế này trước đây (bot GoToEnd), nhưng david đã xóa câu trả lời của họ. Tôi sẽ thay thế bot đó bằng của bạn.
maxb

1
Điều này khá buồn cười, khi thấy các số liệu thống kê mở rộng của các bot này: Ngoại trừ điểmAreForNerds và StopBot, bot này có điểm trung bình thấp nhất, nhưng nó có tỷ lệ thắng đẹp
Belhenix 20/12/18

5

Đảm bảo

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

SureLead mượn ý tưởng từ GoTo20Bot. Nó bổ sung khái niệm mà nó luôn xem xét (khi ở Last_round hoặc đạt 40) rằng có những cái khác sẽ có ít nhất một cuộn nữa. Vì vậy, bot cố gắng đi trước họ một chút, để họ phải bắt kịp.


4

Roll6TimesV2

Không đánh bại tốt nhất hiện tại, nhưng tôi nghĩ nó sẽ công bằng hơn với nhiều bot hơn đang chơi.

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

Bằng cách này, trò chơi thực sự tuyệt vời.


Chào mừng đến với PPCG! Rất ấn tượng cho không chỉ thử thách KotH đầu tiên của bạn, mà cả câu trả lời đầu tiên của bạn. Vui mừng khi bạn thích trò chơi! Tôi đã có nhiều cuộc thảo luận về chiến thuật tốt nhất cho trò chơi sau buổi tối khi tôi chơi nó, vì vậy nó có vẻ hoàn hảo cho một thử thách. Bạn hiện đang ở vị trí thứ ba trong số 18.
maxb

4

StopBot

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

Nghĩa đen chỉ một lần ném.

Điều này tương đương với Botlớp cơ sở .


1
Đừng xin lỗi! Bạn đang tuân thủ tất cả các quy tắc, mặc dù tôi sợ rằng bot của bạn không hiệu quả khủng khiếp với trung bình 2,5 điểm mỗi vòng.
maxb

1
Tôi biết, ai đó đã phải đăng bot đó. Bot thoái hóa cho sự mất mát.
Zacharý

5
Tôi nói rằng tôi rất ấn tượng bởi bot của bạn đảm bảo chính xác một chiến thắng trong lần mô phỏng cuối cùng, chứng minh rằng nó không hoàn toàn vô dụng.
maxb

2
NÓ CÓ MỘT TRÒ CHƠI?! Đó là điều đáng ngạc nhiên.
Zacharý

3

Mang MYOwn_dice (BMO_d)

Bot này yêu thích xúc xắc, nó mang lại xúc xắc 2 (dường như thực hiện tốt nhất) của riêng mình. Trước khi ném xúc xắc vào một vòng, nó sẽ ném 2 con xúc xắc của riêng mình và tính tổng của chúng, đây là số lần ném nó sẽ thực hiện, nó chỉ ném nếu nó không có 40 điểm.

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
Tôi đã nghĩ về một bot ngẫu nhiên bằng cách sử dụng đồng xu lật, nhưng điều này đúng hơn về mặt tinh thần với thử thách! Tôi nghĩ rằng hai con xúc xắc thực hiện tốt nhất, vì bạn nhận được nhiều điểm nhất mỗi vòng khi bạn ném chết 5-6 lần, gần với điểm trung bình khi ném hai con xúc xắc.
maxb

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 oncelà không cần thiết - nó ném một lần trước khi gọi bot của bạn. Bot của bạn sẽ luôn ném tối thiểu hai lần.
Spitemaster 19/12/18

Cảm ơn. Tôi đã bị đánh lừa bởi tên của phương pháp.
Peter Taylor

@PeterTaylor Cảm ơn bạn đã gửi! Tôi đặt tên cho make_throwphương pháp này từ rất sớm, khi tôi muốn người chơi có thể bỏ qua lượt của mình. Tôi đoán một cái tên thích hợp hơn sẽ là keep_throwing. Cảm ơn phản hồi trong hộp cát, nó thực sự đã giúp biến đây thành một thử thách thích hợp!
maxb

3

Đi sớm

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

Khái niệm: Cố gắng giành chiến thắng lớn trên một cuộn sớm (đến 25) sau đó leo lên từ đó 2 cuộn một lần.


3

Nhị phân

Cố gắng để đạt được gần điểm số cuối cùng, để ngay khi có người khác kích hoạt vòng cuối cùng, nó có thể đánh bại điểm số của họ để giành chiến thắng. Mục tiêu luôn là một nửa giữa điểm hiện tại và điểm cuối.

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

Thú vị, Hesitatecũng từ chối vượt qua dòng đầu tiên. Bạn cần bao quanh chức năng của bạn với các classcông cụ.
Christian Sievers

3

PointsAreForNerdsBot

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

Điều này không cần giải thích.

OneInFiveBot

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

Tiếp tục lăn cho đến khi nó lăn một cái năm trên cái chết 5 mặt của chính nó. Năm là ít hơn sáu, vì vậy nó đã thắng!


2
Chào mừng đến với PPCG! Tôi chắc chắn bạn biết, nhưng bot đầu tiên của bạn đúng là bot tồi tệ nhất trong cuộc thi này! Đây OneInFiveBotlà một ý tưởng gọn gàng, nhưng tôi nghĩ rằng nó bị ảnh hưởng trong trò chơi kết thúc so với một số bot cao cấp hơn. Vẫn là một đệ trình tuyệt vời!
maxb

2
Điều OneInFiveBotnày khá thú vị theo cách mà anh ta luôn đạt được tổng điểm cao nhất.
AKroell

1
Cảm ơn đã cho StopBotmột túi đấm: P. OneInFiveBot thực sự là khá gọn gàng, công việc tốt!
Zacharý

@maxb Yep, đó là nơi tôi có tên. Thành thật tôi đã không kiểm tra OneInFiveBotvà nó hoạt động tốt hơn tôi mong đợi
The_Bob


3

LizduadacBot

Cố gắng để giành chiến thắng trong 1 bước. Điều kiện kết thúc là một phần arbritrary.

Đây cũng là bài viết đầu tiên của tôi (và tôi mới biết về Python), vì vậy nếu tôi đánh bại "PointsAreForNerdsBot", tôi sẽ rất vui!

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

Chào mừng bạn đến với PPCG (và chào mừng bạn đến với Python)! Bạn sẽ có một khoảng thời gian khó khăn để chống lại PointsAreForNerdsBot, nhưng bot của bạn thực sự có giá khá tốt. Tôi sẽ cập nhật điểm vào tối nay hoặc ngày mai, nhưng tỷ lệ thắng của bạn là khoảng 15%, cao hơn mức trung bình 12,5%.
maxb

Bởi "thời gian khó khăn", họ có nghĩa là không thể (trừ khi tôi hiểu lầm rất nhiều)
Zacharý

@maxb Tôi thực sự không nghĩ tỷ lệ thắng sẽ cao đến thế! (Tôi đã không kiểm tra nó tại địa phương). Tôi tự hỏi nếu thay đổi 50 thành cao hơn / thấp hơn một chút sẽ làm tăng tỷ lệ thắng.
lizduadac

3

Bắt đầu chậm

Bot này thực hiện thuật toán TCP Slow Start. Nó điều chỉnh số lượng cuộn của nó ( cũng không ) theo lượt trước của nó: nếu nó không lăn số 6 trong lượt trước, thì cũng không tăng lượt cho lượt này; trong khi nó giảm cũng không nếu nó đã làm.

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

Chào mừng đến với PPCG! Cách tiếp cận thú vị, tôi không biết mức độ nhạy cảm của nó đối với các dao động ngẫu nhiên. Hai điều cần thiết để thực hiện việc này: def updateValues():nên là def updateValues(self):(hoặc def update_values(self):nếu bạn muốn theo PEP8). Thứ hai, updateValues()thay vào đó , cuộc gọi nên là self.updateValues()(hoặc self.update_vales()).
maxb

2
Ngoài ra, tôi nghĩ rằng bạn cần cập nhật ibiến của mình trong vòng lặp while. Ngay bây giờ bot của bạn hoàn toàn vượt qua vòng lặp while hoặc bị kẹt trong vòng lặp while cho đến khi nó chạm 6.
maxb

Trong các điểm cao hiện tại, tôi đã tự do thực hiện những thay đổi này. Tôi nghĩ rằng bạn có thể thử nghiệm với giá trị ban đầu self.norvà xem nó ảnh hưởng đến hiệu suất của bot của bạn như thế nào.
maxb

3

KwisatzHader

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                                               

Sự kiêu ngạo thường chiến thắng - nhưng định mệnh không thể luôn luôn tránh được.
Tuyệt vời và bí ẩn là những cách của Shai-Hulud!


Quay trở lại những ngày đầu của thử thách này (tức là trước khi NeoBotđược đăng), tôi đã viết một Oraclebot gần như tầm thường :

    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

nhưng không đăng nó vì tôi không nghĩ nó đủ thú vị;) Nhưng một khi NeoBotđã đi vào vị trí dẫn đầu, tôi bắt đầu nghĩ về cách đánh bại khả năng hoàn hảo của nó để dự đoán tương lai. Vì vậy, đây là một trích dẫn Dune; đó là khi Paul Atreides, Kwisatz Haderach, đứng ở một mối quan hệ mà từ đó vô số các tương lai khác nhau có thể không được kiểm soát:

Ông đã nhận ra rằng, sự lão hóa là một sự chiếu sáng kết hợp các giới hạn của những gì nó tiết lộ - cùng một lúc là một nguồn chính xác và lỗi có ý nghĩa. Một loại sự bất định của Heisenberg đã can thiệp: sự tiêu tốn năng lượng tiết lộ những gì anh ta thấy, đã thay đổi những gì anh ta nhìn thấy, đó là hành động trong phút chốc nhất - nháy mắt, một từ bất cẩn, một hạt cát bị đặt sai chỗ - di chuyển một đòn bẩy khổng lồ qua vũ trụ đã biết. Anh ta thấy bạo lực với kết quả chịu quá nhiều biến số đến nỗi chuyển động nhỏ nhất của anh ta đã tạo ra sự thay đổi lớn trong các mô hình.

Tầm nhìn khiến anh muốn đóng băng thành bất động, nhưng đây cũng là hành động với hậu quả của nó.

Vì vậy, đây là câu trả lời: thấy trước tương lai là thay đổi nó; và nếu bạn rất cẩn thận, thì bằng hành động chọn lọc hoặc không hành động, bạn có thể thay đổi nó theo cách có lợi - ít nhất là hầu hết thời gian. Ngay cả những người KwisatzHaderachkhông thể có được tỷ lệ thắng 100%!


Có vẻ như bot này thay đổi trạng thái của trình tạo số ngẫu nhiên, để đảm bảo rằng nó tránh được việc lăn 6, hoặc ít nhất là dự đoán nó. Tương tự với HarkonnenBot. Tuy nhiên, tôi lưu ý rằng tỷ lệ thắng của các bot này cao hơn nhiều so với NeoBot. Bạn có đang tích cực thao tác trình tạo số ngẫu nhiên để ngăn không cho nó lăn 6 không?
maxb

Ồ, trong lần đọc đầu tiên tôi đã không nhận thấy rằng nó không chỉ đẹp hơn NeoBotmà còn tốt hơn! Tôi cũng thích cách bạn đưa ra một ví dụ về những gì mọi thứ sử dụng tính ngẫu nhiên (đặc biệt là bộ điều khiển) ở đây nên làm: sử dụng random.Randomví dụ của riêng bạn . Giống như NeoBot, điều này có vẻ hơi nhạy cảm với những thay đổi của chi tiết triển khai không xác định của bộ điều khiển.
Christian Sievers

@maxb: HarkonnenBotkhông chạm vào RNG; nó không quan tâm đến tất cả về số ngẫu nhiên. Nó chỉ đầu độc tất cả các bot khác, sau đó đi bộ về đích càng chậm càng tốt. Giống như nhiều món ngon ẩm thực, trả thù là một món ăn ngon nhất được thưởng thức từ từ, sau khi chuẩn bị lâu dài và tinh tế.
Dani O

@ChristianSievers: không giống NeoBot (và HarkonnenBot), chỉ KwisatzHaderachdựa vào một chi tiết của việc thực hiện; đặc biệt là không cần biết cách thức Random.random () được thực hiện, chỉ có điều khiển sử dụng nó; D
Dani O

1
Tôi đã xem qua tất cả các bot của bạn. Tôi đã quyết định điều trị KwisatzHaderachHarkonnenBotcách tương tự như NeoBot. Họ sẽ nhận được điểm số của mình từ một mô phỏng với ít trò chơi hơn và sẽ không nằm trong mô phỏng chính thức. Tuy nhiên, họ sẽ kết thúc trong danh sách điểm cao giống như NeoBot. Lý do chính khiến họ không tham gia mô phỏng chính thức là vì họ sẽ làm rối tung các chiến lược bot khác. Tuy nhiên. WisdomOfCrowdsrất phù hợp để tham gia và tôi tò mò về những thay đổi mới mà bạn đã thực hiện!
maxb

2
class ThrowThriceBot(Bot):

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

Chà, điều đó là hiển nhiên


Tôi đã thực hiện một số thử nghiệm với lớp bot đó (đó là chiến thuật tôi đã sử dụng khi tôi chơi trò chơi lần đầu tiên). Tôi đã đi với 4 lần ném sau đó, mặc dù 5-6 có điểm trung bình cao hơn mỗi vòng.
maxb

Ngoài ra, xin chúc mừng câu trả lời đầu tiên của bạn!
maxb

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 hoạt động giống như nó luôn là vòng cuối cùng và là bot cuối cùng: nó tiếp tục lăn cho đến khi nó dẫn đầu. Nó cũng không muốn giải quyết dưới 15 điểm trừ khi thực sự là vòng cuối cùng hoặc đạt 40 điểm.


Cách tiếp cận thú vị. Tôi nghĩ bot của bạn bị ảnh hưởng nếu nó bắt đầu tụt lại phía sau. Vì tỷ lệ nhận được> 30 điểm trong một vòng duy nhất là thấp, bot của bạn có nhiều khả năng giữ nguyên số điểm hiện tại.
maxb

1
Tôi nghi ngờ điều này mắc phải lỗi tương tự mà tôi đã mắc phải (xem bình luận của NotTooFarBehindBot) - như trong vòng trước, nếu bạn không chiến thắng, bạn sẽ tiếp tục ném cho đến khi bạn nhận được 6 (điểm [self.index] không bao giờ cập nhật) Bạn có bất bình đẳng đó sai cách? tối đa (điểm) sẽ luôn là> = điểm [self.index]
Stuart Moore

@StuartMoore Haha, vâng, tôi nghĩ bạn đúng. Cảm ơn!
Spitemaster 19/12/18

Tôi nghi ngờ bạn muốn "và last_round" vào lần thứ 2 để làm những gì bạn muốn - nếu không thì lần thứ 2 sẽ được sử dụng cho dù Last_round có đúng hay không
Stuart Moore

3
Đó là cố ý. Nó luôn cố gắng dẫn đầu khi kết thúc lượt của mình.
Spitemaster 19/12/18

2

Hạn ngạch

Một hệ thống "hạn ngạch" ngây thơ mà tôi đã triển khai, mà thực sự có vẻ như đạt điểm khá cao về tổng thể.

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:đưa ra một lỗi cho tôi. Ngoài rawhile sum(self.current_throws)
Spitemaster 19/12/18

@Spitemaster là một lỗi dán vào trao đổi ngăn xếp, nên hoạt động ngay bây giờ.
FlipTack

@Spitemaster đó là vì đã có <>các biểu tượng can thiệp vào các <pre>thẻ tôi đang sử dụng
FlipTack

2

Mong đợi

Chỉ cần chơi thẳng, tính toán giá trị mong đợi cho lần ném xúc xắc và chỉ thực hiện nếu nó tích cực.

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

Tôi gặp sự cố khi chạy bộ điều khiển, nhận được "NameError: name 'bot_per_game' không được xác định" trên thiết bị đa luồng, vì vậy thực sự không biết cái này hoạt động như thế nào.


1
Tôi nghĩ rằng điều này cuối cùng tương đương với bot "Tới 16", nhưng chúng ta chưa có một trong số đó
Stuart Moore

1
@StuartMoore Đó ... là một điểm rất đúng, vâng
Cain

Tôi gặp vấn đề với bộ điều khiển khi tôi chạy nó trên máy Windows. Bằng cách nào đó nó chạy tốt trên máy Linux của tôi. Tôi đang cập nhật bộ điều khiển và sẽ cập nhật bài viết sau khi hoàn thành.
maxb

@maxb Cảm ơn, có lẽ một cái gì đó về các biến có sẵn trong quá trình khác nhau. FYI cũng cập nhật điều này, tôi đã mắc một lỗi ngớ ngẩn xung quanh năng suất: /
Cain

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

Bốn mươi tuổi

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

Cố gắng cho 14 điểm cho đến vòng cuối cùng, sau đó giả sử mọi người khác sẽ cố gắng cho 14 điểm và cố gắng buộc số điểm đó.


Tôi đã nhận TypeError: unsupported operand type(s) for -: 'list' and 'int'với bot của bạn.
tsh

Tôi cho rằng bạn max_projected_scorenên là tối đa của danh sách chứ không phải toàn bộ danh sách, tôi có đúng không? Nếu không, tôi nhận được cùng một vấn đề như tsh.
maxb

Rất tiếc, chỉnh sửa để sửa chữa.
lịch sử

2

Do dự

Có hai bước khiêm tốn, sau đó chờ người khác băng qua đường. Phiên bản cập nhật không còn cố gắng đánh bại điểm cao, chỉ muốn tiếp cận nó - cải thiện hiệu suất bằng cách xóa hai byte mã nguồn!

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

Phiến quân

Bot này kết hợp chiến lược đơn giản Hesitate với chiến lược vòng cuối tiên tiến BotFor2X, cố gắng nhớ nó là ai và phát điên khi thấy nó sống trong ảo ảnh.

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)

Vâng, điều này khá thanh lịch :) Ngoài ra, xin chúc mừng bạn đã có được cả vị trí thứ nhất và thứ hai trong cuộc thi chính!
Dani O

Đương nhiên, tôi đã điều chỉnh HarkonnenBotđể Rebelkhông còn có thể tự hủy;) và tôi cũng đã điều chỉnh TleilaxuBotđể Rebelkhông phát hiện ra nữa!
Dani O

1

Lấy năm cái

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

Một nửa thời gian, chúng tôi sẽ cán 5 trước 6. Khi chúng tôi làm, rút ​​tiền.


Nếu chúng ta dừng lại ở 1 thay vào đó, nó sẽ tiến triển chậm hơn, nhưng nhiều khả năng sẽ đạt đến 40 trong một ràng buộc duy nhất.
Mnemonic

Trong các thử nghiệm của tôi, TakeOne đạt 20.868 điểm mỗi vòng so với 24.262 điểm của TakeFive mỗi vòng (và cũng mang lại tỷ lệ thắng từ 0.291 đến 0.259). Vì vậy, tôi không nghĩ rằng nó có giá trị nó.
Spitemaster

1

Máy đuổi

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 cố gắng bắt kịp vị trí số 1 Nếu là vòng cuối cùng, anh ta cố gắng đạt được ít nhất 50 điểm Chỉ vì biện pháp tốt, anh ta ném ít nhất bốn lần bất kể là gì

[chỉnh sửa 1: thêm chiến lược đi tìm vàng trong vòng cuối cùng]

[chỉnh sửa 2: logic được cập nhật vì tôi nhầm tưởng một bot sẽ đạt điểm 40 thay vì chỉ có điểm bot cao nhất]

[chỉnh sửa 3: làm cho chaser phòng thủ hơn một chút trong trò chơi kết thúc]


Chào mừng đến với PPCG! Ý tưởng gọn gàng để không chỉ cố gắng để bắt kịp, mà còn vượt qua vị trí đầu tiên. Tôi đang chạy một mô phỏng ngay bây giờ, và tôi chúc bạn may mắn!
maxb

Cảm ơn! Ban đầu tôi đã cố gắng vượt qua người lãnh đạo trước đó bằng một số tiền cố định (đã thử các giá trị trong khoảng từ 6 đến 20) nhưng hóa ra chỉ cần ném hai lần hội chợ tốt hơn.
AKroell


1

Tương lai

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

Một cặp bot, họ mang theo bộ xúc xắc của riêng mình và cuộn chúng để dự đoán tương lai. Nếu một trong số 6 họ dừng lại, FutureBot không thể nhớ đó là 2 con xúc xắc nào cho lần lăn tiếp theo để nó bỏ cuộc.

Tôi tự hỏi cái nào sẽ làm tốt hơn.

OneStepAhead hơi giống với OneInFive đối với sở thích của tôi, nhưng tôi cũng muốn xem nó so sánh với FutureBot và OneInFive như thế nào.

Chỉnh sửa: Bây giờ họ dừng lại sau khi nhấn 45


Chào mừng đến với PPCG! Bot của bạn chắc chắn chơi với tinh thần của trò chơi! Tôi sẽ chạy một mô phỏng vào tối nay.
maxb

Cảm ơn! Tôi tò mò về việc nó sẽ làm tốt như thế nào, nhưng tôi đoán nó sẽ ở phía thấp.
porter william
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.