주사위 게임이지만 6 번을 피하십시오.


58

토너먼트 오버!

토너먼트가 끝났습니다! 최종 시뮬레이션은 밤 동안 총 게임으로 진행되었습니다. 우승자는 봇 OptFor2X함께한 크리스천 시버 입니다 . Christian Sievers는 Rebel을 통해 2 위를 차지했습니다 . 축하합니다! 아래는 토너먼트의 공식 최고 점수 목록입니다.3108

여전히 게임을 즐기고 싶다면 아래에 게시 된 컨트롤러를 사용하고 자신의 게임을 만드는 데 코드를 사용하는 것이 좋습니다.

주사위

나는 들어 본 적이없는 주사위 게임을하도록 초대 받았습니다. 규칙은 간단했지만 KotH 도전에 완벽하다고 생각합니다.

규칙

게임의 시작

주사위는 테이블 주위를 돌아 다니며, 차례가 될 때마다 원하는만큼 주사위를 던져야합니다. 그러나 적어도 한 번은 던져야합니다. 당신은 당신의 라운드에 대한 모든 던져의 합계를 추적합니다. 중지하기로 선택하면 해당 라운드의 점수가 총 점수에 추가됩니다.

그렇다면 왜 주사위 던지기를 그만두겠습니까? 6을 얻으면 전체 라운드의 점수가 0이되고 주사위가 통과합니다. 따라서 초기 목표는 가능한 빨리 점수를 높이는 것입니다.

승자는 누구입니까?

테이블 주위의 첫 번째 플레이어가 40 점 이상에 도달하면 마지막 라운드가 시작됩니다. 마지막 라운드가 시작되면 마지막 라운드를 시작한 사람을 제외한 모든 사람이 한 번 더 회전합니다.

마지막 라운드의 규칙은 다른 라운드와 동일합니다. 계속 던지거나 멈추도록 선택합니다. 그러나, 당신은 당신이 마지막 라운드에서 이전보다 높은 점수를 얻지 못하면 이길 확률이 없다는 것을 알고 있습니다. 그러나 계속 너무 멀리 가면 6이 나올 수 있습니다.

그러나 고려해야 할 규칙이 하나 더 있습니다. 현재 총 점수 (이전 점수 + 라운드의 현재 점수)가 40 이상이고 6에 도달하면 총 점수 가 0으로 설정됩니다. 즉 , 전체 를 다시 시작해야합니다. 현재 총 점수가 40 이상일 때 6을 기록하면 게임은 현재 마지막 위치에 있다는 점을 제외하고 정상적으로 계속 진행됩니다. 총 점수가 재설정 될 때 마지막 라운드는 트리거되지 않습니다. 당신은 여전히 ​​라운드에서 이길 수는 있지만 더 어려워집니다.

마지막 라운드가 끝나면 승자가 가장 높은 점수를받습니다. 둘 이상의 플레이어가 같은 점수를 공유하면 모두 승자로 계산됩니다.

추가 된 규칙은 게임이 최대 200 라운드 동안 계속된다는 것입니다. 이것은 여러 봇이 기본적으로 6을 기록 할 때까지 현재 점수를 유지하는 경우를 방지하기위한 것입니다. 199 번째 라운드가 끝나면 last_roundtrue로 설정되고 한 번 더 라운드가 재생됩니다. 게임이 200 라운드로 진행되면 40 점 이상이 없어도 가장 높은 점수를 얻은 봇이 승자가됩니다.

요약

  • 매 라운드마다 당신이 멈추거나 6을 얻을 때까지 계속 주사위를 던집니다.
  • 주사위를 한 번 던져야합니다 (첫 번째 던지기가 6이면 라운드가 즉시 끝납니다)
  • 6 점을 받으면 현재 점수가 0으로 설정됩니다 (총 점수 아님)
  • 매 라운드 후 현재 점수를 총 점수에 추가합니다.
  • 봇이 턴을 끝내면 총점이 40 점 이상이면 다른 모든 사람이 마지막 턴을 얻습니다
  • 현재 총 점수가 이고 6을 받으면 총 점수가 0으로 설정되고 라운드가 끝납니다.40
  • 위의 경우 마지막 라운드가 트리거되지 않습니다
  • 마지막 라운드 이후 총점이 가장 높은 사람이 승자입니다.
  • 수상자가 여러 명인 경우 모두 수상자로 계산됩니다
  • 게임은 최대 200 라운드 동안 지속됩니다

점수의 명확화

  • 총점 : 이전 라운드에서 저장 한 점수
  • 현재 점수 : 현재 라운드의 점수
  • 현재 총점 : 위 두 점수의 합

참여 방법

이 KotH 챌린지에 참여하려면에서 상속받은 Python 클래스를 작성해야합니다 Bot. 함수를 구현해야합니다 make_throw(self, scores, last_round).. 당신의 차례가되면 그 함수가 호출 될 것이고, 첫 스로우는 6이 아니 었습니다 yield True. 던지기를 멈추려면 yield False. 각각의 던져 후, 부모 함수 update_state가 호출됩니다. 따라서 변수를 사용하여 현재 라운드의 던지기에 액세스 할 수 있습니다 self.current_throws. 를 사용하여 자신의 색인에 액세스 할 수도 있습니다 self.index. 따라서 자신의 총점을 보려면을 사용하십시오 scores[self.index]. end_score을 사용하여 게임에 액세스 할 수도 self.end_score있지만이 챌린지에서 40이 될 것이라고 안전하게 가정 할 수 있습니다.

수업 내에서 도우미 기능을 만들 수 있습니다. Bot예를 들어 클래스 속성을 더 추가하려는 경우 부모 클래스 에있는 함수를 재정의 할 수도 있습니다 . 당신은 항복 이외 방법으로 게임의 상태를 수정할 수 없습니다 TrueFalse.

이 게시물에서 영감을 얻고 여기에 포함 된 두 봇 중 하나를 복사 할 수 있습니다. 그러나 그들이 효과적이지 않을까 걱정됩니다 ...

다른 언어 허용

샌드 박스와 The Nineteenth Byte에서 다른 언어로 제출할 수있는 방법에 대해 논의했습니다. 그러한 구현에 대해 읽고 양측의 주장을 듣고 난 후에이 과제를 Python으로 만 제한하기로 결정했습니다. 이는 여러 언어를 지원하는 데 필요한 시간과 안정성에 도달하기 위해 많은 반복이 필요한이 난제의 임의성이라는 두 가지 요인 때문입니다. 나는 당신이 여전히 참여하기를 희망하며,이 도전에 대한 파이썬을 배우고 싶다면 가능한 한 자주 채팅에서 사용할 수 있도록 노력할 것입니다.

궁금한 점이 있으면 채팅방에이 도전 과제를 적을 수 있습니다 . 거기서 보자!

규칙

  • 방해 행위가 허용되고 권장됩니다. 즉, 다른 플레이어에 대한 방해
  • 컨트롤러, 런타임 또는 기타 제출물을 수정하려는 시도는 실격 처리됩니다. 모든 제출물은 제공된 입력 및 저장 장치에서만 작동해야합니다.
  • 500MB 이상의 메모리를 사용하여 결정을 내리는 모든 봇은 실격 처리됩니다 (많은 메모리가 필요한 경우 선택 사항을 다시 생각해야 함).
  • 봇은 의도적으로 또는 실수로 기존 전략과 정확히 동일한 전략을 구현해서는 안됩니다.
  • 챌린지 시간 동안 봇을 업데이트 할 수 있습니다. 그러나 접근 방식이 다른 경우 다른 봇을 게시 할 수도 있습니다.

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

이 봇은 라운드 적어도 10의 점수가 될 때까지 계속됩니다, 또는 당신이 6. 또한 첫 번째 던져가 6 인 경우주의 던지는 처리 할 수있는 논리를 필요로하지 않는 6 주를 throw make_throw이다 당신의 라운드가 즉시 끝나기 때문에 전화하지 마십시오.

파이썬에 익숙하지 않고 yield개념 에 익숙하지 않은 사람들에게 yield키워드는 어떤 식 으로든 수익과 비슷하지만 다른 방식으로 다릅니다. 여기서 개념에 대해 읽을 수 있습니다 . 기본적으로, 일단 yield기능이 중지되면 yielded 값이 컨트롤러로 다시 전송됩니다. 여기서 컨트롤러는 봇이 다른 결정을 내릴 때까지 논리를 처리합니다. 그런 다음 컨트롤러는 주사위 던지기를 보내며 make_throw이전에 중단 된 경우 기본적으로 이전 yield명령문 다음 줄에서 함수가 계속 실행됩니다 .

이런 식으로 게임 컨트롤러는 각 주사위 던지기마다 별도의 봇 함수 호출없이 상태를 업데이트 할 수 있습니다.

사양

에서 사용 가능한 모든 Python 라이브러리를 사용할 수 있습니다 pip. 좋은 평균을 얻을 수 있도록 라운드 당 100 밀리 초의 시간 제한이 있습니다. 나는 당신의 스크립트가 그것보다 훨씬 빠르면 더 기뻐할 것입니다.

평가

승자를 찾기 위해 모든 봇을 가져와 무작위 그룹 8로 실행합니다. 제출 된 클래스가 8 개 미만인 경우 4 개의 무작위 그룹으로 실행하여 각 라운드에 모든 봇이 항상있는 것을 피합니다. 약 8 시간 동안 시뮬레이션을 실행하면 승자가 가장 높은 승률의 봇이됩니다. 2019 년 초에 최종 시뮬레이션을 시작하여 봇을 코딩하는 모든 크리스마스를 제공합니다! 예비 최종 날짜는 1 월 4 일이지만, 시간이 너무 적 으면 나중에 날짜를 변경할 수 있습니다.

그때까지 나는 30-60 분의 CPU 시간을 사용하고 스코어 보드를 업데이트하여 매일 시뮬레이션을 시도합니다. 이것은 공식 점수는 아니지만 어떤 봇이 가장 실적이 좋은지 확인하는 가이드 역할을합니다. 그러나 크리스마스가 다가 오면 항상 사용할 수 없다는 것을 이해할 수 있기를 바랍니다. 시뮬레이션을 실행하고 도전과 관련된 질문에 대답하기 위해 최선을 다하겠습니다.

직접 테스트

자체 시뮬레이션을 실행하려는 경우 두 개의 예제 봇을 포함하여 시뮬레이션을 실행하는 컨트롤러에 대한 전체 코드가 있습니다.

제어 장치

이 도전에 대한 업데이트 된 컨트롤러는 다음과 같습니다. AKroell 덕분에 ANSI 출력, 멀티 스레딩을 지원하고 추가 통계를 수집합니다 ! 컨트롤러를 변경하면 문서가 완료되면 게시물을 업데이트합니다.

BMO 덕분에 이제 컨트롤러는 -d깃발을 사용하여이 게시물에서 모든 봇을 다운로드 할 수 있습니다. 이 버전에서는 다른 기능이 변경되지 않았습니다. 이렇게하면 모든 최신 변경 사항이 최대한 빨리 시뮬레이션됩니다!

#!/usr/bin/env python3
import re
import json
import math
import random
import requests
import sys
import time
from numpy import cumsum

from collections import defaultdict
from html import unescape
from lxml import html
from multiprocessing import Pool
from os import path, rename, remove
from sys import stderr
from time import strftime

# If you want to see what each bot decides, set this to true
# Should only be used with one thread and one game
DEBUG = False
# If your terminal supports ANSI, try setting this to true
ANSI = False
# File to keep base class and own bots
OWN_FILE = 'forty_game_bots.py'
# File where to store the downloaded bots
AUTO_FILE = 'auto_bots.py'
# If you want to use up all your quota & re-download all bots
DOWNLOAD = False
# If you want to ignore a specific user's bots (eg. your own bots): add to list
IGNORE = []
# The API-request to get all the bots
URL = "https://api.stackexchange.com/2.2/questions/177765/answers?page=%s&pagesize=100&order=desc&sort=creation&site=codegolf&filter=!bLf7Wx_BfZlJ7X"


def print_str(x, y, string):
    print("\033["+str(y)+";"+str(x)+"H"+string, end = "", flush = True)

class bcolors:
    WHITE = '\033[0m'
    GREEN = '\033[92m'
    BLUE = '\033[94m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    ENDC = '\033[0m'

# Class for handling the game logic and relaying information to the bots
class Controller:

    def __init__(self, bots_per_game, games, bots, thread_id):
        """Initiates all fields relevant to the simulation

        Keyword arguments:
        bots_per_game -- the number of bots that should be included in a game
        games -- the number of games that should be simulated
        bots -- a list of all available bot classes
        """
        self.bots_per_game = bots_per_game
        self.games = games
        self.bots = bots
        self.number_of_bots = len(self.bots)
        self.wins = defaultdict(int)
        self.played_games = defaultdict(int)
        self.bot_timings = defaultdict(float)
        # self.wins = {bot.__name__: 0 for bot in self.bots}
        # self.played_games = {bot.__name__: 0 for bot in self.bots}
        self.end_score = 40
        self.thread_id = thread_id
        self.max_rounds = 200
        self.timed_out_games = 0
        self.tied_games = 0
        self.total_rounds = 0
        self.highest_round = 0
        #max, avg, avg_win, throws, success, rounds
        self.highscore = defaultdict(lambda:[0, 0, 0, 0, 0, 0])
        self.winning_scores = defaultdict(int)
        # self.highscore = {bot.__name__: [0, 0, 0] for bot in self.bots}

    # Returns a fair dice throw
    def throw_die(self):
        return random.randint(1,6)
    # Print the current game number without newline
    def print_progress(self, progress):
        length = 50
        filled = int(progress*length)
        fill = "="*filled
        space = " "*(length-filled)
        perc = int(100*progress)
        if ANSI:
            col = [
                bcolors.RED, 
                bcolors.YELLOW, 
                bcolors.WHITE, 
                bcolors.BLUE, 
                bcolors.GREEN
            ][int(progress*4)]

            end = bcolors.ENDC
            print_str(5, 8 + self.thread_id, 
                "\t%s[%s%s] %3d%%%s" % (col, fill, space, perc, end)
            )
        else:
            print(
                "\r\t[%s%s] %3d%%" % (fill, space, perc),
                flush = True, 
                end = ""
            )

    # Handles selecting bots for each game, and counting how many times
    # each bot has participated in a game
    def simulate_games(self):
        for game in range(self.games):
            if self.games > 100:
                if game % (self.games // 100) == 0 and not DEBUG:
                    if self.thread_id == 0 or ANSI:
                        progress = (game+1) / self.games
                        self.print_progress(progress)
            game_bot_indices = random.sample(
                range(self.number_of_bots), 
                self.bots_per_game
            )

            game_bots = [None for _ in range(self.bots_per_game)]
            for i, bot_index in enumerate(game_bot_indices):
                self.played_games[self.bots[bot_index].__name__] += 1
                game_bots[i] = self.bots[bot_index](i, self.end_score)

            self.play(game_bots)
        if not DEBUG and (ANSI or self.thread_id == 0):
            self.print_progress(1)

        self.collect_results()

    def play(self, game_bots):
        """Simulates a single game between the bots present in game_bots

        Keyword arguments:
        game_bots -- A list of instantiated bot objects for the game
        """
        last_round = False
        last_round_initiator = -1
        round_number = 0
        game_scores = [0 for _ in range(self.bots_per_game)]


        # continue until one bot has reached end_score points
        while not last_round:
            for index, bot in enumerate(game_bots):
                t0 = time.clock()
                self.single_bot(index, bot, game_scores, last_round)
                t1 = time.clock()
                self.bot_timings[bot.__class__.__name__] += t1-t0

                if game_scores[index] >= self.end_score and not last_round:
                    last_round = True
                    last_round_initiator = index
            round_number += 1

            # maximum of 200 rounds per game
            if round_number > self.max_rounds - 1:
                last_round = True
                self.timed_out_games += 1
                # this ensures that everyone gets their last turn
                last_round_initiator = self.bots_per_game

        # make sure that all bots get their last round
        for index, bot in enumerate(game_bots[:last_round_initiator]):
            t0 = time.clock()
            self.single_bot(index, bot, game_scores, last_round)
            t1 = time.clock()
            self.bot_timings[bot.__class__.__name__] += t1-t0

        # calculate which bots have the highest score
        max_score = max(game_scores)
        nr_of_winners = 0
        for i in range(self.bots_per_game):
            bot_name = game_bots[i].__class__.__name__
            # average score per bot
            self.highscore[bot_name][1] += game_scores[i]
            if self.highscore[bot_name][0] < game_scores[i]:
                # maximum score per bot
                self.highscore[bot_name][0] = game_scores[i]
            if game_scores[i] == max_score:
                # average winning score per bot
                self.highscore[bot_name][2] += game_scores[i]
                nr_of_winners += 1
                self.wins[bot_name] += 1
        if nr_of_winners > 1:
            self.tied_games += 1
        self.total_rounds += round_number
        self.highest_round = max(self.highest_round, round_number)
        self.winning_scores[max_score] += 1

    def single_bot(self, index, bot, game_scores, last_round):
        """Simulates a single round for one bot

        Keyword arguments:
        index -- The player index of the bot (e.g. 0 if the bot goes first)
        bot -- The bot object about to be simulated
        game_scores -- A list of ints containing the scores of all players
        last_round -- Boolean describing whether it is currently the last round
        """

        current_throws = [self.throw_die()]
        if current_throws[-1] != 6:

            bot.update_state(current_throws[:])
            for throw in bot.make_throw(game_scores[:], last_round):
                # send the last die cast to the bot
                if not throw:
                    break
                current_throws.append(self.throw_die())
                if current_throws[-1] == 6:
                    break
                bot.update_state(current_throws[:])

        if current_throws[-1] == 6:
            # reset total score if running total is above end_score
            if game_scores[index] + sum(current_throws) - 6 >= self.end_score:
                game_scores[index] = 0
        else:
            # add to total score if no 6 is cast
            game_scores[index] += sum(current_throws)

        if DEBUG:
            desc = "%d: Bot %24s plays %40s with " + \
            "scores %30s and last round == %5s"
            print(desc % (index, bot.__class__.__name__, 
                current_throws, game_scores, last_round))

        bot_name = bot.__class__.__name__
        # average throws per round
        self.highscore[bot_name][3] += len(current_throws)
        # average success rate per round
        self.highscore[bot_name][4] += int(current_throws[-1] != 6)
        # total number of rounds
        self.highscore[bot_name][5] += 1


    # Collects all stats for the thread, so they can be summed up later
    def collect_results(self):
        self.bot_stats = {
            bot.__name__: [
                self.wins[bot.__name__],
                self.played_games[bot.__name__],
                self.highscore[bot.__name__]
            ]
        for bot in self.bots}


# 
def print_results(total_bot_stats, total_game_stats, elapsed_time):
    """Print the high score after the simulation

    Keyword arguments:
    total_bot_stats -- A list containing the winning stats for each thread
    total_game_stats -- A list containing controller stats for each thread
    elapsed_time -- The number of seconds that it took to run the simulation
    """

    # Find the name of each bot, the number of wins, the number
    # of played games, and the win percentage
    wins = defaultdict(int)
    played_games = defaultdict(int)
    highscores = defaultdict(lambda: [0, 0, 0, 0, 0, 0])
    bots = set()
    timed_out_games = sum(s[0] for s in total_game_stats)
    tied_games = sum(s[1] for s in total_game_stats)
    total_games = sum(s[2] for s in total_game_stats)
    total_rounds = sum(s[4] for s in total_game_stats)
    highest_round = max(s[5] for s in total_game_stats)
    average_rounds = total_rounds / total_games
    winning_scores = defaultdict(int)
    bot_timings = defaultdict(float)

    for stats in total_game_stats:
        for score, count in stats[6].items():
            winning_scores[score] += count
    percentiles = calculate_percentiles(winning_scores, total_games)


    for thread in total_bot_stats:
        for bot, stats in thread.items():
            wins[bot] += stats[0]
            played_games[bot] += stats[1]

            highscores[bot][0] = max(highscores[bot][0], stats[2][0])       
            for i in range(1, 6):
                highscores[bot][i] += stats[2][i]
            bots.add(bot)

    for bot in bots:
        bot_timings[bot] += sum(s[3][bot] for s in total_game_stats)

    bot_stats = [[bot, wins[bot], played_games[bot], 0] for bot in bots]

    for i, bot in enumerate(bot_stats):
        bot[3] = 100 * bot[1] / bot[2] if bot[2] > 0 else 0
        bot_stats[i] = tuple(bot)

    # Sort the bots by their winning percentage
    sorted_scores = sorted(bot_stats, key=lambda x: x[3], reverse=True)
    # Find the longest class name for any bot
    max_len = max([len(b[0]) for b in bot_stats])

    # Print the highscore list
    if ANSI:
        print_str(0, 9 + threads, "")
    else:
        print("\n")


    sim_msg = "\tSimulation or %d games between %d bots " + \
        "completed in %.1f seconds"
    print(sim_msg % (total_games, len(bots), elapsed_time))
    print("\tEach game lasted for an average of %.2f rounds" % average_rounds)
    print("\t%d games were tied between two or more bots" % tied_games)
    print("\t%d games ran until the round limit, highest round was %d\n"
        % (timed_out_games, highest_round))

    print_bot_stats(sorted_scores, max_len, highscores)
    print_score_percentiles(percentiles)
    print_time_stats(bot_timings, max_len)

def calculate_percentiles(winning_scores, total_games):
    percentile_bins = 10000
    percentiles = [0 for _ in range(percentile_bins)]
    sorted_keys = list(sorted(winning_scores.keys()))
    sorted_values = [winning_scores[key] for key in sorted_keys]
    cumsum_values = list(cumsum(sorted_values))
    i = 0

    for perc in range(percentile_bins):
        while cumsum_values[i] < total_games * (perc+1) / percentile_bins:
            i += 1
        percentiles[perc] = sorted_keys[i] 
    return percentiles

def print_score_percentiles(percentiles):
    n = len(percentiles)
    show = [.5, .75, .9, .95, .99, .999, .9999]
    print("\t+----------+-----+")
    print("\t|Percentile|Score|")
    print("\t+----------+-----+")
    for p in show:
        print("\t|%10.2f|%5d|" % (100*p, percentiles[int(p*n)]))
    print("\t+----------+-----+")
    print()


def print_bot_stats(sorted_scores, max_len, highscores):
    """Print the stats for the bots

    Keyword arguments:
    sorted_scores -- A list containing the bots in sorted order
    max_len -- The maximum name length for all bots
    highscores -- A dict with additional stats for each bot
    """
    delimiter_format = "\t+%s%s+%s+%s+%s+%s+%s+%s+%s+%s+"
    delimiter_args = ("-"*(max_len), "", "-"*4, "-"*8, 
        "-"*8, "-"*6, "-"*6, "-"*7, "-"*6, "-"*8)
    delimiter_str = delimiter_format % delimiter_args
    print(delimiter_str)
    print("\t|%s%s|%4s|%8s|%8s|%6s|%6s|%7s|%6s|%8s|" 
        % ("Bot", " "*(max_len-3), "Win%", "Wins", 
            "Played", "Max", "Avg", "Avg win", "Throws", "Success%"))
    print(delimiter_str)

    for bot, wins, played, score in sorted_scores:
        highscore = highscores[bot]
        bot_max_score = highscore[0]
        bot_avg_score = highscore[1] / played
        bot_avg_win_score = highscore[2] / max(1, wins)
        bot_avg_throws = highscore[3] / highscore[5]
        bot_success_rate = 100 * highscore[4] / highscore[5]

        space_fill = " "*(max_len-len(bot))
        format_str = "\t|%s%s|%4.1f|%8d|%8d|%6d|%6.2f|%7.2f|%6.2f|%8.2f|"
        format_arguments = (bot, space_fill, score, wins, 
            played, bot_max_score, bot_avg_score,
            bot_avg_win_score, bot_avg_throws, bot_success_rate)
        print(format_str % format_arguments)

    print(delimiter_str)
    print()

def print_time_stats(bot_timings, max_len):
    """Print the execution time for all bots

    Keyword arguments:
    bot_timings -- A dict containing information about timings for each bot
    max_len -- The maximum name length for all bots
    """
    total_time = sum(bot_timings.values())
    sorted_times = sorted(bot_timings.items(), 
        key=lambda x: x[1], reverse = True)

    delimiter_format = "\t+%s+%s+%s+"
    delimiter_args = ("-"*(max_len), "-"*7, "-"*5)
    delimiter_str = delimiter_format % delimiter_args
    print(delimiter_str)

    print("\t|%s%s|%7s|%5s|" % ("Bot", " "*(max_len-3), "Time", "Time%"))
    print(delimiter_str)
    for bot, bot_time in sorted_times:
        space_fill = " "*(max_len-len(bot))
        perc = 100 * bot_time / total_time
        print("\t|%s%s|%7.2f|%5.1f|" % (bot, space_fill, bot_time, perc))
    print(delimiter_str)
    print() 


def run_simulation(thread_id, bots_per_game, games_per_thread, bots):
    """Used by multithreading to run the simulation in parallel

    Keyword arguments:
    thread_id -- A unique identifier for each thread, starting at 0
    bots_per_game -- How many bots should participate in each game
    games_per_thread -- The number of games to be simulated
    bots -- A list of all bot classes available
    """
    try:
        controller = Controller(bots_per_game, 
            games_per_thread, bots, thread_id)
        controller.simulate_games()
        controller_stats = (
            controller.timed_out_games,
            controller.tied_games,
            controller.games,
            controller.bot_timings,
            controller.total_rounds,
            controller.highest_round,
            controller.winning_scores
        )
        return (controller.bot_stats, controller_stats)
    except KeyboardInterrupt:
        return {}


# Prints the help for the script
def print_help():
    print("\nThis is the controller for the PPCG KotH challenge " + \
        "'A game of dice, but avoid number 6'")
    print("For any question, send a message to maxb\n")
    print("Usage: python %s [OPTIONS]" % sys.argv[0])
    print("\n  -n\t\tthe number of games to simluate")
    print("  -b\t\tthe number of bots per round")
    print("  -t\t\tthe number of threads")
    print("  -d\t--download\tdownload all bots from codegolf.SE")
    print("  -A\t--ansi\trun in ANSI mode, with prettier printing")
    print("  -D\t--debug\trun in debug mode. Sets to 1 thread, 1 game")
    print("  -h\t--help\tshow this help\n")

# Make a stack-API request for the n-th page
def req(n):
    req = requests.get(URL % n)
    req.raise_for_status()
    return req.json()

# Pull all the answers via the stack-API
def get_answers():
    n = 1
    api_ans = req(n)
    answers = api_ans['items']
    while api_ans['has_more']:
        n += 1
        if api_ans['quota_remaining']:
            api_ans = req(n)
            answers += api_ans['items']
        else:
            break

    m, r = api_ans['quota_max'], api_ans['quota_remaining']
    if 0.1 * m > r:
        print(" > [WARN]: only %s/%s API-requests remaining!" % (r,m), file=stderr)

    return answers


def download_players():
    players = {}

    for ans in get_answers():
        name = unescape(ans['owner']['display_name'])
        bots = []

        root = html.fromstring('<body>%s</body>' % ans['body'])
        for el in root.findall('.//code'):
            code = el.text
            if re.search(r'^class \w+\(\w*Bot\):.*$', code, flags=re.MULTILINE):
                bots.append(code)

        if not bots:
            print(" > [WARN] user '%s': couldn't locate any bots" % name, file=stderr)
        elif name in players:
            players[name] += bots
        else:
            players[name] = bots

    return players


# Download all bots from codegolf.stackexchange.com
def download_bots():
    print('pulling bots from the interwebs..', file=stderr)
    try:
        players = download_players()
    except Exception as ex:
        print('FAILED: (%s)' % ex, file=stderr)
        exit(1)

    if path.isfile(AUTO_FILE):
        print(' > move: %s -> %s.old' % (AUTO_FILE,AUTO_FILE), file=stderr)
        if path.exists('%s.old' % AUTO_FILE):
            remove('%s.old' % AUTO_FILE)
        rename(AUTO_FILE, '%s.old' % AUTO_FILE)

    print(' > writing players to %s' % AUTO_FILE, file=stderr)
    f = open(AUTO_FILE, 'w+', encoding='utf8')
    f.write('# -*- coding: utf-8 -*- \n')
    f.write('# Bots downloaded from https://codegolf.stackexchange.com/questions/177765 @ %s\n\n' % strftime('%F %H:%M:%S'))
    with open(OWN_FILE, 'r') as bfile:
        f.write(bfile.read()+'\n\n\n# Auto-pulled bots:\n\n')
    for usr in players:
        if usr not in IGNORE:
            for bot in players[usr]:
                f.write('# User: %s\n' % usr)
                f.write(bot+'\n\n')
    f.close()

    print('OK: pulled %s bots' % sum(len(bs) for bs in players.values()))


if __name__ == "__main__":

    games = 10000
    bots_per_game = 8
    threads = 4

    for i, arg in enumerate(sys.argv):
        if arg == "-n" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit():
            games = int(sys.argv[i+1])
        if arg == "-b" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit():
            bots_per_game = int(sys.argv[i+1])
        if arg == "-t" and len(sys.argv) > i+1 and sys.argv[i+1].isdigit():
            threads = int(sys.argv[i+1])
        if arg == "-d" or arg == "--download":
            DOWNLOAD = True
        if arg == "-A" or arg == "--ansi":
            ANSI = True
        if arg == "-D" or arg == "--debug":
            DEBUG = True
        if arg == "-h" or arg == "--help":
            print_help()
            quit()
    if ANSI:
        print(chr(27) + "[2J", flush =  True)
        print_str(1,3,"")
    else:
        print()

    if DOWNLOAD:
        download_bots()
        exit() # Before running other's code, you might want to inspect it..

    if path.isfile(AUTO_FILE):
        exec('from %s import *' % AUTO_FILE[:-3])
    else:
        exec('from %s import *' % OWN_FILE[:-3])

    bots = get_all_bots()

    if bots_per_game > len(bots):
        bots_per_game = len(bots)
    if bots_per_game < 2:
        print("\tAt least 2 bots per game is needed")
        bots_per_game = 2
    if games <= 0:
        print("\tAt least 1 game is needed")
        games = 1
    if threads <= 0:
        print("\tAt least 1 thread is needed")
        threads = 1
    if DEBUG:
        print("\tRunning in debug mode, with 1 thread and 1 game")
        threads = 1
        games = 1

    games_per_thread = math.ceil(games / threads)

    print("\tStarting simulation with %d bots" % len(bots))
    sim_str = "\tSimulating %d games with %d bots per game"
    print(sim_str % (games, bots_per_game))
    print("\tRunning simulation on %d threads" % threads)
    if len(sys.argv) == 1:
        print("\tFor help running the script, use the -h flag")
    print()

    with Pool(threads) as pool:
        t0 = time.time()
        results = pool.starmap(
            run_simulation, 
            [(i, bots_per_game, games_per_thread, bots) for i in range(threads)]
        )
        t1 = time.time()
        if not DEBUG:
            total_bot_stats = [r[0] for r in results]
            total_game_stats = [r[1] for r in results]
            print_results(total_bot_stats, total_game_stats, t1-t0)

이 챌린지를 위해 원래 컨트롤러에 액세스하려는 경우 편집 기록에서 사용할 수 있습니다. 새로운 컨트롤러는 게임 실행을위한 동일한 로직을 가지고 있으며, 유일한 차이점은 성능, 통계 수집 및 더 예쁘게 인쇄하는 것입니다.

내 컴퓨터에서 봇은 파일에 보관됩니다 forty_game_bots.py. 파일에 다른 이름을 사용하는 경우 import컨트롤러 맨 위에서 명령문을 업데이트해야합니다 .

import sys, inspect
import random
import numpy as np

# Returns a list of all bot classes which inherit from the Bot class
def get_all_bots():
    return Bot.__subclasses__()

# The parent class for all bots
class Bot:

    def __init__(self, index, end_score):
        self.index = index
        self.end_score = end_score

    def update_state(self, current_throws):
        self.current_throws = current_throws

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


class ThrowTwiceBot(Bot):

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

class GoToTenBot(Bot):

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

시뮬레이션 실행

시뮬레이션을 실행하려면 위에 게시 된 두 코드 스 니펫을 두 개의 개별 파일에 저장하십시오. forty_game_controller.py와 같이 저장했습니다 forty_game_bots.py. 그런 다음 단순히 Python 구성을 사용 python forty_game_controller.py하거나 사용 python3 forty_game_controller.py하십시오. 시뮬레이션을 추가로 구성하려면 여기에서 지시를 따르거나 원하는 경우 코드로 땜질을 시도하십시오.

게임 통계

다른 봇을 고려하지 않고 특정 점수를 목표로하는 봇을 만드는 경우 다음이 승리 점수 백분위 수입니다.

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

고득점

더 많은 답변이 게시되면이 목록을 계속 업데이트하도록 노력하겠습니다. 목록의 내용은 항상 최신 시뮬레이션에서 가져옵니다. 봇 ThrowTwiceBotGoToTenBot위 코드 의 봇 이며 참조로 사용됩니다. 10 ^ 8 게임으로 시뮬레이션을했는데 약 1 시간이 걸렸습니다. 그런 다음 게임이 10 ^ 7 게임으로 달리는 것에 비해 안정성에 도달 한 것을 보았습니다. 그러나 사람들이 여전히 봇을 게시하므로 응답 빈도가 줄어들 때까지 더 이상 시뮬레이션을 수행하지 않습니다.

새 봇을 모두 추가하고 기존 봇에 대한 변경 사항을 추가하려고합니다. 내가 당신의 봇이나 당신이 가진 새로운 변화를 놓친 것 같다면, 채팅에 글을 쓰고 다음 시뮬레이션에서 가장 최신 버전을 가질 것입니다.

이제 AKroell 덕분에 각 봇에 대한 통계가 더 많이 생겼습니다 ! 세 개의 새로운 열에는 모든 게임의 최대 점수, 게임당 평균 점수 및 각 봇에서 승리했을 때의 평균 점수가 포함됩니다.

의견에서 지적했듯이, 게임 내에서 더 높은 인덱스를 가진 봇이 여분의 라운드를 얻는 게임 로직에 문제가있었습니다. 이것은 현재 수정되었으며 아래 점수가이를 반영합니다.

Simulation or 300000000 games between 49 bots completed in 35628.7 seconds
Each game lasted for an average of 3.73 rounds
29127662 games were tied between two or more bots
0 games ran until the round limit, highest round was 22

+-----------------------+----+--------+--------+------+------+-------+------+--------+
|Bot                    |Win%|    Wins|  Played|   Max|   Avg|Avg win|Throws|Success%|
+-----------------------+----+--------+--------+------+------+-------+------+--------+
|OptFor2X               |21.6|10583693|48967616|    99| 20.49|  44.37|  4.02|   33.09|
|Rebel                  |20.7|10151261|48977862|   104| 21.36|  44.25|  3.90|   35.05|
|Hesitate               |20.3| 9940220|48970815|   105| 21.42|  44.23|  3.89|   35.11|
|EnsureLead             |20.3| 9929074|48992362|   101| 20.43|  44.16|  4.50|   25.05|
|StepBot                |20.2| 9901186|48978938|    96| 20.42|  43.47|  4.56|   24.06|
|BinaryBot              |20.1| 9840684|48981088|   115| 21.01|  44.48|  3.85|   35.92|
|Roll6Timesv2           |20.1| 9831713|48982301|   101| 20.83|  43.53|  4.37|   27.15|
|AggressiveStalker      |19.9| 9767637|48979790|   110| 20.46|  44.86|  3.90|   35.04|
|FooBot                 |19.9| 9740900|48980477|   100| 22.03|  43.79|  3.91|   34.79|
|QuotaBot               |19.9| 9726944|48980023|   101| 19.96|  44.95|  4.50|   25.03|
|BePrepared             |19.8| 9715461|48978569|   112| 18.68|  47.58|  4.30|   28.31|
|AdaptiveRoller         |19.7| 9659023|48982819|   107| 20.70|  43.27|  4.51|   24.81|
|GoTo20Bot              |19.6| 9597515|48973425|   108| 21.15|  43.24|  4.44|   25.98|
|Gladiolen              |19.5| 9550368|48970506|   107| 20.16|  45.31|  3.91|   34.81|
|LastRound              |19.4| 9509645|48988860|   100| 20.45|  43.50|  4.20|   29.98|
|BrainBot               |19.4| 9500957|48985984|   105| 19.26|  45.56|  4.46|   25.71|
|GoTo20orBestBot        |19.4| 9487725|48975944|   104| 20.98|  44.09|  4.46|   25.73|
|Stalker                |19.4| 9485631|48969437|   103| 20.20|  45.34|  3.80|   36.62|
|ClunkyChicken          |19.1| 9354294|48972986|   112| 21.14|  45.44|  3.57|   40.48|
|FortyTeen              |18.8| 9185135|48980498|   107| 20.90|  46.77|  3.88|   35.32|
|Crush                  |18.6| 9115418|48985778|    96| 14.82|  43.08|  5.15|   14.15|
|Chaser                 |18.6| 9109636|48986188|   107| 19.52|  45.62|  4.06|   32.39|
|MatchLeaderBot         |16.6| 8122985|48979024|   104| 18.61|  45.00|  3.20|   46.70|
|Ro                     |16.5| 8063156|48972140|   108| 13.74|  48.24|  5.07|   15.44|
|TakeFive               |16.1| 7906552|48994992|   100| 19.38|  44.68|  3.36|   43.96|
|RollForLuckBot         |16.1| 7901601|48983545|   109| 17.30|  50.54|  4.72|   21.30|
|Alpha                  |15.5| 7584770|48985795|   104| 17.45|  46.64|  4.04|   32.67|
|GoHomeBot              |15.1| 7418649|48974928|    44| 13.23|  41.41|  5.49|    8.52|
|LeadBy5Bot             |15.0| 7354458|48987017|   110| 17.15|  46.95|  4.13|   31.16|
|NotTooFarBehindBot     |15.0| 7338828|48965720|   115| 17.75|  45.03|  2.99|   50.23|
|GoToSeventeenRollTenBot|14.1| 6900832|48976440|   104| 10.26|  49.25|  5.68|    5.42|
|LizduadacBot           |14.0| 6833125|48978161|    96|  9.67|  51.35|  5.72|    4.68|
|TleilaxuBot            |13.5| 6603853|48985292|   137| 15.25|  45.05|  4.27|   28.80|
|BringMyOwn_dice        |12.0| 5870328|48974969|    44| 21.27|  41.47|  4.24|   29.30|
|SafetyNet              |11.4| 5600688|48987015|    98| 15.81|  45.03|  2.41|   59.84|
|WhereFourArtThouChicken|10.5| 5157324|48976428|    64| 22.38|  47.39|  3.59|   40.19|
|ExpectationsBot        | 9.0| 4416154|48976485|    44| 24.40|  41.55|  3.58|   40.41|
|OneStepAheadBot        | 8.4| 4132031|48975605|    50| 18.24|  46.02|  3.20|   46.59|
|GoBigEarly             | 6.6| 3218181|48991348|    49| 20.77|  42.95|  3.90|   35.05|
|OneInFiveBot           | 5.8| 2826326|48974364|   155| 17.26|  49.72|  3.00|   50.00|
|ThrowThriceBot         | 4.1| 1994569|48984367|    54| 21.70|  44.55|  2.53|   57.88|
|FutureBot              | 4.0| 1978660|48985814|    50| 17.93|  45.17|  2.36|   60.70|
|GamblersFallacy        | 1.3|  621945|48986528|    44| 22.52|  41.46|  2.82|   53.07|
|FlipCoinRollDice       | 0.7|  345385|48972339|    87| 15.29|  44.55|  1.61|   73.17|
|BlessRNG               | 0.2|   73506|48974185|    49| 14.54|  42.72|  1.42|   76.39|
|StopBot                | 0.0|    1353|48984828|    44| 10.92|  41.57|  1.00|   83.33|
|CooperativeSwarmBot    | 0.0|     991|48970284|    44| 10.13|  41.51|  1.36|   77.30|
|PointsAreForNerdsBot   | 0.0|       0|48986508|     0|  0.00|   0.00|  6.00|    0.00|
|SlowStart              | 0.0|       0|48973613|    35|  5.22|   0.00|  3.16|   47.39|
+-----------------------+----+--------+--------+------+------+-------+------+--------+

다음 봇 (제외 Rebel)은 규칙을 구부리도록 만들어졌으며 제작자는 공식 토너먼트에 참여하지 않기로 동의했습니다. 그러나 나는 여전히 그들의 아이디어가 창의적이라고 생각하며 존경 할만한 언급을 할 만하다. 반란군은 사보타지를 피하기 위해 영리한 전략을 사용하고 실제로 사보타주 봇이 더 잘 작동하기 때문에이 목록에 있습니다.

NeoBotKwisatzHaderach규칙을 준수 않지만, 임의의 발생을 예측하여 허점을 사용합니다. 이 봇은 시뮬레이션하는 데 많은 리소스가 필요하므로 게임 수가 적은 시뮬레이션에서 통계를 추가했습니다. 봇 HarkonnenBot은 규칙에 위배되는 다른 모든 봇을 비활성화하여 승리를 달성합니다.

    Simulation or 300000 games between 52 bots completed in 66.2 seconds
    Each game lasted for an average of 4.82 rounds
    20709 games were tied between two or more bots
    0 games ran until the round limit, highest round was 31

    +-----------------------+----+--------+--------+------+------+-------+------+--------+
    |Bot                    |Win%|    Wins|  Played|   Max|   Avg|Avg win|Throws|Success%|
    +-----------------------+----+--------+--------+------+------+-------+------+--------+
    |KwisatzHaderach        |80.4|   36986|   46015|   214| 58.19|  64.89| 11.90|   42.09|
    |HarkonnenBot           |76.0|   35152|   46264|    44| 34.04|  41.34|  1.00|   83.20|
    |NeoBot                 |39.0|   17980|   46143|   214| 37.82|  59.55|  5.44|   50.21|
    |Rebel                  |26.8|   12410|   46306|    92| 20.82|  43.39|  3.80|   35.84|
    +-----------------------+----+--------+--------+------+------+-------+------+--------+

    +----------+-----+
    |Percentile|Score|
    +----------+-----+
    |     50.00|   45|
    |     75.00|   50|
    |     90.00|   59|
    |     95.00|   70|
    |     99.00|   97|
    |     99.90|  138|
    |     99.99|  214|
    +----------+-----+

2
"플레이어가 적어도 40 점으로 턴을 끝내면 다른 모든 사람이 마지막 턴을 얻습니다"라고 말하면 규칙이 약간 더 명확 해집니다. 이것은 마지막 라운드를 실제로 유발하는 40에 도달하지 못하고 40 이상으로
중지됨

1
@aschepler 그것은 좋은 공식입니다, 나는 내 컴퓨터에있을 때 게시물을 편집합니다
maxb

2
@maxb 나는 내 개발 과정과 관련된 통계를 더 추가하기 위해 컨트롤러를 확장했습니다 : 최고 점수, 평균 점수 및 평균 승점 gist.github.com/AwK/91446718a46f3e001c19533298b5756c
AKroell

2
이 Farkled라는 아주 재미있는 주사위 게임과 매우 유사한 소리 en.wikipedia.org/wiki/Farkle
갈렙 제이

5
이미 드 사실상 있기 때문에 새로운 답변 ( "! 대회, 3 개 * 108 게임의 총 현재를 통해 최종 시뮬레이션을 야간에 실행 된 것입니다") 폐쇄 나는이 질문을 닫으 투표 해요
pppery

답변:


6

OptFor2X

이 봇은 점수와 최고 상대의 점수 만 사용하여이 게임의 두 플레이어 버전에 대한 최적의 전략에 대한 근사치를 따릅니다. 마지막 라운드에서 업데이트 된 버전은 모든 점수를 고려합니다.

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

최대한 빨리 구현을 살펴 보겠습니다. 크리스마스 축하 행사와 함께, 그것은
maxb

당신의 봇이 선두에 있습니다! 또한 더 빨리 실행할 필요가 없으며 다른 모든 봇이 결정을 내릴 때처럼 빠릅니다.
maxb

나는 그것을 더 빨리 만들고 싶지 않았습니다. 나는 이미하고 싶었던 일을 한 번만 초기화했지만 특히 클래스 외부의 함수를 정의하지 않고 더 좋은 방법을 찾고있었습니다. 나는 지금 그것이 더 낫다고 생각합니다.
Christian Sievers

지금은 훨씬 나아 보입니다.
maxb

1 위와 2 위를 모두 축하합니다!
14:55에 maxb

20

네오 봇

대신, 진실을 깨닫기 위해 노력하십시오-숟가락이 없습니다

NeoBot은 매트릭스 (일명 무작위)를 들여다보고 다음 롤이 6이 될지 여부를 예측합니다-6으로 시작하는 것에 대해서는 아무것도 할 수 없지만 행진을 피하는 것이 행복합니다.

NeoBot은 실제로 컨트롤러 또는 런타임을 수정하지 않고 정중하게 라이브러리에 자세한 정보를 요청합니다.

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
PPCG에 오신 것을 환영합니다! 이것은 정말 인상적인 답변입니다. 처음 실행했을 때 다른 모든 봇을 결합한 것과 동일한 양의 런타임을 사용했다는 사실 때문에 걱정했습니다. 그런 다음 나는 승리 율을 보았다. 규칙을 지키는 정말 영리한 방법입니다. 나는 당신의 봇이 토너먼트에 참여할 수 있도록 허용 할 것이지만, 다른 사람들이 게임의 정신에 위배되는 것과 같은 전술을 사용하지 않기를 바랍니다.
maxb

2
이 봇과 2 위 사이에 엄청난 차이가 있기 때문에 봇에 많은 컴퓨팅이 필요하다는 사실과 결합하여 승리 율을 찾기 위해 더 적은 반복으로 시뮬레이션을 실행 한 다음 공식을 실행한다는 사실을 인정 하시겠습니까? 봇이없는 시뮬레이션?
maxb

3
나에 의해, 나는 이것이 자격을 갖추지 못했고 게임의 정신에 확실히 해당하지 않을 것이라고 생각했다. 즉, 파이썬 소스 코드에서 일을하는 것은 폭발적인 일이며 재미있는 변명이었습니다.
대부분 무해

2
감사! 나는 다른 봇이 당신의 점수에 가까워 질 것이라고 생각하지 않습니다. 그리고이 전략을 구현하려는 다른 사람들에게는 그렇게하지 마십시오. 지금부터이 전략은 규칙에 위배되며 NeoBot만이 토너먼트를 공정하게 유지하기 위해 사용할 수 있습니다.
maxb

1
글쎄, myBot이 모든 것을 이겼지 만, 이것은 훨씬 나아졌습니다. 비록 이런 식으로 봇을 게시하면 -100을 얻지 만 최고 점수는 얻지 못합니다.
Jan Ivan

15

협동 무리

전략

다른 사람이 아직이 규칙의 중요성을 알아 차리지 못했다고 생각합니다.

게임이 200 라운드로 진행되면 40 점 이상이 없어도 가장 높은 점수를 얻은 봇이 승자가됩니다.

모든 봇이 파열 될 때까지 항상 굴리면 200 라운드가 끝날 때 모두 0의 점수를 얻게되고 모두가 이길 것입니다! 따라서 협동 조합 스웜의 전략은 모든 플레이어의 점수가 0 인 한, 아무 점수 나 득점하면 정상적으로 플레이하는 것입니다.

이 글에서는 두 개의 봇을 제출합니다. 첫 번째는 CooperativeSwarmBot이고 두 번째는 CooperativeThrowTwice입니다. CooperativeSwarmBot은 공식적으로 협동 조합 떼의 일부인 모든 봇의 기본 클래스 역할을하며 협력이 실패 할 때 첫 번째 성공적인 롤을 수락하는 자리 표시 자 동작을 갖습니다. CooperativeSwarmBot은 CooperativeSwarmBot를 부모로 사용하며 비협조적 행동이 하나가 아닌 두 개의 롤을 만드는 것 외에는 모든면에서 동일합니다. 다음 며칠 동안 저는 비협조적인 봇에 대해 훨씬 지능적인 행동을하는 새로운 봇을 추가하기 위해이 게시물을 개정 할 것입니다.

암호

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

분석

생존 능력

이 게임을하려면 8 명 모두의 지원이 필요하기 때문에이 게임에서 협력하기가 매우 어렵습니다. 각 봇 클래스는 게임당 하나의 인스턴스로 제한되므로 달성하기 어려운 목표입니다. 예를 들어, 100 개의 협동 로봇 봇과 30 ​​개의 비 협조 로봇 봇 풀에서 8 개의 협동 로봇을 선택할 확률은 다음과 같습니다.

100130991299812897127961269512594124931230.115

icn

c!÷(ci)!(c+n)!÷(c+ni)!

i=8n=38

사례 연구

여러 가지 이유로 (각주 1과 2 참조), 적절한 협동 조합 무리는 공식 게임에서 절대 경쟁하지 않습니다. 따라서이 섹션에서 내 시뮬레이션 중 하나의 결과를 요약하겠습니다.

이 시뮬레이션은 내가 마지막으로 확인했을 때 여기에 게시 된 38 개의 다른 봇과 CooperativeSwarmBot을 부모 클래스로 사용한 2900 개의 봇을 사용하여 10000 게임을 실행했습니다. 컨트롤러는 10000 개 게임 중 9051 개 (90.51 %)가 200 라운드에서 끝났다고보고했는데 이는 게임의 90 %가 협동 할 것이라는 예측과 매우 유사합니다. 이 봇의 구현은 사소한 것이 었습니다. CooperativeSwarmBot 이외의 모든 유형은 다음과 같습니다.

class CooperativeSwarm_1234(CooperativeSwarmBot):
    pass

봇의 3 % 미만이 80 % 미만의 승률을 보였으며 , 봇의 11 % 이상이 자신이 한 게임마다 승리했습니다. 떼에서 2900 봇의 평균 승률은 약 86 %이며, 이는 엄청나게 좋습니다. 현재 공식 리더 보드에서 최고의 성과를 거둔 사람은 22 % 미만의 게임에서 이깁니다. 답변을 위해 허용 된 최대 길이 내에 협동 조합 떼의 전체 목록을 맞출 수 없으므로 대신 여기로 가야한다고보고 싶다면 https://pastebin.com/3Zc8m1Ex

각 봇은 평균 약 27 게임을했기 때문에 운은 개별 봇에 대한 결과를 볼 때 상대적으로 큰 롤을합니다. 비협조적인 게임에 대한 고급 전략을 아직 구현하지 않았기 때문에 대부분의 다른 봇은 협동 조합 떼와의 경기에서 큰 이득을 얻었으며 협동 조합 떼의 중간 승률은 86 %였습니다.

떼에없는 봇에 대한 전체 결과는 다음과 같습니다. 그 결과에 특별한주의를 기울일만한 두 개의 봇이 있습니다. 첫째, StopBot은 어떤 게임에서도 이기지 못했습니다. 협동 조합 무리가 실제로 StopBot과 동일한 전략을 사용했기 때문에 이것은 특히 비극적입니다. 당신은 StopBot이 우연히 여덟 번의 게임에서 이길 것으로 예상했을 것입니다. 협동하는 무리는 상대방에게 첫 번째 움직임을 주어야하기 때문에 조금 더 많은 것을 할 것입니다. 그러나 두 번째 흥미로운 결과는 PointsAreForNerdsBot의 노력이 마침내 성과를 거두었다는 것입니다. 그것은 떼와 협력하여 모든 게임에서 승리했습니다!

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

결함

이 협력 방식에는 몇 가지 단점이 있습니다. 첫째, 비협조적인 봇과 상대 할 때 협동 봇은 상대방이 협력 할 의사가 있는지 여부를 모르기 때문에 첫 번째 우위를 얻지 못합니다. 0 점. 마찬가지로이 협력 전략은 악성 봇의 악용에 매우 취약합니다. 예를 들어, 협동 플레이 중에 마지막 라운드에서 마지막으로 플레이하는 봇은 다른 사람들이 모두 잃게하기 위해 즉시 롤링을 중단 할 수 있습니다 (물론 첫 번째 롤이 6이 아니라고 가정).

협력함으로써 모든 봇은 100 % 승리 율의 최적 솔루션을 달성 할 수 있습니다. 따라서 승리 율이 중요한 문제라면 협력은 안정적인 균형이 될 것이며 걱정할 것은 없습니다. 그러나 일부 봇은 리더 보드 상단에 도달하는 등 다른 목표를 우선시 할 수 있습니다. 이것은 마지막 턴 후에 다른 봇이 결함을 일으킬 위험이 있음을 의미하며, 이로 인해 먼저 결함이 생길 수 있습니다. 이 경쟁의 설정으로 인해 상대방이 이전 게임에서 한 일을 볼 수 없으므로 결함이있는 개인을 처벌 할 수 없습니다. 따라서 협력은 결국 실패에 대한 불안정한 평형이다.

각주

[1] : 두 개가 아닌 수천 개의 봇을 제출하고 싶지 않은 주된 이유는 1000 개 정도의 속도로 시뮬레이션 속도를 늦추고 [2]와 같이 크게 엉망이되기 때문입니다. 다른 봇이 거의 독점적으로 서로가 아닌 웜에 대항하여 플레이 할 수 있으므로 백분율을 얻습니다. 그러나 더 중요한 것은 "원하는 경우에도"봇과 동일한 전략을 구현해서는 안된다는 규칙의 정신을 깨지 않고 합리적인 시간 내에 많은 봇을 만들 수 없다는 사실입니다. 의도적이든 우발적이든

[2] : 협동 떼를 운영 할 때 시뮬레이션 속도가 느려지는 두 가지 주된 이유가 있다고 생각합니다. 첫째, 더 많은 봇은 각 봇이 같은 수의 게임에서 플레이하기를 원할 경우 더 많은 게임을 의미합니다 (사례 연구에서 게임 수는 약 77 배 차이가납니다). 둘째, 협동 게임은 200 라운드 동안 지속되기 때문에 시간이 오래 걸리며 라운드 내에서 플레이어는 무기한으로 계속 굴러 가야합니다. 내 설정의 경우 게임을 시뮬레이트하는 데 약 40 배가 더 걸렸습니다. 사례 연구는 10000 게임을 실행하는 데 3 분이 조금 걸렸지 만, 협동 떼를 제거하면 4.5 초만에 10000 게임을 마칠 수있었습니다. 이 두 가지 이유 사이에서, 나는 경쟁이 없을 때와 비교할 때 떼가있을 때 봇의 성능을 정확하게 측정하는 데 약 3100 배 더 오래 걸릴 것으로 추정합니다.


4
와. PPCG에 오신 것을 환영합니다. 이것은 첫 번째 대답입니다. 나는 실제로 이런 상황을 계획하지 않았습니다. 당신은 분명히 규칙에서 허점을 발견했습니다. 귀하의 답변은 단일 봇이 아닌 봇 모음이므로이 점수를 어떻게 받아야하는지 잘 모르겠습니다. 그러나 지금 당장 말할 것은 한 명의 참가자가 모든 봇의 98.7 %를 통제한다는 것이 불공평하다는 것입니다.
maxb

2
사실 중복 봇이 공식 경쟁에 참여하기를 원하지 않습니다. 그렇기 때문에 수천 개의 거의 동일한 봇을 제출하는 대신 시뮬레이션을 직접 실행했습니다. 더 명확하게하기 위해 제출물을 수정하겠습니다.
Einhaender

1
이와 같은 답변을 기대했다면, 200 라운드로 진행되는 게임이 플레이어에게 점수를주지 않도록 변경했을 것입니다. 그러나 알다시피,이 전략을 규칙에 위배시키는 동일한 봇을 만드는 규칙이 있습니다. 봇을 만든 모든 사람에게 불공평하기 때문에 규칙을 변경하지는 않겠습니다. 그러나 협력의 개념은 매우 흥미롭고, 고유 한 전략과 함께 협력 전략을 구현하는 다른 봇이 제출되기를 바랍니다.
maxb

1
더 자세히 읽은 후에는 게시물이 명확하다고 생각합니다.
maxb

대다수의 리더 보드 배치에서 순이익을 얻기 위해이 협업 프레임 워크에서 코드를 래핑해야하는 기존 봇 수는 몇 개입니까? 내 순진한 추측은 50 %입니다.
스파링

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

모든 것을 시도해보십시오 GoToNBot.20, 22, 24가 가장 좋습니다. 이유를 모르겠습니다.


업데이트 : 점수가 40 이상이면 항상 던지기를 중지하십시오.


또한 이러한 종류의 봇을 실험했습니다. 라운드 당 가장 높은 평균 점수는 봇이 16이되었을 때 발견되지만, "최종 게임"이 20- 봇이 더 자주 승리한다고 가정합니다.
maxb

@maxb 그렇지 않습니다. 20은 여전히 ​​테스트에서 "최종 게임"이없는 최고의 제품입니다. 이전 버전의 컨트롤러에서 테스트했을 수도 있습니다.
tsh December

이 챌린지를 설계하기 전에 별도의 테스트를 수행했는데, 여기서 내 게시물에서 두 가지 전술에 대한 라운드 당 평균 점수 ( "throw x times"및 "throw until x score")를 계산했으며, 찾은 최대 값은 15-16입니다. . 샘플 크기가 너무 작았지만 불안정 함을 알았습니다.
maxb

2
나는 이것으로 몇 가지 테스트를 수행했으며, 내 결론은 20이 40/2이므로 잘 작동한다는 결론입니다. 확실하지는 않지만. 내가 end_score4000으로 설정 하고 target계산 에서 이것을 사용하도록 봇을 변경 했을 때 15-16 봇은 훨씬 더 나았습니다. 그러나 게임이 점수를 올리는 것이라면 사소한 것입니다.
maxb

1
@maxb end_score4000 인 경우 200 턴 전에 4000을 얻는 것은 거의 불가능합니다. 그리고 게임은 단순히 200 턴에서 가장 높은 점수를 얻은 사람입니다. 그리고 이번에는 한 차례에 최고 점수에 대한 전략이 200 차례에 최고 점수와 동일하기 때문에 15에서 멈추십시오.
tsh

10

적응 형 롤러

더 공격적으로 시작하고 라운드가 끝날 때까지 진정합니다.
그것이 성공했다고 생각되면, 안전을 위해 여분의 시간을 굴립니다.

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

훌륭한 첫 제출! 테스트를 위해 작성한 봇에 대해 실행하지만 더 많은 봇이 게시되면 최고 점수를 업데이트합니다.
maxb

나는 당신의 봇을 약간 수정하여 몇 가지 테스트를 실행했습니다. lim = max(min(self.end_score - scores[self.index], 24), 6)최대 값을 24로 높이고 최소값을 6으로 늘리면 자체적으로 우 승률이 높아지고 훨씬 더 많이 결합됩니다.
AKroell

@AKroell : 쿨! 나는 마지막에 몇 번 구르는 것을 확인하기 위해 비슷한 일을하려고했지만 아직 할 시간이 없었습니다. 그러나 이상하게도 100k 런을 할 때 그 값으로 성능이 저하되는 것 같습니다. 그래도 18 봇으로 테스트했습니다. 어쩌면 모든 봇에서 몇 가지 테스트를해야 할 수도 있습니다.
Emigna

5

알파

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

알파는 누구에게도 뒤지지 않는 것을 거부합니다. 더 높은 점수를받은 봇이있는 한 계속 회전합니다.


yield작동 방식으로 인해 롤링이 시작되면 멈추지 않습니다. my_score루프에서 업데이트하고 싶을 것 입니다.
Spitemaster

@Spitemaster 감사합니다.
니모닉

5

NotFoBearBotBot

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

아이디어는 다른 봇이 점수를 잃을 수 있기 때문에 2 위가 나쁘지는 않다는 것입니다.


1
PPCG에 오신 것을 환영합니다! 제출 한 내용을 살펴 보니 게임에 참여하는 플레이어가 많을수록 봇의 승률이 낮아지는 것 같습니다. 나는 왜 곧바로 말할 수 없다. 봇이 1vs1과 일치하면 10 %의 승률을 얻습니다. 아이디어가 유망한 것처럼 보이고 코드가 정확 해 보이므로 왜 당신의 승리 율이 높지 않은지 알 수 없습니다.
maxb December

6
나는 그 행동을 살펴 보았고,이 줄은 나를 혼란스럽게했다 6: Bot NotTooFarBehindBot plays [4, 2, 4, 2, 3, 3, 5, 5, 1, 4, 1, 4, 2, 4, 3, 6] with scores [0, 9, 0, 20, 0, 0, 0] and last round == False. 당신의 봇이 7 번의 던지기 후에 리드에 있지만, 6이 될 때까지 계속됩니다. scores뿐만 총 점수가 아닌 현재 라운드 다이의 경우를 포함한다. 로 수정해야합니다 current_score = scores[self.index] + sum(self.current_throws).
maxb

감사합니다-그 변화를 만들 것입니다!
스튜어트 무어

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

우리는 크게 가거나 집에 가고 싶어요? GoHomeBot은 주로 집으로 돌아갑니다. (그러나 놀랍게도 잘합니다!)


이 봇은 항상 40 포인트가되므로 scores목록에 포인트가 없습니다 . 전에는 이와 같은 봇 (GoToEnd 봇)이 있었지만, 데이빗은 답을 삭제했습니다. 그 봇을 당신의 것으로 교체하겠습니다.
maxb

1
이 봇의 확장 된 통계를 보면 매우 재미 있습니다 : pointsAreForNerds 및 StopBot를 제외하고,이 봇은 평균 점수가 가장
낮지 만

5

LeadLead

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

LeadLead는 GoTo20Bot에서 아이디어를 빌립니다. 그것은 적어도 하나 이상의 롤을 가질 다른 것들이 있다고 항상 생각합니다 (last_round 또는 40에 도달 할 때). 따라서 봇은 따라 잡기 위해 약간 앞서려고합니다.


4

Roll6TimesV2

현재 최고를이기는 것은 아니지만 더 많은 봇이 있으면 더 나아질 것이라고 생각합니다.

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

그건 그렇고 정말 멋진 게임.


PPCG에 오신 것을 환영합니다! 첫 번째 KotH 챌린지뿐만 아니라 첫 번째 답변에 매우 인상적입니다. 당신이 게임을 좋아해서 다행입니다! 저녁 경기 후 최고의 전략에 대해 많은 토론을 했으므로 도전하기에 완벽 해 보였습니다. 당신은 현재 18에서 3 위를 차지했습니다.
maxb

4

스톱 봇

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

말 그대로 단 한 번의 던지기.

이것은 기본 Bot클래스 와 동일합니다 .


1
죄송합니다! 당신은 모든 규칙을 따르고 있지만, 당신의 봇은 라운드 당 평균 2.5 점으로별로 효과적이지 않을까 걱정됩니다.
maxb

1
그래도 누군가 봇을 게시해야한다는 것을 알고 있습니다. 손실에 대한 봇을 퇴화시킵니다.
Zacharý

5
마지막 시뮬레이션에서 정확히 하나의 승리를 확보 한 봇이 완전히 쓸모없는 것은 아니라는 점에 깊은 인상을 받았습니다.
maxb

2
게임이 되었습니까?! 놀랍습니다.
Zacharý

3

BringMyOwn_dice (BMO_d)

이 봇은 주사위를 좋아하며 2 개의 주사위를 가져옵니다. 한 라운드에 주사위를 던지기 전에 자체 주사위 2 개를 던져 그들의 합을 계산합니다. 이것은 수행 할 던지기의 수이며, 아직 40 점이없는 경우에만 던집니다.

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
나는 코인 플립을 사용하는 임의의 봇을 생각하고 있었지만 이것은 도전에 더 가깝습니다! 주사위를 5-6 회 던질 때 라운드 당 가장 많은 점수를 얻으므로 두 개의 주사위를 던질 때 평균 점수에 가깝기 때문에 두 개의 주사위가 가장 잘 작동한다고 생각합니다.
maxb

3

푸봇

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 once불필요합니다-봇을 호출하기 전에 한 번 던집니다. 봇은 항상 최소 두 번 던질 것입니다.
Spitemaster

감사. 나는 그 방법의 이름으로 잘못 인도되었다.
Peter Taylor

@PeterTaylor 제출해 주셔서 감사합니다! 나는 make_throw플레이어가 자신의 차례를 건너 뛸 수 있기를 원했을 때 방법의 초기 이름을 지정했습니다 . 더 적절한 이름이 될 것 같아요 keep_throwing. 샌드 박스에있는 의견에 감사드립니다. 이것이 올바른 도전을하도록 도와주었습니다.
maxb

3

빨리 간다

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

컨셉 : 초기 롤 (25 세 이상)에서 큰 승리를 거둔 다음 한 번에 2 개의 롤을 올리십시오.


3

이진 봇

다른 사람이 마지막 라운드를 시작하자마자 승리 점수를 이길 수 있도록 최종 점수에 가까워 지려고합니다. 목표는 항상 현재 점수와 종료 점수의 중간입니다.

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

흥미롭고, Hesitate먼저 선을 넘지 않습니다. 기능을 class물건 으로 둘러 쌀 필요가 있습니다 .
Christian Sievers

3

포인트 AreForNerdsBot

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

이것은 설명이 필요 없습니다.

OneInFiveBot

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

5면 주사위로 5를 굴릴 때까지 계속 굴립니다. 5는 6보다 작으므로 승리해야합니다!


2
PPCG에 오신 것을 환영합니다! 나는 당신이 알고 있다고 확신하지만, 첫 번째 봇은 말 그대로이 경쟁에서 최악의 봇입니다! 이것은 OneInFiveBot깔끔한 아이디어이지만 고급 봇과 비교할 때 최종 게임에서 어려움을 겪고 있다고 생각합니다. 여전히 좋은 제출입니다!
maxb

2
OneInFiveBot그가 지속적으로 도달 가장 높은 총점이있는 방법으로 매우 흥미 롭다.
AKroell

1
StopBot샌드백을 주셔서 감사합니다 : P. OneInFiveBot은 실제로 매우 깔끔하고 훌륭합니다.
Zacharý

@ maxb Yep, 그것이 내가 이름을 얻은 곳입니다. 나는 정직하게 테스트하지 않았고 OneInFiveBot예상보다 훨씬 잘하고 있습니다.
The_Bob


3

LizduadacBot

1 단계로 승리하려고합니다. 최종 조건은 다소 불완전합니다.

이것은 또한 첫 번째 게시물이며 (Python을 처음 사용합니다) "PointsAreForNerdsBot"을 이길 경우 행복합니다!

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

PPCG에 오신 것을 환영합니다 (Python에 오신 것을 환영합니다)! 에 대해지는 데 어려움을 PointsAreForNerdsBot겪지 만 봇은 실제로 꽤 잘 지냅니다. 오늘 밤 또는 내일 점수를 업데이트하지만 승률은 약 15 %로 평균 12.5 %보다 높습니다.
maxb

"
나쁜

@ maxb 나는 실제로 승률이 그렇게 높을 것이라고 생각하지 않았습니다! (나는 로컬에서 테스트하지 않았다). 50을 조금 더 높이거나 낮추면 승률이 높아지는 지 궁금합니다.
lizduadac

3

슬로우 스타트

이 봇은 TCP Slow Start 알고리즘을 구현합니다. 이전 턴에 따라 롤 수 ( 또는 )를 조정합니다. 이전 턴에서 6을 굴리지 않은 경우이 턴 의 노어 를 증가시킵니다 . 가 감소하는 반면 경우는 않았다.

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

PPCG에 오신 것을 환영합니다! 흥미로운 접근법은 무작위 변동에 얼마나 민감한 지 모르겠습니다. 이 작업을 수행하는 데 필요한 두 가지 : def updateValues():있어야합니다 def updateValues(self):(또는 def update_values(self):PEP8을 따르고 싶은 경우). 둘째, 호출 updateValues()은 대신 self.updateValues()(또는 self.update_vales()) 이어야합니다 .
maxb

2
또한 iwhile 루프에서 변수 를 업데이트해야한다고 생각합니다 . 지금 봇은 while 루프를 완전히 통과하거나 6에
도달

현재 최고 점수에서 이러한 변경 사항을 구현할 자유를 얻었습니다. 나는 당신이 초기 값을 실험하고 self.nor그것이 봇의 성능에 어떤 영향을 미치는지 볼 수 있다고 생각합니다 .
maxb

3

키사 츠

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

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

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

Prescience는 일반적으로 승리하지만 운명을 항상 피할 수는 없습니다.
Shai-Hulud의 길은 위대하고 신비합니다!


이 도전의 초기에 (즉 NeoBot, 게시 되기 전 ), 거의 사소한 Oracle봇을 썼습니다 .

    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

그러나 그것이 충분히 재미 있다고 생각하지 않았기 때문에 그것을 게시하지 않았습니다.)하지만 한 번 NeoBot 선두에 들어서서 미래를 예측하는 완벽한 능력을 이길 방법에 대해 생각하기 시작했다. 여기 Dune 인용구가 있습니다; 그것은 Kwisatz Haderach 인 Paul Atreides가 무한한 서로 다른 미래를 풀 수있는 넥서스에 서있을 때입니다.

프리 사이언스 (prescience)는 한 번에 정확성과 의미있는 오류의 원인으로 밝혀진 한계를 통합 한 조명이었다. 일종의 하이젠 베르크 불확실성이 개입했다 : 그가 본 것을 밝혀낸 에너지 소비, 그가 본 것을 바꿨다…… 알려진 우주. 그는 결과에 대한 폭력이 너무나 많은 변수에 영향을받는 것을 보았고, 그의 작은 움직임만으로도 패턴에 엄청난 변화가 일어났다.

그 비전은 그를 움직이지 못하게하려는 것이었지만, 이것 역시 그 결과에 따른 행동이었습니다.

답은 다음과 같습니다. 미래를 예측하는 것은 미래를 바꾸는 것입니다. 매우 조심 스러우면 선택적인 행동이나 무 활동에 의해 적어도 대부분의 시간에 유리한 방식으로 바꿀 수 있습니다. 심지어 KwisatzHaderach100 % 승률을 얻을 수 없습니다!


이 봇은 난수 생성기의 상태를 변경하여 롤링을 피하거나 적어도 예상하는 것처럼 보입니다. HarkonnenBot도 마찬가지입니다. 그러나이 봇의 승률은 NeoBot의 승률보다 훨씬 높습니다. 6 난수가 발생하지 않도록하기 위해 난수 생성기를 적극적으로 조작하고 있습니까?
maxb

아, 첫 번째 독서에서 나는 이것이 더 좋을 NeoBot뿐만 아니라 더 좋다는 것을 알지 못했습니다 ! 또한 무작위 (특히 컨트롤러)를 사용하는 모든 작업을 수행하는 방법에 대한 예를 제공하는 방법을 좋아합니다 random.Random. 자신의 인스턴스를 사용하십시오 . 마찬가지로 NeoBot, 이것은 컨트롤러의 지정되지 않은 구현 세부 사항의 변경에 약간 민감한 것으로 보입니다.
Christian Sievers

@maxb : HarkonnenBotRNG를 건드리지 않습니다. 난수에 대해서는 전혀 신경 쓰지 않습니다. 그것은 다른 모든 봇을 독살 한 다음 가능한 한 천천히 결승선까지 걸어갑니다. 많은 요리 진미와 마찬가지로 복수는 길고 섬세한 준비 후에 천천히 맛볼 수있는 요리입니다.
Dani O

@ChristianSievers : (및 ) 와 달리 구현의 세부 사항에만 의존합니다. 특히 random.random ()이 어떻게 구현되는지 알 필요가 없으며 컨트롤러가 그것을 사용한다는 것만; DNeoBotHarkonnenBotKwisatzHaderach
Dani O

1
나는 당신의 모든 봇을 살펴 보았습니다. 나는 치료하기로 결정했습니다 KwisatzHaderachHarkonnenBot같은 방법으로 NeoBot. 적은 수의 게임으로 시뮬레이션에서 점수를 받고 공식 시뮬레이션에는 포함되지 않습니다. 그러나 그들은 다음과 같이 최고 점수 목록에 올 것 NeoBot입니다. 그들이 공식 시뮬레이션에 있지 않은 주된 이유는 다른 봇 전략을 망칠 수 있기 때문입니다. 하나. WisdomOfCrowds참여하기에 적합해야하며, 새로운 변경 사항이 궁금합니다.
20:49에 maxb

2
class ThrowThriceBot(Bot):

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

글쎄, 그 하나는 분명하다


나는 그 클래스의 봇으로 실험을 해왔다. (게임을 처음 할 때 사용한 전술이었다). 5-6은 라운드 당 평균 점수가 높지만 4 번의 투구를 가졌습니다.
maxb December

또한 첫 번째 KotH 답변을 축하합니다!
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는 항상 마지막 라운드와 같은 역할을하며 마지막 봇입니다. 리드가 나올 때까지 계속 굴립니다. 또한 실제로 마지막 라운드이거나 40 점에 도달하지 않는 한 15 점 미만으로 정산하고 싶지 않습니다.


재미있는 접근법. 나는 당신의 봇이 뒤떨어지기 시작하면 고통받는 것 같아 한 라운드에서 30 점 이상을 획득 할 확률은 낮으므로 봇은 현재 점수를 유지할 가능성이 높습니다.
maxb December

1
마지막 라운드에서와 같이 내가 저지른 실수 (NotTooFarBehindBot 코멘트 참조)로 인해이 문제가 발생한 것으로 의심됩니다.이기는 경우 6 점 (점수 [self.index] 업데이트 안 함)이 될 때까지 계속 던집니다. 그 불평등이 잘못된 길을 가지고 있습니까? max (scores)는 항상> = scores [self.index]입니다.
Stuart Moore

@StuartMoore Haha, 그렇습니다. 감사!
Spitemaster

나는 당신이 원하는 것을하기 위해 2 번째에 "and last_round"를 원한다고 생각합니다. 그렇지 않으면 2 번째는 last_round가 참인지 아닌지에 사용될 것입니다
Stuart Moore

3
의도적입니다. 턴을 끝낼 때 항상 선두에 서려고합니다.
Spitemaster

2

쿼터 봇

내가 구현 한 순진한 "quota"시스템은 실제로 전체적으로 상당히 높은 점수를 얻은 것으로 보인다.

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:나에게 오류를 준다. 또한while sum(self.current_throws)
Spitemaster

@Spitemaster는 스택 교환에 붙여 넣는 중 오류가 발생했습니다. 이제 작동합니다.
FlipTack

거기 때문입니다 @Spitemaster <>방해 기호를 <pre>내가 사용하던 태그
FlipTack

2

기대 봇

그냥 똑바로 플레이하고 주사위 던지기에 대한 예상 값을 계산하고 긍정적 인 경우에만 만듭니다.

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

컨트롤러를 실행하는 데 문제가 있었으며 멀티 스레드 스레드에서 "NameError : name 'bots_per_game'is not defined"라는 이름이 표시되었으므로 실제로 어떻게 작동하는지 알 수 없습니다.


1
나는 이것이에 해당 될 수있을 테니까요 생각 봇 "16로 이동"의,하지만 우리는 아직 그 중 하나가없는
스튜어트 무어

1
그 @StuartMoore ... 네, 매우 사실 지점입니다
가인

Windows 컴퓨터에서 컨트롤러를 실행할 때 컨트롤러 관련 문제가 발생했습니다. 어떻게 든 내 리눅스 컴퓨터에서 잘 돌아갔다. 컨트롤러를 업데이트 중이며 게시물이 완료되면 업데이트합니다.
maxb

@maxb 고맙습니다. 아마도 다른 프로세스에서 어떤 변수를 사용할 수 있는지에 대한 것입니다. 참고로 이것을 업데이트하여 수율에 바보 같은 오류가 발생했습니다.
Cain

2

축복

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

축복받은 FrankerZ GabeN 축복받은


2

마흔 대

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

마지막 라운드까지 14 점을 시도한 다음 다른 모든 사람이 14 점을 시도하고 그 점수를 매길 것이라고 가정합니다.


나는 TypeError: unsupported operand type(s) for -: 'list' and 'int'당신의 봇과 함께했습니다.
tsh December

max_projected_score전체 목록이 아닌 최대 목록이어야 한다고 가정합니다. 맞습니까? 그렇지 않으면 tsh와 동일한 문제가 발생합니다.
maxb

죄송합니다. 수정하도록 수정되었습니다.
histocrat

2

서슴다

겸손한 두 단계를 수행 한 다음 다른 사람이 회선을 건너기를 기다립니다. 업데이트 된 버전은 더 이상 최고 점수를 넘기려고하지 않고 도달하기를 원합니다. 소스 코드의 2 바이트를 삭제하여 성능을 향상시킵니다!

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

반역자

이 봇은의 Hesitate 마지막 전략과 고급 전략을 결합하고 BotFor2X, 그것이 누구인지를 기억하려고 시도하며 환상에 처한 것을 발견하면 사납게됩니다.

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)

글쎄요, 이것은 매우 우아합니다 :) 또한, 주요 경쟁에서 1 위와 2 위를 차지한 것을 축하합니다!
21:53의 Dani O

당연히 나는 쥐게 한 HarkonnenBot그 때문에 Rebel더 이상 자체 unpoison 수)와 나는 또한 쥐게 한 TleilaxuBot그, 그래서 Rebel더 이상 그것을 감지하지 않습니다!
Dani O

1

다섯을

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

우리는 6시 전에 5를 굴릴 것입니다.


대신 1에서 멈 추면 진행 속도가 느려지지만 단일 범위에서 40에 도달 할 가능성이 높습니다.
니모닉

내 테스트에서 TakeOne은 라운드 당 20.868 포인트를 얻었으며 TakeFive의 라운드 당 24.262 포인트를 얻었습니다 (또한 0.291에서 0.259로 승리 율을 얻었습니다). 그래서 나는 그만한 가치가 있다고 생각하지 않습니다.
Spitemaster

1

체이서

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

체이서가 한 자리를 잡으려고 노력합니다. 마지막 라운드라면 필사적으로 적어도 50 포인트에 도달하려고합니다. 좋은 측정을 위해 그는 무엇이든 상관없이 4 번 이상 던집니다.

[편집 1 : 마지막 라운드에서 금메달 전략 추가]

[편집 2 : 봇이 최고 봇 점수 만이 아니라 40 점으로 잘못 판단 할 수 있기 때문에 논리가 업데이트 됨]

[편집 3 : 체이서 게임 종료시 좀 더 방어적임]


PPCG에 오신 것을 환영합니다! 따라 잡으려고 노력할뿐만 아니라 첫 번째 장소를 통과하는 깔끔한 아이디어. 지금 시뮬레이션을 실행하고 있습니다. 운이 좋기를 바랍니다.
maxb

감사! 처음에는 이전 리더를 고정 금액 (6과 20 사이의 값으로 시도)을 초과하려고했지만 박람회를 두 번 더 잘 던지는 것으로 나타났습니다.
AKroell


1

FutureBot

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

OneStepAheadBot

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

한 쌍의 봇은 자신의 주사위를 가져와 미래를 예측하기 위해 굴립니다. 하나가 6 인 경우, FutureBot은 다음 롤을위한 2 개의 주사위 중 어느 것을 기억하지 못하므로 포기합니다.

어느 쪽이 더 나을지 궁금합니다.

OneStepAhead는 내 취향에 따라 OneInFive와 약간 유사하지만 FutureBot 및 OneInFive와 비교하는 방법도보고 싶습니다.

편집 : 이제 45를 친 후 멈춤


PPCG에 오신 것을 환영합니다! 당신의 봇은 확실히 게임의 정신으로 재생됩니다! 오늘 저녁 늦게 시뮬레이션을 실행하겠습니다.
maxb

감사! 나는 그것이 얼마나 잘 될지 궁금하지만 나는 그것이 낮은 편에있을 것이라고 추측하고있다.
윌리엄 포터
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.