เกมลูกเต๋า แต่หลีกเลี่ยงหมายเลข 6 [ปิด]


58

ทัวร์นาเมนต์จบ!

การแข่งขันสิ้นสุดแล้ว! การจำลองขั้นสุดท้ายดำเนินการในตอนกลางคืนรวมทั้งหมดเกม ผู้ชนะคือChristian Sieversด้วย bot OptFor2X ของเขา คริสเตียน Sievers ยังมีการจัดการเพื่อรักษาความปลอดภัยสถานที่ที่สองกับกบฎ ขอแสดงความยินดี! ด้านล่างคุณสามารถดูรายการคะแนนสูงอย่างเป็นทางการสำหรับการแข่งขัน3108

หากคุณยังต้องการเล่นเกมคุณมีความยินดีอย่างยิ่งที่จะใช้คอนโทรลเลอร์ที่โพสต์ด้านล่างและใช้รหัสในเกมเพื่อสร้างเกมของคุณเอง

ลูกเต๋า

ฉันได้รับเชิญให้เล่นเกมลูกเต๋าซึ่งฉันไม่เคยได้ยินมาก่อน กฎนั้นง่าย แต่ฉันคิดว่ามันจะสมบูรณ์แบบสำหรับความท้าทายของ KotH

กฎระเบียบ

จุดเริ่มต้นของเกม

ตายไปรอบ ๆ โต๊ะและทุกครั้งที่ถึงตาคุณคุณจะต้องขว้างผู้ตายหลาย ๆ ครั้งตามที่คุณต้องการ อย่างไรก็ตามคุณต้องโยนอย่างน้อยหนึ่งครั้ง คุณติดตามผลรวมของการโยนทั้งหมดสำหรับรอบของคุณ หากคุณเลือกที่จะหยุดคะแนนสำหรับรอบนั้นจะถูกเพิ่มเข้าไปในคะแนนรวมของคุณ

ดังนั้นทำไมคุณไม่หยุดโยนความตาย? เพราะถ้าคุณได้ 6 คะแนนของคุณตลอดทั้งรอบจะกลายเป็นศูนย์และตายต่อไป ดังนั้นเป้าหมายเริ่มต้นคือการเพิ่มคะแนนของคุณโดยเร็วที่สุด

ใครคือผู้ชนะ

เมื่อผู้เล่นคนแรกรอบโต๊ะถึง 40 คะแนนขึ้นไปรอบสุดท้ายจะเริ่ม เมื่อรอบสุดท้ายได้เริ่มขึ้นทุกคนยกเว้นคนที่ริเริ่มรอบสุดท้ายจะได้รับเทิร์นอีกครั้ง

กฎสำหรับรอบสุดท้ายเป็นเช่นเดียวกับรอบอื่น ๆ คุณเลือกที่จะขว้างหรือหยุด อย่างไรก็ตามคุณรู้ว่าคุณไม่มีโอกาสชนะถ้าคุณไม่ได้รับคะแนนสูงกว่าคะแนนก่อนหน้านี้ในรอบสุดท้าย แต่ถ้าคุณไปไกลเกินไปคุณอาจได้ 6

อย่างไรก็ตามยังมีกฎอีกข้อที่ต้องคำนึงถึง หากคะแนนรวมปัจจุบันของคุณ (คะแนนก่อนหน้านี้ของคุณ + คะแนนปัจจุบันของคุณสำหรับรอบ) เป็น 40 หรือมากกว่าและคุณตี 6 คะแนนรวมของคุณตั้งเป็น 0 นั่นหมายความว่าคุณต้องเริ่มต้นใหม่ทั้งหมด หากคุณตี 6 เมื่อคะแนนรวมปัจจุบันของคุณเป็น 40 หรือมากกว่าเกมจะดำเนินต่อไปตามปกติยกเว้นตอนนี้คุณอยู่ในตำแหน่งสุดท้าย รอบสุดท้ายจะไม่ถูกเรียกเมื่อคะแนนรวมของคุณถูกรีเซ็ต คุณยังสามารถชนะรอบ แต่มันจะกลายเป็นความท้าทายมากขึ้น

ผู้ชนะคือผู้เล่นที่มีคะแนนสูงสุดเมื่อรอบสุดท้ายจบลง หากผู้เล่นสองคนหรือมากกว่าแบ่งปันคะแนนเดียวกันพวกเขาทั้งหมดจะถูกนับเป็นผู้ชนะ

กฎเพิ่มเติมคือเกมดำเนินต่อไปได้สูงสุด 200 รอบ นี่คือเพื่อป้องกันกรณีที่มีหลาย ๆ บอทโดยทั่วไปขว้างต่อไปจนกว่าพวกเขาจะตี 6 เพื่ออยู่ที่คะแนนปัจจุบัน เมื่อรอบที่ 199 ผ่านไปแล้วlast_roundจะถูกตั้งค่าเป็นจริงและเล่นอีกหนึ่งรอบ หากเกมไปถึง 200 รอบบอท (หรือบอท) ที่มีคะแนนสูงสุดคือผู้ชนะแม้ว่าพวกเขาจะไม่มี 40 คะแนนหรือมากกว่า

ปะยางรถ

  • แต่ละรอบที่คุณขว้างปาผู้ตายต่อไปจนกว่าคุณจะเลือกหยุดหรือรับ 6
  • คุณจะต้องโยนความตายครั้งเดียว (ถ้าการโยนครั้งแรกของคุณคือ 6, รอบของคุณจะจบลงทันที)
  • หากคุณได้รับ 6 คะแนนปัจจุบันของคุณถูกกำหนดเป็น 0 (ไม่ใช่คะแนนรวมของคุณ)
  • คุณเพิ่มคะแนนปัจจุบันของคุณไปยังคะแนนรวมของคุณหลังจากแต่ละรอบ
  • เมื่อบอทจบเทิร์นของพวกเขาส่งผลให้คะแนนรวมอย่างน้อย 40 คนอื่นจะได้รับเทิร์นสุดท้าย
  • หากคะแนนรวมปัจจุบันของคุณคือและคุณได้รับ 6 คะแนนรวมของคุณจะถูกตั้งค่าเป็น 0 และรอบของคุณจะจบ40
  • รอบสุดท้ายจะไม่ถูกกระตุ้นเมื่อเกิดเหตุการณ์ข้างต้นขึ้น
  • คนที่มีคะแนนรวมสูงสุดหลังจากรอบสุดท้ายคือผู้ชนะ
  • ในกรณีที่มีผู้ชนะหลายคนทุกคนจะถูกนับเป็นผู้ชนะ
  • เกมดังกล่าวมีระยะเวลาสูงสุด 200 รอบ

ชี้แจงผลคะแนน

  • คะแนนรวม: คะแนนที่คุณบันทึกไว้จากรอบก่อนหน้า
  • คะแนนปัจจุบัน: คะแนนสำหรับรอบปัจจุบัน
  • คะแนนรวมปัจจุบัน: ผลรวมของคะแนนทั้งสองข้างต้น

คุณมีส่วนร่วมอย่างไร

จะมีส่วนร่วมในความท้าทาย KOTH 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

คุณมีอิสระที่จะแสวงหาแรงบันดาลใจจากโพสต์นี้และคัดลอกบอทใด ๆ ที่ฉันได้รวมไว้ที่นี่ อย่างไรก็ตามฉันกลัวว่าพวกเขาจะไม่มีประสิทธิภาพโดยเฉพาะ ...

เมื่อเปิดใช้งานภาษาอื่น

ทั้งในแซนด์บ็อกซ์และบนไบต์ที่สิบเก้าเราได้มีการหารือเกี่ยวกับการอนุญาตให้ส่งเป็นภาษาอื่น ๆ หลังจากอ่านเกี่ยวกับการใช้งานและการรับฟังการโต้แย้งจากทั้งสองฝ่ายฉันได้ตัดสินใจที่จะจำกัดความท้าทายนี้ไว้กับ Python เท่านั้น นี่เป็นเพราะปัจจัยสองประการ: เวลาที่ต้องใช้ในการรองรับหลายภาษาและการสุ่มของความท้าทายนี้ต้องการการวนซ้ำจำนวนมากเพื่อความมั่นคง ฉันหวังว่าคุณจะยังคงเข้าร่วมและหากคุณต้องการเรียนรู้ 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 make_throwคือ ไม่เคยเรียกเพราะรอบของคุณจบลงทันที

สำหรับผู้ที่ยังใหม่กับ Python (และใหม่กับyieldแนวคิด) แต่ต้องการที่จะให้ไปyieldคำหลักที่คล้ายกับผลตอบแทนในบางวิธี แต่แตกต่างกันไปในทางอื่น คุณสามารถอ่านเกี่ยวกับแนวคิดที่นี่ โดยทั่วไปเมื่อคุณyieldฟังก์ชั่นของคุณจะหยุดทำงานและค่าที่คุณyielded จะถูกส่งกลับไปยังคอนโทรลเลอร์ ที่นั่นคอนโทรลเลอร์จะจัดการกับตรรกะของมันจนกว่าจะถึงเวลาที่บอทของคุณจะตัดสินใจอีกครั้ง จากนั้นคอนโทรลเลอร์จะส่งลูกเต๋าไปให้คุณและmake_throwฟังก์ชั่นของคุณจะดำเนินการต่อไปโดยที่หากหยุดไปก่อนโดยทั่วไปจะอยู่บนบรรทัดหลังจากyieldคำสั่งก่อนหน้า

วิธีนี้ผู้ควบคุมเกมสามารถอัปเดตสถานะโดยไม่ต้องเรียกฟังก์ชั่น bot แยกต่างหากสำหรับการโยนลูกเต๋าแต่ละครั้ง

สเปค

คุณอาจจะใช้ใด ๆ pipห้องสมุดหลามที่มีอยู่ใน เพื่อให้แน่ใจว่าฉันจะได้รับค่าเฉลี่ยที่ดีคุณมีเวลา จำกัด 100 มิลลิวินาทีต่อรอบ ฉันดีใจจริง ๆ ถ้าสคริปต์ของคุณเร็วกว่านั้นเพื่อที่ฉันจะได้วิ่งได้มากขึ้น

การประเมินผล

ในการค้นหาผู้ชนะฉันจะใช้บอททั้งหมดและเรียกใช้พวกมันเป็นกลุ่มแบบสุ่มจาก 8 ถ้ามีน้อยกว่า 8 คลาสที่ส่งฉันจะรันพวกมันเป็นกลุ่มสุ่ม 4 เพื่อหลีกเลี่ยงการมีบอททั้งหมดในแต่ละรอบ ฉันจะทำการจำลองเป็นเวลาประมาณ 8 ชั่วโมงและผู้ชนะจะเป็นบอทที่มีเปอร์เซ็นต์การชนะสูงสุด ฉันจะเริ่มต้นการจำลองขั้นสุดท้ายเมื่อต้นปี 2562 เพื่อให้คริสต์มาสกับคุณเพื่อเขียนรหัสบอทของคุณ! วันสุดท้ายเบื้องต้นคือวันที่ 4 มกราคม แต่ถ้านั่นเป็นเวลาน้อยเกินไปฉันสามารถเปลี่ยนเป็นวันที่ใหม่กว่าได้

ก่อนหน้านั้นฉันจะลองทำแบบจำลองรายวันโดยใช้เวลา CPU 30-60 นาทีและอัปเดตกระดานคะแนน นี่จะไม่ใช่คะแนนอย่างเป็นทางการ แต่มันจะทำหน้าที่เป็นแนวทางในการดูว่าบ็อตใดทำหน้าที่ได้ดีที่สุด อย่างไรก็ตามเมื่อวันคริสต์มาสกำลังจะมาถึงฉันหวังว่าคุณจะเข้าใจว่าจะไม่มีให้บริการตลอดเวลา ฉันจะทำให้ดีที่สุดในการจำลองสถานการณ์และตอบคำถามใด ๆ ที่เกี่ยวข้องกับความท้าทาย

ทดสอบด้วยตัวเอง

หากคุณต้องการเรียกใช้แบบจำลองของคุณเองนี่คือรหัสแบบเต็มสำหรับคอนโทรลเลอร์ที่ใช้การจำลองรวมถึงบอตตัวอย่างสองตัวอย่าง

ตัวควบคุม

นี่คือคอนโทรลเลอร์ที่อัปเดตสำหรับความท้าทายนี้ รองรับเอาท์พุต ANSI มัลติเธรดและรวบรวมสถิติเพิ่มเติมด้วยAKroell ! เมื่อฉันทำการเปลี่ยนแปลงตัวควบคุมฉันจะอัปเดตโพสต์เมื่อเอกสารประกอบเสร็จสมบูรณ์

ขอบคุณBMOผู้ควบคุมสามารถดาวน์โหลดบอททั้งหมดจากโพสต์นี้โดยใช้-dแฟล็ก ฟังก์ชั่นอื่น ๆ ไม่เปลี่ยนแปลงในรุ่นนี้ สิ่งนี้ควรตรวจสอบให้แน่ใจว่าการเปลี่ยนแปลงล่าสุดทั้งหมดของคุณถูกจำลองโดยเร็วที่สุด!

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

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

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


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

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

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

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

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

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

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

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

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

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

        self.collect_results()

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

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


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

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

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

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

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

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

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

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

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

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

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

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


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


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

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

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

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


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

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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

    print(delimiter_str)
    print()

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

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

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

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


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

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


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

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

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

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

    return answers


def download_players():
    players = {}

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

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

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

    return players


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

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

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

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


if __name__ == "__main__":

    games = 10000
    bots_per_game = 8
    threads = 4

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

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

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

    bots = get_all_bots()

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

    games_per_thread = math.ceil(games / threads)

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

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

หากคุณต้องการเข้าถึงตัวควบคุมดั้งเดิมสำหรับความท้าทายนี้จะมีอยู่ในประวัติการแก้ไข คอนโทรลเลอร์รุ่นใหม่มีตรรกะเดียวกันที่แน่นอนสำหรับการรันเกมความแตกต่างเพียงอย่างเดียวคือประสิทธิภาพการรวบรวมสถิติและการพิมพ์ที่ดีขึ้น

บอท

ในเครื่องของฉัน, forty_game_bots.pyบอทจะถูกเก็บไว้ในแฟ้ม หากคุณใช้ชื่ออื่นสำหรับไฟล์คุณต้องอัพเดตimportคำสั่งที่ด้านบนของตัวควบคุม

import sys, inspect
import random
import numpy as np

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

# The parent class for all bots
class Bot:

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

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

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


class ThrowTwiceBot(Bot):

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

class GoToTenBot(Bot):

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

ใช้การจำลอง

หากต้องการเรียกใช้การจำลองให้บันทึกข้อมูลโค้ดทั้งสองที่โพสต์ไว้ด้านบนเป็นไฟล์แยกกันสองไฟล์ ฉันได้ช่วยให้พวกเขาเป็นและforty_game_controller.py forty_game_bots.pyจากนั้นคุณเพียงแค่ใช้python forty_game_controller.pyหรือpython3 forty_game_controller.pyขึ้นอยู่กับการกำหนดค่า Python ของคุณ ทำตามคำแนะนำจากที่นั่นหากคุณต้องการกำหนดค่าการจำลองเพิ่มเติมหรือลองแก้ไขรหัสหากคุณต้องการ

สถิติของเกม

หากคุณกำลังสร้างบอทที่มีจุดมุ่งหมายเพื่อให้ได้คะแนนที่แน่นอนโดยไม่คำนึงถึงบ็อตอื่น ๆ สิ่งเหล่านี้จะเป็นคะแนนที่ชนะ:

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

คะแนนสูง

เมื่อมีการโพสต์คำตอบเพิ่มเติมฉันจะพยายามอัปเดตรายการนี้ต่อไป เนื้อหาของรายการจะมาจากการจำลองล่าสุดเสมอ บอทThrowTwiceBotและGoToTenBotบอทจากโค้ดด้านบนและใช้เป็นข้อมูลอ้างอิง ฉันจำลองด้วยเกม 10 ^ 8 ซึ่งใช้เวลาประมาณ 1 ชั่วโมง จากนั้นฉันเห็นว่าเกมมีความเสถียรเมื่อเปรียบเทียบกับเกมของฉันที่มีขนาด 10 ^ 7 เกม อย่างไรก็ตามด้วยผู้คนที่โพสต์บอทฉันจะไม่ทำการจำลองอีกต่อไปจนกว่าความถี่ของการตอบกลับจะลดลง

ฉันพยายามเพิ่มบ็อตใหม่ทั้งหมดและเพิ่มการเปลี่ยนแปลงใด ๆ ที่คุณทำกับบอทที่มีอยู่ หากดูเหมือนว่าฉันพลาดบอทของคุณหรือมีการเปลี่ยนแปลงใหม่ ๆ ให้เขียนในการแชทและฉันจะทำให้แน่ใจว่ามีเวอร์ชั่นล่าสุดของคุณในการจำลองครั้งต่อไป

ตอนนี้เรามีสถิติเพิ่มเติมสำหรับแต่ละบอทขอบคุณAKroell ! คอลัมน์ใหม่ทั้งสามคอลัมน์นี้มีคะแนนสูงสุดในทุกเกมคะแนนเฉลี่ยต่อเกมและคะแนนเฉลี่ยเมื่อชนะบอตแต่ละแห่ง

ตามที่ระบุไว้ในความคิดเห็นมีปัญหากับตรรกะของเกมซึ่งทำให้บอทที่มีดัชนีสูงกว่าในเกมได้รับรอบพิเศษในบางกรณี สิ่งนี้ได้รับการแก้ไขแล้วและคะแนนด้านล่างแสดงถึงสิ่งนี้

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

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

บอตต่อไปนี้ (ยกเว้นRebel) ทำขึ้นเพื่อทำให้กฏกติกาและผู้สร้างได้ตกลงที่จะไม่เข้าร่วมการแข่งขันอย่างเป็นทางการ อย่างไรก็ตามฉันยังคิดว่าความคิดของพวกเขามีความคิดสร้างสรรค์และพวกเขาสมควรได้รับการกล่าวถึงอย่างมีเกียรติ กบฏยังอยู่ในรายการนี้เพราะใช้กลยุทธ์ที่ฉลาดเพื่อหลีกเลี่ยงการก่อวินาศกรรมและประสิทธิภาพที่ดีขึ้นจริง ๆ กับบอทการก่อวินาศกรรมในการเล่น

บอตNeoBotและKwisatzHaderachทำตามกฎ แต่ใช้ช่องโหว่ด้วยการทำนายเครื่องกำเนิดแบบสุ่ม เนื่องจากบอทเหล่านี้ใช้ทรัพยากรจำนวนมากในการจำลองฉันจึงเพิ่มสถิติของมันจากการจำลองด้วยเกมที่น้อยลง บอทHarkonnenBotประสบความสำเร็จโดยการปิดบอทอื่น ๆ ทั้งหมดซึ่งขัดกับกฎอย่างเคร่งครัด

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

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

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

2
ดังนั้นกฎอาจจะชัดเจนขึ้นเล็กน้อยหากพวกเขาพูดว่า "เมื่อผู้เล่นจบเทิร์นของพวกเขาด้วยคะแนนอย่างน้อย 40 ทุกคนจะได้เทิร์นสุดท้าย" นี่เป็นการหลีกเลี่ยงความขัดแย้งที่ชัดเจนโดยการชี้ให้เห็นว่ามันไม่ถึง 40 ซึ่งเป็นต้นเหตุของรอบสุดท้ายมันหยุดอย่างน้อย 40
aschepler

1
@aschepler ที่เป็นสูตรที่ดีฉันจะแก้ไขโพสต์เมื่อฉันบนคอมพิวเตอร์ของฉัน
maxb

2
@maxb ฉันได้ขยายคอนโทรลเลอร์เพื่อเพิ่มสถิติเพิ่มเติมที่เกี่ยวข้องกับกระบวนการพัฒนาของฉัน: ได้คะแนนสูงสุดถึงคะแนนเฉลี่ยและคะแนนที่ชนะโดยเฉลี่ยgist.github.com/AwK/91446718a46f3e001c19533298b5756c
AKroell

2
ฟังดูคล้ายกับเกมลูกเต๋าที่สนุกมากที่มีชื่อว่า Farkled en.wikipedia.org/wiki/Farkle
Caleb Jay

5
ฉันโหวตให้ปิดคำถามนี้เพราะมันใกล้จะถึงคำตอบใหม่แล้ว ("การแข่งขันสิ้นสุดลงแล้ว! การจำลองขั้นสุดท้ายได้ดำเนินการในตอนกลางคืนรวมทั้งหมด 3 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

ฉันจะตรวจสอบการดำเนินการโดยเร็วที่สุดเท่าที่จะทำได้ ด้วยการเฉลิมฉลองวันคริสต์มาสมันอาจจะไม่เกิดขึ้นจนถึงวันที่ 25
สูงสุด

บอทของคุณเป็นผู้นำ! นอกจากนี้ไม่จำเป็นต้องทำให้มันทำงานได้เร็วขึ้นมันเร็วประมาณบอทอื่น ๆ ในการตัดสินใจ
maxb

ฉันไม่ต้องการทำให้เร็วขึ้น ฉันทำสิ่งที่ฉันต้องการแล้ว - เริ่มต้นเพียงครั้งเดียว - แต่กำลังมองหาวิธีที่ดีกว่าที่จะทำโดยเฉพาะอย่างยิ่งโดยไม่ต้องกำหนดฟังก์ชั่นนอกห้องเรียน ฉันคิดว่ามันจะดีกว่าตอนนี้
Christian Sievers

ตอนนี้มันดูดีขึ้นมากแล้วใช้งานได้ดี!
maxb

ขอแสดงความยินดีกับความปลอดภัยทั้งที่หนึ่งและสอง!
maxb

20

NeoBot

แต่ลองพยายามรู้ความจริง - ไม่มีช้อน

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
เนื่องจากมีช่องว่างขนาดใหญ่ระหว่างบ็อตนี้และที่สองบวกกับความจริงที่ว่าบอตของคุณต้องการการคำนวณจำนวนมากคุณจะยอมรับได้ไหมว่าฉันใช้การจำลองโดยมีการวนซ้ำน้อยลงเพื่อหาอัตราการชนะของคุณ จำลองสถานการณ์โดยไม่ต้องบอทของคุณ?
maxb

3
ไม่เป็นไรสำหรับฉันฉันคิดว่าการทำเช่นนี้น่าจะเป็นสิ่งที่ขาดคุณสมบัติและแน่นอนไม่ได้อยู่ในจิตวิญญาณของเกม ดังที่กล่าวมามันเป็นการระเบิดครั้งยิ่งใหญ่ในการทำงานและเป็นข้อแก้ตัวที่สนุกที่จะพูดถึงในซอร์สโค้ดของไพ ธ อน
ไม่เป็นอันตรายส่วนใหญ่

2
ขอบคุณ! ฉันไม่คิดว่าบ็อตอื่นจะเข้าใกล้คะแนนของคุณ และสำหรับใครที่คิดจะใช้กลยุทธ์นี้ จากนี้ไปกลยุทธ์นี้ขัดกับกฎและ NeoBot เป็นเพียงคนเดียวที่ได้รับอนุญาตให้ใช้เพื่อประโยชน์ในการรักษาความเป็นธรรมในการแข่งขัน
maxb

1
myBot เต้นทุกคน แต่มันดีกว่านี้มาก - แม้ว่าฉันจะโพสต์บอทแบบนี้ฉันจะได้รับ -100 และไม่ใช่คะแนนที่ดีที่สุด
Jan Ivan

15

กลุ่มสหกรณ์

กลยุทธ์

ฉันไม่คิดว่าจะมีใครสังเกตเห็นความสำคัญของกฎนี้:

หากเกมไปถึง 200 รอบบอท (หรือบอท) ที่มีคะแนนสูงสุดคือผู้ชนะแม้ว่าพวกเขาจะไม่มี 40 คะแนนหรือมากกว่า

หากบอททุกคนกลิ้งไปเรื่อย ๆ จนกว่าพวกเขาจะถูกจับจากนั้นทุกคนจะได้คะแนนเป็นศูนย์เมื่อจบรอบ 200 และทุกคนจะชนะ! ดังนั้นกลยุทธ์ของ Cooperative Swarm ก็คือการร่วมมือกันตราบเท่าที่ผู้เล่นทุกคนมีคะแนนเป็นศูนย์ แต่จะเล่นได้ตามปกติถ้ามีคนทำคะแนนใด ๆ

ในโพสต์นี้ฉันกำลังส่งบอทสองอัน: อันแรกคือ 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

การวิเคราะห์

การทำงานได้

มันยากมากที่จะให้ความร่วมมือในเกมนี้เพราะเราต้องการการสนับสนุนจากผู้เล่นทั้งแปดคนเพื่อให้มันทำงาน เนื่องจากคลาสบอทแต่ละคลาสมีข้อ จำกัด เพียงหนึ่งตัวอย่างต่อเกมนี่เป็นเป้าหมายที่ยากที่จะบรรลุ ตัวอย่างเช่นอัตราต่อรองในการเลือกแปดบอทแบบร่วมมือจากกลุ่ม 100 บอทแบบร่วมมือและ 30 แบบไม่ร่วมมือคือบอท:

100130991299812897127961269512594124931230.115

โดยทั่วไปอัตราต่อรองของการเลือกบอทสหกรณ์จากสระว่ายน้ำของบอทสหกรณ์และบอท noncooperative คือ:icn

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

จากสมการนี้เราสามารถแสดงให้เห็นได้อย่างง่ายดายว่าเราจะต้องมีบอตแบบร่วมมือกันประมาณ 430 บอทเพื่อให้เกม 50% จบการทำงานแบบร่วมมือกันหรือประมาณ 2,900 บอตสำหรับ 90% (ใช้ตามกฎและ )i=8n=38

กรณีศึกษา

ด้วยเหตุผลหลายประการ (ดูเชิงอรรถ 1 และ 2) กลุ่มสหกรณ์ที่เหมาะสมจะไม่แข่งขันในเกมทางการ เช่นนี้ฉันจะสรุปผลการจำลองสถานการณ์ของฉันในหัวข้อนี้

การจำลองนี้ดำเนินการ 10,000 เกมโดยใช้บอทอื่น ๆ 38 ตัวที่โพสต์ที่นี่ในครั้งสุดท้ายที่ฉันตรวจสอบและบอต 2900 ที่มี CooperativeSwarmBot เป็นคลาสแม่ของพวกเขา ผู้ควบคุมรายงานว่า 9051 จาก 10,000 เกม (90.51%) สิ้นสุดที่ 200 รอบซึ่งค่อนข้างใกล้เคียงกับการคาดการณ์ว่า 90% ของเกมจะร่วมมือกัน การดำเนินการของบอทเหล่านี้เป็นเรื่องเล็กน้อย นอกเหนือจาก CooperativeSwarmBot พวกเขาทั้งหมดได้รับแบบฟอร์มนี้

class CooperativeSwarm_1234(CooperativeSwarmBot):
    pass

น้อยกว่าที่บอต 3% มีเปอร์เซ็นต์การชนะที่ต่ำกว่า 80% และบอทมากกว่า 11% ชนะทุกเกมที่พวกเขาเล่น เปอร์เซ็นต์การชนะเฉลี่ยของ 2,900 บอตในฝูงเป็นเรื่องเกี่ยวกับ 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|
+---------------------+----+--------+--------+------+------+-------+------+--------+

ข้อบกพร่อง

มีข้อเสียสองประการสำหรับวิธีการสหกรณ์นี้ ประการแรกเมื่อเล่นกับบอทที่ไม่ใช่สหกรณ์บอทสหกรณ์จะไม่ได้รับประโยชน์จากเทิร์นแรกเพราะเมื่อพวกเขาเล่นก่อนพวกเขายังไม่รู้ว่าคู่ต่อสู้ของพวกเขาเต็มใจที่จะร่วมมือหรือไม่ดังนั้นจึงไม่มีทางเลือกอื่น คะแนนเป็นศูนย์ ในทำนองเดียวกันกลยุทธ์ความร่วมมือนี้มีความเสี่ยงสูงต่อการถูกเอารัดเอาเปรียบจากบ็อตที่เป็นอันตราย ตัวอย่างเช่นระหว่างการเล่นแบบร่วมมือกันบอทที่เล่นคนสุดท้ายในรอบสุดท้ายสามารถเลือกที่จะหยุดกลิ้งทันทีเพื่อให้ทุกคนสูญเสียอีก (แน่นอนว่าการหมุนครั้งแรกของพวกเขาไม่ใช่หก)

ด้วยการร่วมมือกันบอททั้งหมดสามารถบรรลุทางออกที่ดีที่สุดของอัตราการชนะ 100% ดังนั้นหากอัตราการชนะเป็นสิ่งเดียวที่สำคัญความร่วมมือจะเป็นดุลยภาพที่มั่นคงและไม่มีอะไรต้องกังวล อย่างไรก็ตามบ็อตบางตัวอาจจัดลำดับความสำคัญเป้าหมายอื่น ๆ เช่นไปถึงจุดสูงสุดของกระดานแต้มนำ ซึ่งหมายความว่ามีความเสี่ยงที่บอทอื่นอาจจะเสียไปหลังจากเทิร์นครั้งสุดท้ายของคุณซึ่งจะสร้างแรงจูงใจให้คุณเสียก่อน เนื่องจากการตั้งค่าของการแข่งขันนี้ไม่อนุญาตให้เราเห็นสิ่งที่คู่ต่อสู้ของเราทำในเกมก่อนหน้านี้เราจึงไม่สามารถลงโทษบุคคลที่เสียเปรียบได้ ดังนั้นในที่สุดความร่วมมือจึงเป็นจุดสมดุลที่ไม่มั่นคงสำหรับความล้มเหลว

เชิงอรรถ

[1]: สาเหตุหลักที่ทำไมฉันไม่ต้องการส่งพันบอทแทนที่จะแค่สองคนก็คือการทำเช่นนั้นจะทำให้การจำลองสถานการณ์ช้าลงโดยเรียงตามลำดับที่ 1,000 [2] และการทำเช่นนั้นจะยุ่งเหยิงอย่างมีนัยสำคัญ ชนะร้อยละเป็นบอทอื่น ๆ ที่เกือบจะเล่นกับฝูงมากกว่ากัน อย่างไรก็ตามที่สำคัญกว่านั้นคือความจริงที่ว่าแม้ว่าฉันต้องการฉันจะไม่สามารถทำให้บอทจำนวนมากในกรอบเวลาที่เหมาะสมโดยไม่ทำลายจิตวิญญาณของกฎว่า "บอทต้องไม่ใช้กลยุทธ์เดียวกันกับ อันที่มีอยู่โดยเจตนาหรือไม่ตั้งใจ ".

[2]: ฉันคิดว่ามีสองเหตุผลหลักที่การจำลองจะช้าลงเมื่อใช้งานกลุ่มที่ร่วมมือกัน ก่อนอื่นบอตมากขึ้นหมายถึงเกมมากขึ้นถ้าคุณต้องการให้บอทแต่ละตัวเล่นในจำนวนเกมเท่ากัน (ในกรณีศึกษาจำนวนเกมจะแตกต่างกันไปตามปัจจัยประมาณ 77) ประการที่สองเกมแบบมีส่วนร่วมใช้เวลานานกว่าเพราะเกมนี้จะเต็ม 200 รอบและผู้เล่นในรอบนั้นจะต้องกลิ้งไปเรื่อย ๆ สำหรับการตั้งค่าของฉันเกมใช้เวลาในการจำลองประมาณ 40 ครั้ง: กรณีศึกษาใช้เวลานานกว่าสามนาทีในการรันเกม 10,000 เกม แต่หลังจากลบการร่วมมือกันแล้วมันจะจบเกม 10,000 เกมในเวลาเพียง 4.5 วินาที ระหว่างสองเหตุผลนี้ฉันคาดว่าจะใช้เวลาประมาณ 3100 เท่าในการวัดประสิทธิภาพการทำงานของบอทอย่างแม่นยำเมื่อมีฝูงชนแข่งขันกันเมื่อเทียบกับเมื่อไม่มี


4
ว้าว. และยินดีต้อนรับสู่ PPCG นี่เป็นคำตอบแรกทีเดียว ฉันไม่ได้วางแผนเกี่ยวกับสถานการณ์เช่นนี้ แน่นอนคุณพบช่องโหว่ในกฎ ฉันไม่แน่ใจว่าฉันควรให้คะแนนแบบนี้อย่างไรเนื่องจากคำตอบของคุณคือชุดบอทมากกว่าบอทเดี่ยว อย่างไรก็ตามสิ่งเดียวที่ฉันจะพูดตอนนี้คือรู้สึกไม่ยุติธรรมที่ผู้เข้าร่วมคนหนึ่งจะควบคุม 98.7% ของบอททั้งหมด
maxb

2
จริง ๆ แล้วฉันไม่ต้องการให้บอทซ้ำซ้อนในการแข่งขันอย่างเป็นทางการ นั่นเป็นเหตุผลที่ฉันรันการจำลองด้วยตัวเองแทนที่จะส่งบอทที่เหมือนกันเกือบหลายพันตัว ฉันจะแก้ไขการส่งของฉันเพื่อให้ชัดเจนยิ่งขึ้น
Einhaender

1
หากฉันคาดว่าจะได้คำตอบเช่นนี้ฉันจะเปลี่ยนเกมที่มี 200 รอบเพื่อให้พวกเขาไม่ให้คะแนนกับผู้เล่น อย่างไรก็ตามตามที่คุณทราบมีกฎเกี่ยวกับการสร้างบอทที่เหมือนกันซึ่งจะทำให้กลยุทธ์นี้ขัดต่อกฎ ฉันจะไม่เปลี่ยนกฎเพราะมันจะไม่ยุติธรรมกับทุกคนที่ทำบอท อย่างไรก็ตามแนวคิดของความร่วมมือนั้นน่าสนใจมากและฉันหวังว่าจะมีบอทอื่น ๆ ที่ส่งซึ่งใช้กลยุทธ์ความร่วมมือร่วมกับกลยุทธ์เฉพาะของตัวเอง
maxb

1
ฉันคิดว่าโพสต์ของคุณชัดเจนหลังจากอ่านมันอย่างละเอียดมากขึ้น
maxb

จำนวนบอตที่มีอยู่จะต้องห่อรหัสของพวกเขาในกรอบความร่วมมือนี้เพื่อให้พวกเขาส่วนใหญ่เห็นกำไรสุทธิในตำแหน่งลีดเดอร์ของพวกเขา? เดาไร้เดียงสาของฉันคือ 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

ลองเล่นกับทุกคนGoToNBotและเล่นได้ดีที่สุด 20, 22, 24 ฉันไม่รู้ว่าทำไม


อัพเดท: หยุดโยนเสมอถ้าได้ 40 คะแนนขึ้นไป


ฉันได้ทดลองกับบอทประเภทนั้นด้วย คะแนนเฉลี่ยสูงสุดต่อรอบพบได้เมื่อบอทถึง 16 แต่ฉันสมมติว่า "เกมจบ" ทำให้การชนะบอต 20 ครั้งบ่อยขึ้น
maxb

@ max ไม่เช่นนั้น 20 ยังคงเป็นเกมที่ดีที่สุดหากไม่มี "เกมจบ" ในการทดสอบของฉัน บางทีคุณอาจทดสอบกับคอนโทรลเลอร์เวอร์ชันเก่า
tsh

ฉันทำการทดสอบแยกต่างหากก่อนที่จะออกแบบการท้าทายนี้โดยที่ฉันคำนวณคะแนนเฉลี่ยต่อรอบสำหรับกลยุทธ์ทั้งสองในโพสต์ของฉัน ("โยน x คูณ" และ "โยนจนถึงคะแนน x") และค่าสูงสุดที่ฉันพบคือ 15-16 . แม้ว่าขนาดตัวอย่างของฉันอาจเล็กเกินไปฉันก็สังเกตเห็นความไม่แน่นอน
maxb

2
ฉันได้ทำการทดสอบนี้แล้วและข้อสรุปของฉันก็คือ 20 ใช้ได้ดีเพราะมันคือ 40/2 แม้ว่าฉันจะไม่แน่ใจอย่างสมบูรณ์ เมื่อฉันตั้งค่าend_scoreเป็น 4000 (และเปลี่ยนบอทของคุณเพื่อใช้ในการtargetคำนวณ) บอท 15-16 นั้นค่อนข้างดีกว่ามาก แต่ถ้าเกมนี้เกี่ยวกับการเพิ่มคะแนนของคุณมันจะไม่สำคัญ
maxb

1
@maxb หากend_scoreเป็น 4000 ก็แทบจะเป็นไปไม่ได้ที่จะได้รับ 4000 ก่อนถึง 200 รอบ และเกมนี้เป็นเพียงผู้ที่ได้คะแนนสูงสุดในรอบ 200 และหยุดที่ 15 ควรจะทำงานตั้งแต่เวลานี้กลยุทธ์สำหรับคะแนนสูงสุดในหนึ่งเทิร์นก็เหมือนกับคะแนนสูงสุดในรอบ 200
tsh

10

Adaptive Roller

เริ่มก้าวร้าวมากขึ้นและสงบลงจนจบรอบ
หากเชื่อว่าชนะการแข่งขันให้หมุนเวลาพิเศษเพื่อความปลอดภัย

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 แก้ไขขอบคุณ
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

แนวคิดก็คือบอตอื่นอาจเสียคะแนนดังนั้นการเป็นอันดับที่สองก็ไม่เลวเลยทีเดียว แต่ถ้าคุณอยู่ด้านหลังมากคุณก็อาจแพ้ได้


1
ยินดีต้อนรับสู่ PPCG! ฉันกำลังดูสิ่งที่คุณส่งมาและดูเหมือนว่ายิ่งมีผู้เล่นอยู่ในเกมมากเท่าไรเปอร์เซ็นต์การชนะก็จะยิ่งน้อยลงสำหรับบอทของคุณ ฉันบอกไม่ได้เลยว่าทำไม ด้วยบอตที่ถูกจับคู่ 1vs1 คุณจะได้รับ winrate 10% แนวคิดนี้ดูดีและรหัสก็ดูถูกต้องดังนั้นฉันจึงไม่สามารถบอกได้อย่างชัดเจนว่าทำไมการชนะของคุณจึงไม่สูงกว่านี้
maxb

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

ขอบคุณ - จะทำการเปลี่ยนแปลงนั้น!
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

เราต้องการที่จะไปใหญ่หรือกลับบ้านใช่ไหม? GoHomeBot ส่วนใหญ่เพิ่งกลับบ้าน (แต่ทำได้ดีอย่างน่าประหลาดใจ!)


เนื่องจากบอทนี้มีคะแนน 40 คะแนนเสมอจึงไม่มีคะแนนในscoresรายการ มีบอทแบบนี้มาก่อน (ธ ปทของ GoToEnd) แต่เดวิดลบคำตอบของพวกเขา ฉันจะแทนที่บอทนั้นด้วยตัวคุณเอง
maxb

1
มันค่อนข้างตลกเมื่อได้เห็นสถิติที่ขยายออกของบอท: ยกเว้นคะแนนล่วงหน้าสำหรับบอทและบอทบ็อตนี้มีคะแนนเฉลี่ยต่ำสุดและยังมีอัตราการชนะที่ดี
Belhenix

5

EnsureLead

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 ยืมแนวคิดจาก 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! น่าประทับใจมากไม่เพียง แต่เป็นการท้าทายครั้งแรกของคุณเท่านั้น ดีใจที่คุณชอบเกม! ฉันมีการสนทนามากมายเกี่ยวกับกลยุทธ์ที่ดีที่สุดสำหรับเกมหลังจากตอนเย็นเมื่อฉันเล่นเกมดังนั้นมันจึงเหมาะสำหรับการท้าทาย ขณะนี้คุณอยู่อันดับที่สามจาก 18
สูงสุด

4

StopBot

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

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

BinaryBot

พยายามเข้าใกล้คะแนนสุดท้ายเพื่อให้ทันทีที่มีคนทริกเกอร์รอบสุดท้ายมันสามารถเอาชนะคะแนนของพวกเขาสำหรับการชนะ เป้าหมายอยู่กึ่งกลางระหว่างคะแนนปัจจุบันและคะแนนสิ้นสุด

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

PointsAreForNerdsBot

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 ด้านของตัวเอง ห้าน้อยกว่าหกจึงจะชนะ!


2
ยินดีต้อนรับสู่ PPCG! ฉันแน่ใจว่าคุณรู้ แต่บอทแรกของคุณคือบอทที่แย่ที่สุดในการแข่งขันนี้! OneInFiveBotเป็นความคิดที่เรียบร้อย แต่ฉันคิดว่ามันทรมานในช่วงท้ายเกมเมื่อเทียบกับบางส่วนของบอทที่สูงขึ้น ยังคงเป็นสิ่งที่ยอดเยี่ยม!
maxb

2
OneInFiveBotค่อนข้างน่าสนใจในทางที่เขาอย่างต่อเนื่องมีคะแนนรวมสูงสุดถึง
AKroell

1
ขอบคุณที่ให้StopBotถุงเจาะ: P OneInFiveBot ที่จริงแล้วค่อนข้างเรียบร้อยเป็นงานที่ดี!
Zacharý

@ maxb อ๋อนั่นคือสิ่งที่ฉันได้รับชื่อ ฉันไม่ได้ทดสอบอย่างตรงไปตรงมาOneInFiveBotและทำได้ดีกว่าที่คาดไว้
The_Bob

4
ฉันกลัวว่าจะติดบรรทัดฐานชุมชน PointsAreForNerdsBots ควรจะถูกลบ
Peter Taylor

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แต่บอทของคุณก็ค่อนข้างดี ฉันจะอัปเดตคะแนนในภายหลังในคืนนี้หรือพรุ่งนี้ แต่ winrate ของคุณจะอยู่ที่ประมาณ 15% ซึ่งสูงกว่าค่าเฉลี่ย 12.5%
maxb

โดย "เวลาที่ยากลำบาก" พวกเขาหมายถึงเป็นไปไม่ได้ (เว้นแต่ฉันจะเข้าใจผิดอย่างมาก)
Zacharý

@maxb จริง ๆ แล้วฉันไม่คิดว่าอัตราการชนะจะสูงมาก! (ฉันไม่ได้ทดสอบเฉพาะที่) ฉันสงสัยว่าการเปลี่ยน 50 ให้สูงขึ้นหรือต่ำลงเล็กน้อยจะเพิ่มอัตราการชนะหรือไม่
lizduadac

3

SlowStart

บอตนี้ใช้อัลกอริทึมเริ่มช้า TCP มันปรับจำนวนม้วน ( หรือ ) ตามเทิร์นก่อน: ถ้าไม่หมุน 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
นอกจากนี้ฉันคิดว่าคุณจำเป็นต้องปรับปรุงiตัวแปรของคุณในขณะที่ห่วง ตอนนี้บอทของคุณจะผ่านขณะที่ลูปทั้งหมดหรือติดอยู่ในขณะที่ลูปจนกว่ามันจะฮิต 6
maxb

ในคะแนนสูงสุดปัจจุบันฉันใช้เสรีภาพในการดำเนินการเปลี่ยนแปลงเหล่านี้ ฉันคิดว่าคุณสามารถทดสอบด้วยค่าเริ่มต้นself.norและดูว่ามันมีผลต่อประสิทธิภาพของบอทของคุณอย่างไร
maxb

3

KwisatzHaderach

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

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

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

ความรู้สึกผิดชอบชั่วดีชนะ แต่โชคชะตาไม่สามารถหลีกเลี่ยงได้
วิธีที่ยิ่งใหญ่และลึกลับของ 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เข้าสู่ระยะฉันเริ่มคิดว่าจะเอาชนะความสามารถที่สมบูรณ์แบบในการทำนายอนาคตได้อย่างไร ดังนั้นนี่คือการอ้างอิงทราย เมื่อ Paul Atreides, Kwisatz Haderach ยืนอยู่ที่ Nexus ซึ่งอินฟินิตี้ต่าง ๆ ของฟิวเจอร์สสามารถคลี่คลาย:

เขาตระหนักถึงความรู้สึกผิดชอบชั่วดีเป็นความสว่างที่รวมขอบเขตของสิ่งที่เปิดเผยออกมาในทันทีที่เป็นแหล่งของความแม่นยำและข้อผิดพลาดที่มีความหมาย ชนิดของไฮเซนเบิร์กไม่ จำกัด การแทรกแซง: ค่าใช้จ่ายของพลังงานที่เปิดเผยสิ่งที่เขาเห็นเปลี่ยนสิ่งที่เขาเห็น ... ... การกระทำในนาทีที่มากที่สุด - พริบตา, คำที่ประมาทเลินเล่อ จักรวาลที่รู้จักกัน เขาเห็นความรุนแรงกับผลลัพธ์นั้นขึ้นอยู่กับตัวแปรมากมายที่การเคลื่อนไหวเพียงเล็กน้อยของเขาทำให้เกิดการเปลี่ยนแปลงอย่างมากมายในรูปแบบ

นิมิตทำให้เขาต้องการที่จะหยุดความไม่สามารถเคลื่อนไหวได้ แต่นี่ก็เป็นการกระทำที่เกิดขึ้นตามมา

ดังนั้นนี่คือคำตอบ: การคาดการณ์อนาคตคือการเปลี่ยนแปลง และหากคุณระมัดระวังอย่างมากจากการกระทำที่เลือกหรือไม่ทำอะไรคุณสามารถเปลี่ยนได้ด้วยวิธีที่ได้เปรียบ - อย่างน้อยก็เกือบตลอดเวลา แม้แต่ผู้ที่KwisatzHaderachยังไม่ได้รับอัตราการชนะ 100%


ดูเหมือนว่าบ็อตนี้จะเปลี่ยนสถานะของตัวสร้างตัวเลขสุ่มเพื่อให้แน่ใจว่ามันหลีกเลี่ยงการหมุน 6 หรืออย่างน้อยก็คาดหวัง กันไปสำหรับ HarkonnenBot อย่างไรก็ตามฉันทราบว่าอัตราการชนะของบอทเหล่านี้สูงกว่า NeoBot มาก คุณกำลังจัดการตัวสร้างหมายเลขสุ่มเพื่อป้องกันไม่ให้เกิดการหมุน 6 หรือไม่?
maxb

โอ้ในการอ่านครั้งแรกของฉันฉันไม่ได้สังเกตว่านี่ไม่เพียง แต่ดีกว่าNeoBotแต่ยังดีกว่า! ฉันชอบวิธีที่คุณให้ตัวอย่างของทุกสิ่งที่ใช้การสุ่ม (โดยrandom.Randomเฉพาะตัวควบคุม) ที่นี่ควรทำ: ใช้อินสแตนซ์ของคุณเอง เช่นNeoBotนี้ดูเหมือนว่าจะมีความอ่อนไหวต่อการเปลี่ยนแปลงรายละเอียดการใช้งานที่ไม่ได้ระบุของคอนโทรลเลอร์
Christian Sievers

@maxb: HarkonnenBotไม่ได้สัมผัส RNG; มันไม่สนใจเลยเกี่ยวกับตัวเลขสุ่ม มันเป็นเพียงบอทอื่น ๆ ที่เป็นพิษแล้วเดินไปถึงเส้นชัยให้ช้าที่สุด เช่นเดียวกับอาหารจานเด็ดมากมายการล้างแค้นเป็นอาหารจานที่ดีที่สุดอย่างช้าๆหลังจากเตรียมมานานและละเอียดอ่อน
Dani O

@ChristianSievers: ซึ่งแตกต่างจาก NeoBot (และHarkonnenBot) KwisatzHaderachอาศัยเพียงหนึ่งรายละเอียดของการดำเนินการ; โดยเฉพาะอย่างยิ่งไม่จำเป็นต้องทราบว่ามีการใช้งาน random.random อย่างไรเฉพาะเมื่อคอนโทรลเลอร์ใช้มัน D
Dani O

1
ฉันได้ตรวจสอบบอทของคุณทั้งหมดแล้ว ฉันได้ตัดสินใจที่จะรักษาKwisatzHaderachและวิธีเดียวกับHarkonnenBot NeoBotพวกเขาจะได้รับคะแนนจากการจำลองด้วยเกมที่น้อยลงและจะไม่อยู่ในการจำลองอย่างเป็นทางการ อย่างไรก็ตามพวกเขาจะจบลงในรายการคะแนนสูงสุดเหมือนNeoBotกัน เหตุผลหลักสำหรับพวกเขาที่ไม่ได้อยู่ในการจำลองอย่างเป็นทางการคือพวกเขาจะทำให้กลยุทธ์บอทอื่น ๆ ยุ่งเหยิง อย่างไรก็ตาม WisdomOfCrowdsควรเหมาะสำหรับการมีส่วนร่วมและฉันอยากรู้เกี่ยวกับการเปลี่ยนแปลงใหม่ที่คุณทำไว้!
maxb

2
class ThrowThriceBot(Bot):

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

นั่นเป็นสิ่งที่ชัดเจน


ฉันได้ทำการทดลองกับบอทระดับนั้น (มันเป็นกลยุทธ์ที่ฉันใช้เมื่อฉันเล่นเกมเป็นครั้งแรก) ฉันไปด้วยการโยน 4 ครั้งแม้ว่าจะมีคะแนนเฉลี่ยสูงกว่า 5-6 ต่อรอบ
maxb

ขอแสดงความยินดีกับคำตอบ 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 ทำตัวเหมือนมันเป็นรอบสุดท้ายเสมอและเป็นบอทสุดท้าย: มันจะกลิ้งไปเรื่อย ๆ จนกว่ามันจะเป็นผู้นำ นอกจากนี้ยังไม่ต้องการชำระน้อยกว่า 15 คะแนนเว้นแต่ว่ามันจะเป็นรอบสุดท้ายหรือถึง 40 คะแนน


แนวทางที่น่าสนใจ ฉันคิดว่าบอทของคุณทนทุกข์ทรมานหากมันเริ่มตก เนื่องจากโอกาสที่จะได้รับ> 30 คะแนนในรอบเดียวต่ำบอทของคุณมีแนวโน้มที่จะอยู่ที่คะแนนปัจจุบันมากขึ้น
maxb

1
ฉันสงสัยว่านี่เป็นความผิดพลาดแบบเดียวกับที่ฉันทำ (ดูความคิดเห็น NotTooFarBehindBot) - ในรอบสุดท้ายหากคุณไม่ชนะคุณจะต้องขว้างต่อไปจนกว่าคุณจะได้ 6 (คะแนน [self.index] ไม่เคยอัพเดท) จริง ๆ - คุณมีความไม่เท่าเทียมนั้นในทางที่ผิดหรือไม่? สูงสุด (คะแนน) จะเป็น> = score [self.index]
Stuart Moore

@ StuartMoore ฮ่าฮ่าใช่ฉันคิดว่าคุณพูดถูก ขอบคุณ!
Spitemaster

ฉันสงสัยว่าคุณต้องการ "and last_round" ในวันที่ 2 ในขณะที่ทำสิ่งที่คุณต้องการ - ไม่เช่นนั้น 2nd จะใช้เวลาไม่ว่า last_round จะเป็นจริงหรือไม่
Stuart Moore

3
นั่นเป็นความตั้งใจ มันมักจะพยายามเป็นผู้นำเมื่อจบเทิร์นของมัน
Spitemaster

2

QuotaBot

ระบบ "โควต้า" ที่ไร้เดียงสาที่ฉันนำมาใช้ซึ่งดูเหมือนว่าจะให้คะแนนโดยรวมค่อนข้างสูง

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

ExpectationsBot

เพียงแค่เล่นมันตรงคำนวณค่าที่คาดหวังสำหรับการโยนลูกเต๋าและจะทำให้มันถ้ามันเป็นบวก

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' ไม่ได้ถูกกำหนด" บนมัลติเธรดดังนั้นจึงไม่รู้เลยว่ามันทำงานอย่างไร


1
ฉันคิดว่าสิ่งนี้จบลงด้วยการเทียบเท่าบอท "ไปที่ 16" แต่เรายังไม่ได้เป็นหนึ่งในนั้น
Stuart Moore

1
@ StuartMoore นั่นคือ ... เป็นจุดที่แท้จริงมากใช่
Cain

ฉันพบปัญหาของคุณกับคอนโทรลเลอร์เมื่อฉันเรียกใช้บนเครื่อง Windows ของฉัน อย่างใดก็วิ่งได้ดีบนเครื่อง Linux ของฉัน ฉันกำลังอัปเดตคอนโทรลเลอร์และจะอัปเดตโพสต์เมื่อเสร็จสิ้น
maxb

@maxb ขอบคุณอาจมีบางอย่างเกี่ยวกับตัวแปรที่มีอยู่ในกระบวนการที่แตกต่างกัน FYI ได้อัปเดตสิ่งนี้ด้วยฉันทำข้อผิดพลาดโง่ ๆ เกี่ยวกับการให้ผลตอบแทน: /
Cain


2

FortyTeen

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

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

ลอง 14 คะแนนจนกระทั่งรอบสุดท้ายจากนั้นสมมติว่าทุกคนกำลังลอง 14 คะแนนแล้วลองผูกคะแนนนั้น


ฉันไปTypeError: unsupported operand type(s) for -: 'list' and 'int'กับบอทของคุณ
tsh

ฉันสมมติว่าคุณmax_projected_scoreควรจะเป็นสูงสุดของรายการแทนที่จะเป็นทั้งรายการฉันถูกต้องหรือไม่ มิฉะนั้นฉันจะได้รับปัญหาเช่นเดียวกับ tsh
maxb

โอ๊ะโอแก้ไขเพื่อแก้ไข
ฮิสโทแคต

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)

นี่เป็นสิ่งที่สวยงามมาก :) ขอแสดงความยินดีกับการได้อันดับที่หนึ่งและสองในการแข่งขันหลัก!
Dani O

โดยธรรมชาติฉันได้ปรับแต่งHarkonnenBotเพื่อที่Rebelจะไม่ปลดเปลื้องตัวเองอีกต่อไป;) และฉันยังได้ปรับแต่ง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

ครึ่งหนึ่งเราจะหมุน 5 ก่อน 6 เมื่อเราทำออกมาเป็นเงินสด


หากเราหยุดที่ 1 แทนมันจะทำให้ความคืบหน้าช้าลง แต่มีแนวโน้มมากขึ้นที่จะถึง 40 ในขอบเขตเดียว
Mnemonic

ในการทดสอบของฉัน TakeOne ได้คะแนน 20.868 คะแนนต่อรอบเมื่อเทียบกับ TakeFive ที่ 24.262 คะแนนต่อรอบ ดังนั้นฉันไม่คิดว่ามันจะคุ้มค่า
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 คะแนนสำหรับการวัดที่ดีเขาจะโยนอย่างน้อยสี่ครั้งไม่ว่าอะไรก็ตาม

[แก้ไข 1: เพิ่มกลยุทธ์ go-for-gold ในรอบสุดท้าย]

[แก้ไข 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

ขอบคุณ! ฉันอยากรู้ว่ามันจะทำอย่างไรดี แต่ฉันเดาว่ามันจะอยู่ด้านล่าง
william porter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.