पासा का खेल, लेकिन नंबर 6 से बचें [बंद]


58

टूर्नामेंट खत्म!

टूर्नामेंट अब खत्म हो गया है! अंतिम सिमुलेशन रात के दौरान की कुल चलाया गया था, 3108 खेल। विजेता अपने बॉट OptFor2X के साथ क्रिश्चियन सिवर्स है । क्रिश्चियन सिवर्स भी रेबेल के साथ दूसरे स्थान को सुरक्षित करने में कामयाब रहे । बधाई हो! नीचे आप टूर्नामेंट के लिए आधिकारिक उच्च स्कोर सूची देख सकते हैं।

यदि आप अभी भी गेम खेलना चाहते हैं, तो नीचे दिए गए नियंत्रक का उपयोग करने के लिए, और अपना गेम बनाने के लिए इसमें कोड का उपयोग करने के लिए आपका स्वागत है।

पासा

मुझे पासा का खेल खेलने के लिए आमंत्रित किया गया था, जिसके बारे में मैंने कभी नहीं सुना था। नियम सरल थे, फिर भी मुझे लगता है कि यह कोठारी चुनौती के लिए एकदम सही होगा।

नियम

खेल की शुरुआत

डाई टेबल के चारों ओर जाती है, और हर बार जब आपकी बारी होती है, तो आप जितनी बार चाहें उतनी बार थ्रो फेंकते हैं। हालांकि, आपको इसे कम से कम एक बार फेंकना होगा। आप अपने राउंड के लिए सभी थ्रो के योग का ट्रैक रखते हैं। यदि आप रोकना चुनते हैं, तो राउंड के लिए स्कोर आपके कुल स्कोर में जोड़ा जाता है।

तो आप कभी भी मरना बंद क्यों नहीं करेंगे? क्योंकि अगर आपको 6 मिलते हैं, तो पूरे दौर के लिए आपका स्कोर शून्य हो जाता है, और मर जाता है। इस प्रकार, प्रारंभिक लक्ष्य अपने स्कोर को जल्द से जल्द बढ़ाना है।

विजेता कौन है?

जब टेबल के चारों ओर पहला खिलाड़ी 40 अंक या उससे अधिक तक पहुंच जाता है, तो अंतिम दौर शुरू होता है। एक बार अंतिम दौर शुरू हो जाने के बाद, अंतिम दौर की शुरुआत करने वाले व्यक्ति को छोड़कर सभी को एक और मोड़ मिलता है।

अंतिम राउंड के नियम किसी अन्य राउंड के लिए समान हैं। आप फेंकने या रोकने के लिए चुनते हैं। हालाँकि, आप जानते हैं कि आपके पास जीतने का कोई मौका नहीं है यदि आपको अंतिम राउंड पर अपने से अधिक अंक नहीं मिलते हैं। लेकिन अगर आप बहुत दूर जा रहे हैं, तो आपको 6 मिल सकते हैं।

हालांकि, ध्यान में रखने के लिए एक और नियम है। यदि आपका वर्तमान कुल स्कोर (आपका पिछला स्कोर + राउंड के लिए आपका वर्तमान स्कोर) 40 या अधिक है, और आपने 6 स्कोर किया है , तो आपका कुल स्कोर 0. पर सेट होता है। इसका मतलब है कि आपको सभी शुरू करना होगा। यदि आप एक 6 मारा जब आपके वर्तमान कुल स्कोर 40 या अधिक है, तो खेल सामान्य रूप से जारी है, सिवाय इसके कि अब आप अंतिम स्थान पर हैं। जब आपका कुल स्कोर रीसेट हो जाता है तो अंतिम दौर शुरू नहीं होता है। आप अभी भी राउंड जीत सकते हैं, लेकिन यह अधिक चुनौतीपूर्ण हो जाता है।

आखिरी राउंड खत्म होने के बाद विजेता सबसे अधिक अंक वाला खिलाड़ी होता है। यदि दो या अधिक खिलाड़ी एक ही स्कोर साझा करते हैं, तो वे सभी विजेता के रूप में गिने जाएंगे।

एक अतिरिक्त नियम यह है कि खेल अधिकतम 200 राउंड के लिए जारी रहता है। यह उन मामलों को रोकने के लिए है जहां कई बॉट मूल रूप से फेंकते रहते हैं जब तक कि वे अपने वर्तमान स्कोर पर बने रहने के लिए 6 से टकराते हैं। एक बार 199 वां राउंड बीत जाने के बाद, last_roundइसे सही पर सेट किया जाता है और एक और राउंड खेला जाता है। यदि खेल 200 राउंड में जाता है, तो बॉट (या बॉट) उच्चतम स्कोर के साथ विजेता होता है, भले ही उनके पास 40 अंक या अधिक न हों।

संक्षिप्त

  • प्रत्येक राउंड आप तब तक मरते रहते हैं जब तक कि आप रुकना नहीं चुनते या आपको 6 नहीं मिलते
  • आपको एक बार मरना होगा (यदि आपका पहला फेंक 6 है, तो आपका दौर तुरंत समाप्त हो जाएगा)
  • यदि आपको एक 6 मिलता है, तो आपका वर्तमान स्कोर 0 पर सेट होता है (आपका कुल स्कोर नहीं)
  • आप प्रत्येक दौर के बाद अपने वर्तमान स्कोर को अपने कुल स्कोर में जोड़ते हैं
  • जब एक बॉट अपनी बारी समाप्त करता है, जिसके परिणामस्वरूप कम से कम 40 का कुल स्कोर होता है, तो बाकी सभी को अंतिम बारी मिलती है
  • अपने वर्तमान कुल स्कोर है, तो 40 और आप एक 6 मिलता है, अपने कुल स्कोर 0 पर सेट है और अपने दौर खत्म हो गया है
  • अंतिम दौर ट्रिगर नहीं होता है जब ऊपर होता है
  • अंतिम राउंड के बाद उच्चतम कुल स्कोर वाला व्यक्ति विजेता है
  • यदि कई विजेता हैं, तो सभी को विजेता के रूप में गिना जाएगा
  • खेल अधिकतम 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यदि आप अधिक वर्ग गुण जोड़ना चाहते हैं, तो आप मूल वर्ग में मौजूद फ़ंक्शन को भी ओवरराइड कर सकते हैं। आपको पैदावार को छोड़कर Trueया किसी भी तरह से खेल की स्थिति को संशोधित करने की अनुमति नहीं है False

आप इस पोस्ट से प्रेरणा लेने के लिए स्वतंत्र हैं, और उन दो बॉट्स में से किसी को भी कॉपी कर सकते हैं जिन्हें मैंने यहाँ शामिल किया है। हालांकि, मुझे डर है कि वे विशेष रूप से प्रभावी नहीं हैं ...

अन्य भाषाओं की अनुमति देने पर

सैंडबॉक्स और उन्नीसवीं बाइट पर, हमने अन्य भाषाओं में प्रस्तुतियाँ देने की चर्चा की है। इस तरह के कार्यान्वयन के बारे में पढ़ने, और दोनों पक्षों से तर्क सुनने के बाद, मैंने इस चुनौती को केवल पायथन तक सीमित रखने का फैसला किया है। यह दो कारकों के कारण है: कई भाषाओं का समर्थन करने के लिए आवश्यक समय, और इस चुनौती की यादृच्छिकता को स्थिरता तक पहुंचने के लिए अधिक संख्या में पुनरावृत्तियों की आवश्यकता होती है। मुझे उम्मीद है कि आप अभी भी भाग लेंगे, और यदि आप इस चुनौती के लिए कुछ पायथन सीखना चाहते हैं, तो मैं चैट में उपलब्ध होने की कोशिश करूंगा।

आपके पास किसी भी प्रश्न के लिए, आप इस चुनौती के लिए चैट रूम में लिख सकते हैं । वहाँ मिलते हैं!

नियम

  • सबोटेज की अनुमति है, और प्रोत्साहित किया। यानी दूसरे खिलाड़ियों के खिलाफ तोड़फोड़
  • नियंत्रक, रन-टाइम या अन्य सबमिशन के साथ छेड़छाड़ करने का कोई भी प्रयास अयोग्य होगा। सभी प्रस्तुतियाँ केवल उन इनपुट और भंडारण के साथ काम करना चाहिए जो उन्हें दिए गए हैं।
  • कोई भी बॉट जो अपना निर्णय लेने के लिए 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 है कभी नहीं कहा जाता है, क्योंकि आपका दौर तुरंत खत्म हो गया है।

उन लोगों के लिए जो पाइथन के लिए नए हैं (और yieldअवधारणा के लिए नए हैं ), लेकिन यह इसे देना चाहते हैं, yieldकीवर्ड कुछ तरीकों से वापसी के समान है, लेकिन अन्य तरीकों से अलग है। आप यहां अवधारणा के बारे में पढ़ सकते हैं । मूल रूप से, एक बार जब आप yield, आपका कार्य बंद हो जाएगा, और आपके द्वारा yieldसंपादित मूल्य नियंत्रक को वापस भेज दिया जाएगा। वहां, नियंत्रक अपने तर्क को संभालता है जब तक कि आपके बॉट के लिए एक और निर्णय लेने का समय नहीं है। फिर नियंत्रक आपको पासा फेंकता है, और आपका make_throwकार्य सही तरीके से निष्पादित करना जारी रखेगा जहां पहले बंद कर दिया गया था, मूल रूप से पिछले yieldबयान के बाद लाइन पर ।

इस तरह, खेल नियंत्रक प्रत्येक पासा फेंक के लिए एक अलग बॉट फ़ंक्शन कॉल की आवश्यकता के बिना राज्य को अपडेट कर सकता है।

विशिष्टता

आप किसी भी उपलब्ध पाइथन लाइब्रेरी का उपयोग कर सकते हैं pip। यह सुनिश्चित करने के लिए कि मैं एक अच्छा औसत प्राप्त करने में सक्षम हूं, आपके पास प्रति चक्कर में 100 मिलीसेकंड समय सीमा है। अगर आपकी पटकथा इससे कहीं तेज होती, तो मुझे बहुत खुशी होती, ताकि मैं और दौर चला सकूं।

मूल्यांकन

विजेता को खोजने के लिए, मैं सभी बॉट ले जाऊंगा और उन्हें 8 के यादृच्छिक समूहों में चलाऊंगा। यदि 8 से कम कक्षाएं जमा की जाती हैं, तो मैं उन्हें प्रत्येक दौर में सभी बॉट्स से बचने के लिए 4 के यादृच्छिक समूहों में चलाऊंगा। मैं लगभग 8 घंटे के लिए सिमुलेशन चलाऊंगा, और विजेता सबसे अधिक जीत प्रतिशत के साथ बॉट होगा। मैं 2019 की शुरुआत में अंतिम सिमुलेशन शुरू करूंगा, जिससे आप सभी क्रिसमस को अपने बॉट्स को कोड कर सकेंगे! प्रारंभिक अंतिम तारीख 4 जनवरी है, लेकिन अगर यह बहुत कम समय है तो मैं इसे बाद की तारीख में बदल सकता हूं।

तब तक, मैं 30-60 मिनट के CPU समय और स्कोर बोर्ड को अपडेट करके एक दैनिक सिमुलेशन बनाने की कोशिश करूंगा। यह आधिकारिक स्कोर नहीं होगा, लेकिन यह देखने के लिए एक मार्गदर्शिका के रूप में काम करेगा कि कौन से बॉट सर्वश्रेष्ठ प्रदर्शन करते हैं। हालाँकि, क्रिसमस आने के साथ, मुझे आशा है कि आप समझ सकते हैं कि मैं हर समय उपलब्ध नहीं रहूँगा। मैं सिमुलेशन चलाने और चुनौती से संबंधित किसी भी सवाल का जवाब देने की पूरी कोशिश करूंगा।

इसे स्वयं परखें

यदि आप अपने स्वयं के सिमुलेशन चलाना चाहते हैं, तो दो उदाहरण बॉट सहित सिमुलेशन चलाने वाले नियंत्रक का पूर्ण कोड यहां है।

नियंत्रक

यहाँ इस चुनौती के लिए अद्यतन नियंत्रक है। यह ANSI आउटपुट, मल्टी-थ्रेडिंग का समर्थन करता है, और AKroell के लिए अतिरिक्त आँकड़े एकत्र करता है ! जब मैं कंट्रोलर में बदलाव करता हूं, तो डाक्यूमेंट पूरा होने के बाद मैं पोस्ट अपडेट कर दूंगा।

बीएमओ के लिए धन्यवाद , नियंत्रक अब -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 अपने पाइथन विन्यास पर निर्भर करते हैं। यदि आप अपने सिमुलेशन को आगे कॉन्फ़िगर करना चाहते हैं, या यदि आप चाहते हैं तो कोड के साथ छेड़छाड़ करने की कोशिश करें।

खेल आँकड़े

यदि आप एक बॉट बना रहे हैं जिसका उद्देश्य अन्य बॉट्स को ध्यान में रखते हुए एक निश्चित स्कोर के लिए है, तो ये जीतने वाले स्कोर प्रतिशत हैं:

+----------+-----+
|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 के साथ
रुक रहा है।

1
@aschepler यह एक अच्छा सूत्रीकरण है, जब मैं अपने कंप्यूटर पर हूँ तो पोस्ट को संपादित करूँगा
अधिकतम

2
@ maxb मैंने अधिक आँकड़े जोड़ने के लिए नियंत्रक को विस्तारित किया है जो मेरी विकास प्रक्रिया के लिए प्रासंगिक थे: उच्चतम स्कोर तक पहुँच गया, औसत स्कोर तक पहुँच गया और औसत विजेता स्कोर gist.github.com/AwK/91446718a46f3e001c195398985756c
AKroell

2
यह एक बहुत ही मजेदार पासा खेल के समान लगता है जिसे Farkled en.wikipedia.org/wiki/Farkle
Caleb Jay

5
मैं इस प्रश्न को बंद करने के लिए मतदान कर रहा हूं क्योंकि यह पहले से ही नए उत्तरों के लिए बंद है ("टूर्नामेंट अब समाप्त हो गया है! अंतिम सिमुलेशन रात के दौरान चला गया था, कुल 3 games 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

आपका बॉट लीड में है! इसके अलावा, इसे तेजी से चलाने की कोई आवश्यकता नहीं है, यह निर्णय लेने में अन्य सभी बॉट के रूप में लगभग उतना ही जल्दी है।
अधिकतम 12

मैं इसे और तेज़ नहीं बनाना चाहता था। मैंने पहले ही वही किया था जो मैं करना चाहता था - केवल एक बार इनिशियलाइज़ करें - लेकिन इसे करने के लिए एक अच्छे तरीके की तलाश थी, खासकर क्लास के बाहर के कार्यों को परिभाषित किए बिना। मुझे लगता है कि अब यह बेहतर है।
क्रिश्चियन सिवर्स

यह अब बहुत अच्छा लग रहा है, अच्छा काम!
अधिकतम

पहला और दूसरा दोनों स्थान हासिल करने के लिए बधाई!
मैक्स

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 में आपका स्वागत है! यह वास्तव में प्रभावशाली उत्तर है। जब मैंने पहली बार इसे चलाया, तो मैं इस तथ्य से परेशान था कि इसने अन्य सभी बॉट्स के समान रनटाइम का उपयोग किया। फिर मैंने जीत प्रतिशत पर ध्यान दिया। वास्तव में नियमों का पालन करने का चतुर तरीका। मैं आपके बॉट को टूर्नामेंट में भाग लेने की अनुमति दूंगा, लेकिन मुझे उम्मीद है कि अन्य लोग भी इस तरह की रणनीति का उपयोग करने से बचेंगे, क्योंकि यह खेल की भावना का उल्लंघन करता है।
अधिकतम

2
चूंकि इस बॉट और दूसरी जगह के बीच इतना बड़ा अंतर है, इस तथ्य के साथ कि आपके बॉट को बहुत कंप्यूटिंग की आवश्यकता होती है, तो क्या आप स्वीकार करेंगे कि मैं आपकी जीत की दर का पता लगाने के लिए कम पुनरावृत्तियों के साथ एक सिमुलेशन चलाता हूं, और फिर अधिकारी को चलाता हूं अपने बॉट के बिना सिमुलेशन?
अधिकतम

3
मेरे द्वारा ठीक, मुझे लगा कि यह अयोग्य होने की संभावना है और निश्चित रूप से खेल की भावना में काफी नहीं है। कहा जा रहा है, यह काम पाने के लिए एक विस्फोट था और अजगर स्रोत कोड में चारों ओर प्रहार करने के लिए एक मजेदार बहाना था।
ज्यादातर हानिरहित

2
धन्यवाद! मुझे नहीं लगता कि कोई अन्य बॉट आपके स्कोर के करीब पहुंच पाएगा। और इस रणनीति को लागू करने के बारे में किसी और सोच के लिए, नहीं। अब से यह रणनीति नियमों के विरुद्ध है, और NeoBot टूर्नामेंट को निष्पक्ष रखने के लिए इसका उपयोग करने की अनुमति देता है।
अधिकतम

1
खैर, myBot हर एक को हराता है, लेकिन यह वैसे भी बहुत बेहतर है - हालांकि अगर मैं इस तरह से बॉट पोस्ट करूंगा, तो मुझे -100 मिलेगा और सर्वश्रेष्ठ स्कोर नहीं।
जन इवान

15

सहकारी झुंड

रणनीति

मुझे नहीं लगता कि किसी और ने अभी तक इस नियम के महत्व पर ध्यान दिया है:

यदि खेल 200 राउंड में जाता है, तो बॉट (या बॉट) उच्चतम स्कोर के साथ विजेता होता है, भले ही उनके पास 40 अंक या अधिक न हों।

यदि प्रत्येक बॉट हमेशा बस्ट होने तक लुढ़कता रहता है, तो हर किसी के पास राउंड 200 के अंत में शून्य का स्कोर होगा और हर कोई जीत जाएगा! इस प्रकार, सहकारी झुंड की रणनीति तब तक सहयोग करना है जब तक सभी खिलाड़ियों के पास शून्य का स्कोर है, लेकिन यदि कोई अंक प्राप्त करता है तो सामान्य रूप से खेलने के लिए।

इस पोस्ट में, मैं दो बॉट प्रस्तुत कर रहा हूं: पहला है कॉपरेटिवस्वर्मबॉट, और दूसरा है कोऑपरेटिवट्रोविटविस। कोऑपरेटिवस्वर्मबॉट सभी बॉट्स के लिए एक बेस क्लास के रूप में कार्य करता है जो औपचारिक रूप से सहकारी झुंड का हिस्सा हैं, और इसमें सहयोग के विफल होने पर बस अपने पहले सफल रोल को स्वीकार करने का प्लेसहोल्डर व्यवहार है। कॉपरेटिवस्वर्मबॉट के पास अपने माता-पिता के रूप में कोआपरेटिवस्वर्मबॉट है और यह हर तरह से समान है, सिवाय इसके कि इसके असहयोगी व्यवहार को एक के बजाय दो रोल करना है। अगले कुछ दिनों में मैं इस पोस्ट को संशोधित करके नए बॉट्स जोड़ूंगा जो गैर-सहकारी बॉट्स के खिलाफ खेलने वाले अधिक बुद्धिमान व्यवहार का उपयोग करते हैं।

कोड

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 गैर-सहकारी बॉट के पूल से आठ सहकारी बॉट चुनने की संभावनाएं हैं:

100130*99129*98128*97127*96126*95125*94124*931230.115

icn

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

i=8n=38

मामले का अध्ययन

कई कारणों से (फुटनोट 1 और 2 देखें), एक उचित सहकारी झुंड आधिकारिक खेलों में कभी प्रतिस्पर्धा नहीं करेगा। जैसे, मैं इस खंड में अपने स्वयं के सिमुलेशन में से एक के परिणामों का सारांश दूंगा।

इस सिमुलेशन ने 38 अन्य बॉट्स का उपयोग करके 10000 गेम चलाए जो पिछली बार मैंने यहां पर पोस्ट किए थे और 2900 बॉट्स थे जो कि कोपरेटिवस्वर्मबॉट को उनके मूल वर्ग के रूप में थे। नियंत्रक ने बताया कि 10000 खेलों में से 9051 (90.51%) 200 राउंड पर समाप्त हुए, जो इस भविष्यवाणी के काफी करीब है कि 90% खेल सहकारी होंगे। इन बॉट्स का कार्यान्वयन तुच्छ था; कोऑपरेटिवस्वर्मबोट के अलावा सभी ने यह फॉर्म लिया:

class CooperativeSwarm_1234(CooperativeSwarmBot):
    pass

कम से कम 3% बॉट्स में एक जीत प्रतिशत था जो 80% से नीचे था , और सिर्फ 11% से अधिक बॉट्स ने हर एक गेम जीता। झुंड में 2900 बॉट्स का औसत जीत प्रतिशत लगभग 86% है, जो अपमानजनक रूप से अच्छा है। तुलना के लिए, वर्तमान आधिकारिक लीडरबोर्ड पर शीर्ष कलाकार अपने खेल का 22% से कम जीतते हैं। मैं एक उत्तर के लिए अधिकतम अनुमत लंबाई के भीतर सहकारी झुंड की पूरी सूची को फिट नहीं कर सकता, इसलिए यदि आप देखना चाहते हैं कि आपको इसके बजाय यहां जाना होगा: https://pastebin.com/3Zc8m1Ex

चूंकि प्रत्येक बॉट लगभग 27 खेलों के औसत में खेला जाता है, इसलिए जब आप व्यक्तिगत बॉट के परिणामों को देखते हैं तो भाग्य अपेक्षाकृत बड़ा रोल निभाता है। जैसा कि मैंने अभी तक गैर-सहकारी खेलों के लिए एक उन्नत रणनीति को लागू नहीं किया है, ज्यादातर अन्य बॉट ने सहकारी झुंड के खिलाफ खेलने से काफी लाभान्वित किया है, यहां तक ​​कि सहकारी झुंड की औसत जीत दर 86% है।

बॉट के पूर्ण परिणाम जो झुंड में नहीं हैं, नीचे सूचीबद्ध हैं; दो बॉट हैं जिनके परिणाम मुझे लगता है कि विशेष रूप से ध्यान देने योग्य हैं। सबसे पहले, 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% जीत दर का इष्टतम समाधान प्राप्त कर सकते हैं। जैसे, अगर जीत की दर एकमात्र ऐसी चीज थी जो मायने रखती थी तो सहयोग एक स्थिर संतुलन होगा और चिंता की कोई बात नहीं होगी। हालाँकि, कुछ बॉट अन्य लक्ष्यों को प्राथमिकता दे सकते हैं, जैसे लीडरबोर्ड के शीर्ष पर पहुंचना। इसका मतलब है कि एक जोखिम यह है कि एक और बॉट आपके अंतिम मोड़ के बाद दोष कर सकता है, जो आपको पहले दोष के लिए एक प्रोत्साहन बनाता है। क्योंकि इस प्रतियोगिता का सेटअप हमें यह देखने की अनुमति नहीं देता है कि हमारे विरोधियों ने अपने पूर्व खेलों में क्या किया था, हम उन व्यक्तियों को दंडित नहीं कर सकते हैं जो दोषपूर्ण थे। इस प्रकार, सहयोग अंततः विफलता के लिए एक अस्थिर संतुलन है।

फुटनोट

[१]: प्राथमिक कारण कि मैं सिर्फ दो के बजाय हजारों बॉट जमा नहीं करना चाहता, ऐसा करने से १००० [२] के आदेश पर एक कारक द्वारा सिमुलेशन धीमा हो जाएगा, और ऐसा करने से काफी गड़बड़ होगी अन्य बॉट के रूप में प्रतिशत जीतना लगभग विशेष रूप से एक दूसरे के बजाय झुंड के खिलाफ खेलना होगा। हालाँकि, अधिक महत्वपूर्ण यह है कि यदि मैं चाहता हूं कि नियम की भावना को तोड़े बगैर मैं उचित समय सीमा में कई बॉट नहीं बना पाऊंगा, तो "बॉट को एक ही रणनीति को लागू नहीं करना चाहिए" मौजूदा एक, जानबूझकर या गलती से "।

[२]: मुझे लगता है कि दो मुख्य कारण हैं कि सहकारी झुंड चलाते समय अनुकरण धीमा हो जाता है। सबसे पहले, अधिक बॉट का मतलब है अधिक खेल यदि आप चाहते हैं कि प्रत्येक बॉट एक ही संख्या में गेम खेले (केस स्टडी में, गेम की संख्या लगभग 77 के कारक से भिन्न होगी)। दूसरा, सहकारी खेलों में अधिक समय लगता है क्योंकि वे पूरे 200 राउंड तक चलते हैं, और एक राउंड के भीतर खिलाड़ियों को अनिश्चित काल के लिए रोल करना पड़ता है। मेरे सेटअप के लिए, गेम्स को अनुकरण करने में लगभग 40 गुना अधिक समय लगा: 10000 खेलों को चलाने के लिए केस स्टडी को तीन मिनट से अधिक समय लगा, लेकिन सहकारी झुंड को हटाने के बाद यह केवल 4.5 सेकंड में 10000 गेम खत्म कर देगा। इन दो कारणों के बीच, मुझे लगता है कि बॉट्स के प्रदर्शन को सही ढंग से मापने के लिए लगभग 3100 गुना अधिक समय लगेगा जब एक झुंड प्रतिस्पर्धा में होता है जब वह नहीं होता है।


4
वाह। और पीपीसीजी में आपका स्वागत है। यह काफी पहला जवाब है। मैं वास्तव में इस तरह की स्थिति पर योजना नहीं बना रहा था। आपको निश्चित रूप से नियमों में एक खामी मिली। मुझे वास्तव में यकीन नहीं है कि मुझे यह कैसे स्कोर करना चाहिए, क्योंकि आपका जवाब एकल बॉट के बजाय बॉट का संग्रह है। हालाँकि, अभी मैं केवल यही कहूंगा कि यह अनुचित लगता है कि एक प्रतिभागी सभी बॉट्स के 98.7% को नियंत्रित करेगा।
मैक्सिमम

2
मैं वास्तव में नहीं चाहता कि डुप्लिकेट बॉट्स आधिकारिक प्रतियोगिता में हों; यही कारण है कि मैंने हजारों बहुत ही समान बॉट्स प्रस्तुत करने के बजाय खुद सिमुलेशन चलाया। मैं और अधिक स्पष्ट करने के लिए अपने सबमिशन को संशोधित करूंगा।
ईन्हेन्डर

1
अगर मैंने इस तरह के उत्तर का अनुमान लगाया होता, तो मैं 200 राउंड में जाने वाले खेलों को बदल देता ताकि वे खिलाड़ियों को स्कोर न दें। हालाँकि, जैसा कि आप ध्यान दें, समान बॉट्स बनाने के बारे में एक नियम है जो इस रणनीति को नियमों के विरुद्ध बना देगा। मैं नियमों को बदलने नहीं जा रहा हूं, क्योंकि यह उन सभी के साथ अन्याय होगा जिन्होंने बॉट बनाया है। हालाँकि, सहयोग की अवधारणा बहुत दिलचस्प है, और मुझे उम्मीद है कि प्रस्तुत अन्य बॉट्स हैं जो अपनी अनूठी रणनीति के साथ सहयोग रणनीति को लागू करते हैं।
अधिकतम

1
मुझे लगता है कि इसे और अच्छी तरह से पढ़ने के बाद आपकी पोस्ट स्पष्ट है।
अधिकतम

उनके लीडरबोर्ड प्लेसमेंट में शुद्ध लाभ देखने के लिए कितने मौजूदा बॉट को इस सहयोग ढांचे में अपने कोड को लपेटने की आवश्यकता होगी? मेरा भोला अनुमान 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-बॉट को अधिक बार बनाता है।
अधिकतम

@ मैक्सब ऐसा नहीं है, 20 अभी भी मेरी परीक्षा में "एंड गेम" के बिना सबसे अच्छा है। हो सकता है कि आपने नियंत्रक के पुराने संस्करण पर इसका परीक्षण किया हो।
tsh

मैंने इस चुनौती को डिजाइन करने से पहले एक अलग परीक्षण चलाया, जहां मैंने अपनी पोस्ट में दो रणनीति ("थ्रो एक्स टाइम्स" और "थ्रो एक्स स्कोर तक") के लिए प्रति राउंड औसत स्कोर की गणना की, और मुझे जो अधिकतम मिला वह 15-16 के लिए था। । हालांकि मेरा नमूना आकार बहुत छोटा हो सकता था, मैंने अस्थिरता पर ध्यान दिया।
अधिकतम

2
मैंने इसके साथ कुछ परीक्षण किया है, और मेरा निष्कर्ष केवल यह है कि 20 अच्छी तरह से काम करता है क्योंकि यह 40/2 है। हालांकि मुझे पूरा यकीन नहीं है। जब मैंने end_score4000 में सेट किया (और targetगणना में इसका उपयोग करने के लिए आपके बॉट को बदल दिया ), तो 15-16 बॉट काफी बेहतर थे। लेकिन अगर खेल केवल आपके स्कोर को बढ़ाने के बारे में था तो यह तुच्छ होगा।
मैक्स

1
@ मैक्सब अगर end_score4000 है, तो 200 मोड़ से पहले 4000 प्राप्त करना लगभग असंभव है। और खेल केवल 200 अंकों में सर्वोच्च अंक प्राप्त करने वाला है। और 15 पर रोक काम करना चाहिए क्योंकि इस समय एक बारी में उच्चतम स्कोर के लिए रणनीति 200 बारी में उच्चतम स्कोर के समान है।
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

महान प्रथम प्रस्तुत! मैं इसे अपने बॉट्स के खिलाफ चलाऊंगा जिसे मैंने परीक्षण के लिए लिखा था, लेकिन जब अधिक बॉट्स पोस्ट किए गए हैं तो मैं हाईस्कोर अपडेट करूंगा।
अधिकतम

मैंने आपके बॉट में मामूली संशोधन के साथ कुछ परीक्षण चलाए। lim = max(min(self.end_score - scores[self.index], 24), 6)अधिकतम 24 को बढ़ाकर और न्यूनतम 6 जोड़कर दोनों अपने आप ही जीत प्रतिशत बढ़ाते हैं और इससे भी अधिक संयुक्त।
AKroell

@ शकर: शांत! मैंने यह सुनिश्चित करने के लिए कुछ समान करने का इरादा किया है कि यह अंत में कुछ बार रोल करता है, लेकिन मैंने अभी तक इसे करने का समय नहीं लिया है। हालांकि, जब मैं 100k रन करता हूं, तो यह उन मूल्यों के साथ खराब प्रदर्शन करता है। मैंने केवल 18 बॉट्स के साथ परीक्षण किया है। शायद मुझे सभी बॉट्स के साथ कुछ टेस्ट करने चाहिए।
इमिग्ना

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 फिक्स्ड, धन्यवाद।
मेमनोनिक

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

विचार यह है कि अन्य बॉट्स अंक खो सकते हैं, इसलिए 2 का होना बुरा नहीं है - लेकिन यदि आप बहुत पीछे हैं, तो आप भी टूट सकते हैं।


1
PPCG में आपका स्वागत है! मैं आपके सबमिशन के माध्यम से देख रहा हूं, और ऐसा लगता है कि खेल में जितने अधिक खिलाड़ी होंगे, आपके बॉट के लिए जीत प्रतिशत उतना ही कम होगा। मैं नहीं बता सकता कि सीधे क्यों। बॉट्स का मिलान 1vs1 के साथ होने पर आपको 10% जीत मिलती है। विचार आशाजनक लगता है, और कोड सही दिखता है, इसलिए मैं वास्तव में नहीं बता सकता कि आपकी जीत अधिक क्यों नहीं है।
अधिकतम

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 फेंके जाने के बाद भी आगे है, यह तब तक जारी रहता है जब तक कि यह एक हिट न हो जाए। scoresकेवल कुल स्कोर, नहीं मौजूदा दौर के लिए मरने के मामलों में होते हैं। आपको इसे संशोधित करना चाहिए current_score = scores[self.index] + sum(self.current_throws)
अधिकतम

धन्यवाद - वह परिवर्तन कर देगा!
स्टुअर्ट मूर

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 bot) इस तरह का एक बॉट था, लेकिन डेविड ने उनका जवाब हटा दिया। मैं उस बॉट को आपकी जगह ले लूंगा।
अधिकतम 5

1
यह काफी हास्यास्पद है, इस बॉट्स के एक्सटेंडेड स्टैटिस्टिक्स को देखते हुए: पॉइंटअरेफोरनरैड्स और स्टॉपबॉट को छोड़कर, इस बॉट में सबसे कम औसत अंक हैं, और फिर भी इसमें एक अच्छा जीत अनुपात है
बेलहिनिक्स

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

सुनिश्चित करें कि GoTo20Bot से विचार उधार लें। यह उस अवधारणा को जोड़ता है जो इसे हमेशा मानता है (जब अंतिम में या 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 में से तीसरे स्थान पर हैं।
20

4

StopBot

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

सचमुच केवल एक फेंक।

यह बेस Botक्लास के बराबर है ।


1
क्षमा करें! आप सभी नियमों का पालन कर रहे हैं, हालांकि मुझे डर है कि आपका बॉट औसतन प्रति राउंड 2.5 अंकों के साथ बहुत प्रभावी नहीं है।
अधिकतम

1
मुझे पता है, किसी को हालांकि उस बॉट को पोस्ट करना था। नुकसान के लिए डीजेनरेट बॉट।
जकार्इ

5
मैं कहता हूं कि मैं आपके बॉट से प्रभावित होकर आखिरी सिमुलेशन में एक जीत हासिल कर रहा हूं, जिससे साबित होता है कि यह पूरी तरह से बेकार नहीं है।
अधिकतम

2
यह एक खेल है ?! यह आश्चर्य की बात है।
जकार्इ

3

ब्रैंडमायोवन_डाइस (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 बार मरते हैं, तो दो पासा कास्टिंग करते समय औसत स्कोर के करीब।
अधिकतम

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अनावश्यक है - यह आपके बॉट को कॉल करने से पहले एक बार फेंकता है। आपका बॉट हमेशा न्यूनतम दो बार फेंक देगा।
स्पिटमास्टर

धन्यवाद। मुझे विधि के नाम से गुमराह किया गया था।
पीटर टेलर

@PeterTaylor आपके प्रस्तुत करने के लिए धन्यवाद! मैंने इस make_throwपद्धति को जल्दी नाम दिया , जब मैं चाहता था कि खिलाड़ी अपनी बारी को छोड़ सकें। मुझे लगता है कि एक अधिक उपयुक्त नाम होगा keep_throwing। सैंडबॉक्स में प्रतिक्रिया के लिए धन्यवाद, इसने वास्तव में इसे एक उचित चुनौती बनाने में मदद की!
अधिकतम

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सामान के साथ घेरने की आवश्यकता है ।
क्रिश्चियन सेवर्स

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एक स्वच्छ विचार है, लेकिन मैं इसे और अधिक उन्नत बॉट से कुछ की तुलना में अंत खेल में ग्रस्त लगता है। अभी भी एक महान प्रस्तुत!
अधिकतम

2
इस तरह OneInFiveBotसे काफी दिलचस्प है कि वह लगातार उच्चतम स्कोर तक पहुंच गया है।
AKroell

1
StopBotपंचिंग बैग देने के लिए धन्यवाद : पी। OneInFiveBot वास्तव में बहुत साफ, अच्छा काम है!
२०:२५ बजे जकार्इ

@ मैक्सब यप, यहीं से मुझे नाम मिला। मैंने ईमानदारी से परीक्षण नहीं किया है OneInFiveBotऔर यह मेरी अपेक्षा से बहुत बेहतर कर रहा है
The_bus


3

LizduadacBot

1 चरण में जीतने की कोशिश करता है। अंतिम स्थिति कुछ हद तक असाध्य है।

यह मेरी पहली पोस्ट भी है (मैं पायथन के लिए नया हूँ), इसलिए अगर मैं "पॉइंटएफ़ोर नॉर्ब्सबॉट" को हराता हूं, तो मुझे खुशी होगी!

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 में आपका स्वागत है (और पायथन में आपका स्वागत है)! आपके पास हारने में मुश्किल समय होगा PointsAreForNerdsBot, लेकिन आपका बॉट वास्तव में काफी अच्छा है। मैं स्कोर को आज रात या कल अपडेट करूंगा, लेकिन आपकी जीत लगभग 15% है, जो औसत 12.5% ​​से अधिक है।
23

"कठिन समय" से, उनका मतलब है कि यह असंभव है (जब तक कि मुझे बहुत गलत न समझा जाए)
जकार्इ

@ मैक्सब मैंने वास्तव में नहीं सोचा था कि जीत की दर इतनी अधिक होगी! (मैं स्थानीय स्तर पर इसका परीक्षण नहीं किया था)। मुझे आश्चर्य है कि अगर 50 को थोड़ा अधिक / कम करने से जीत दर में वृद्धि होगी।
लिज़ुअदैक

3

SlowStart

यह बॉट टीसीपी स्लो स्टार्ट एल्गोरिथ्म को लागू करता है। यह अपनी पिछली बारी के अनुसार रोल की संख्या ( न ही ) को समायोजित करता है : यदि यह पिछले मोड़ में 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()) होना चाहिए ।
अधिकतम

2
इसके अलावा, मुझे लगता है कि आपको iलूप में अपने वैरिएबल को अपडेट करने की आवश्यकता है । अभी आपका बॉट या तो लूप को पूरी तरह से पास करता है या जब तक हिट नहीं होता तब तक लूप में फंस जाता है। 6.
अधिकतम

वर्तमान हाईस्कोर में, मैंने इन परिवर्तनों को लागू करने की स्वतंत्रता ली है। मुझे लगता है कि आप शुरुआती मूल्य के साथ प्रयोग कर सकते हैं self.norऔर देखें कि यह आपके बॉट के प्रदर्शन को कैसे प्रभावित करता है।
अधिकतम डेस

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                                               

उपस्थिति आमतौर पर जीतती है - लेकिन भाग्य को हमेशा टाला नहीं जा सकता है।
महान और रहस्यमयी हैं शाइ-हुलूद के तरीके!


इस चुनौती के शुरुआती दिनों में (यानी 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लीड में जाने के बाद मैंने यह सोचना शुरू कर दिया कि भविष्य की भविष्यवाणी करने के लिए इसकी सही क्षमता को कैसे हराया जाए। तो यहाँ एक दून बोली; जब पॉल एटराइड्स, क्विसटैड हैडरैच, एक नेक्सस पर खड़ा होता है, जिसमें से विभिन्न वायदाओं की अनंतता को अनियंत्रित किया जा सकता है:

उन्होंने कहा कि यह अनुमान, एक रोशनी थी जो एक बार सटीकता और सार्थक त्रुटि के स्रोत पर प्रकट की गई सीमाओं को शामिल करती है। एक प्रकार की हाइजेनबर्ग अनिश्चितता ने हस्तक्षेप किया: ऊर्जा का व्यय जो उसने देखा था, जो उसने देखा था उसे बदल दिया ... ... सबसे अधिक मिनट की कार्रवाई- एक पलक, एक लापरवाह शब्द, रेत का एक गलत अनाज - एक विशाल लीवर को स्थानांतरित कर दिया। ज्ञात ब्रह्मांड। उन्होंने इस परिणाम के साथ हिंसा को इतने अधिक चर के साथ देखा कि उनके थोड़े से आंदोलन ने पैटर्न में विशाल बदलाव पैदा किए।

दृष्टि ने उसे गतिहीनता से मुक्त करना चाहा, लेकिन यह भी, इसके परिणामों के साथ कार्रवाई थी।

तो यहाँ जवाब था: भविष्य को बदलने के लिए इसे बदलना है; और यदि आप बहुत सावधान हैं, तो चयनात्मक कार्रवाई या निष्क्रियता से, आप इसे लाभप्रद तरीके से बदल सकते हैं - कम से कम अधिकांश समय। यहां तक ​​कि KwisatzHaderach100% जीत की दर भी नहीं मिल सकती है!


ऐसा लगता है कि यह बॉट यादृच्छिक संख्या जनरेटर की स्थिति को बदलता है, यह सुनिश्चित करने के लिए कि यह रोलिंग 6 से बचा जाता है, या कम से कम इसकी आशंका है। उसी के लिए जाता है HarkonnenBot। हालाँकि, मैं ध्यान देता हूं कि इन बॉट्स की जीत दर NeoBot की तुलना में बहुत अधिक है। क्या आप सक्रिय रूप से यादृच्छिक संख्या जनरेटर को जोड़-तोड़ कर रहे हैं ताकि इसे रोलिंग 6 से रोका जा सके?
13

ओह, अपने पहले पढ़ने पर मैंने ध्यान नहीं दिया कि यह न केवल बेहतर है NeoBotबल्कि बेहतर भी है! मुझे यह भी पसंद है कि आप कैसे यादृच्छिकता (विशेष रूप से नियंत्रक) का उपयोग करते हुए सब कुछ का एक उदाहरण देते हैं: अपने स्वयं के random.Randomउदाहरण का उपयोग करें । जैसे NeoBot, यह नियंत्रक के अनिर्दिष्ट कार्यान्वयन विवरण के परिवर्तनों के लिए थोड़ा संवेदनशील लगता है।
क्रिश्चियन सिवर्स

@ एमएक्सबी: आरएनजी को HarkonnenBotस्पर्श नहीं करता है; यह यादृच्छिक संख्या के बारे में बिल्कुल परवाह नहीं करता है। यह सिर्फ अन्य सभी बॉट्स को जहर देता है, फिर धीरे-धीरे खत्म हो जाता है। कई पाक व्यंजनों की तरह, लंबी और नाजुक तैयारी के बाद, बदला सबसे अच्छा धीरे-धीरे चखा जाने वाला व्यंजन है।
दानी ओ

@ChristianSievers: विपरीत NeoBot (और HarkonnenBot) के , KwisatzHaderachकार्यान्वयन के केवल एक विवरण पर निर्भर करता है; विशेष रूप से यह जानने की जरूरत नहीं है कि कैसे random.random () लागू किया जाता है, केवल यह है कि नियंत्रक इसका उपयोग करता है; D
Dani O

1
मैंने आपके सभी बॉट्स को देखा है। मैंने इलाज करने का फैसला किया है KwisatzHaderachऔर HarkonnenBotउसी तरह से NeoBot। वे कम गेम वाले सिमुलेशन से अपने स्कोर प्राप्त करेंगे, और आधिकारिक सिमुलेशन में नहीं होंगे। हालाँकि, वे हाईस्कोर सूची को बहुत पसंद करेंगे NeoBot। उनके आधिकारिक अनुकरण में नहीं होने का मुख्य कारण यह है कि वे अन्य बॉट रणनीतियों को गड़बड़ कर देंगे। हालाँकि। WisdomOfCrowdsभागीदारी के लिए अच्छी तरह से अनुकूल होना चाहिए, और मैं आपके द्वारा किए गए नए परिवर्तनों के बारे में उत्सुक हूं!
अधिकतम

2
class ThrowThriceBot(Bot):

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

खैर, यह एक स्पष्ट है


मैंने उस वर्ग के बॉट्स के साथ कुछ प्रयोग किए हैं (यह पहली बार जब मैंने खेल खेला था तब मैंने जिस रणनीति का इस्तेमाल किया था)। मैं तब 4 थ्रो के साथ गया, हालांकि 5-6 का राउंड प्रति उच्च औसत स्कोर है।
अधिकतम

इसके अलावा, आपके पहले कोट के जवाब पर बधाई!
अधिकतम

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

लास्ट राउंड ऐसा काम करता है जैसे कि यह हमेशा अंतिम राउंड होता है और यह अंतिम बॉट होता है: यह तब तक लुढ़कता रहता है जब तक यह लीड में न हो। यह भी 15 से कम अंक के लिए व्यवस्थित नहीं करना चाहता है जब तक कि यह वास्तव में अंतिम दौर न हो या यह 40 अंक तक पहुंच जाए।


दिलचस्प दृष्टिकोण। मुझे लगता है कि अगर यह पीछे गिरने लगे तो आपका बॉट ग्रस्त है। चूंकि एक दौर में 30 अंक प्राप्त करने की संभावना कम है, इसलिए आपके बॉट के अपने वर्तमान स्कोर पर बने रहने की अधिक संभावना है।
अधिकतम

1
मुझे संदेह है कि यह उसी गलती से ग्रस्त है जिसे मैंने किया था (NotTooFarBehindBot टिप्पणी देखें) - जैसे कि अंतिम दौर में, यदि आप नहीं जीत रहे हैं तो आप तब तक फेंकते रहेंगे जब तक कि आपको 6 नहीं मिलते (स्कोर [self.index] कभी भी अपडेट नहीं होता): वास्तव में क्या आपके पास असमानता गलत तरीका है? अधिकतम (स्कोर) हमेशा> = स्कोर [आत्म.इंडेक्स] होगा
स्टुअर्ट मूर

@StuartMoore Haha, हाँ, मुझे लगता है कि आप सही हैं। धन्यवाद!
स्पिटमास्टर

मुझे संदेह है कि आप 2 पर "और last_round" चाहते हैं जबकि आप जो चाहते हैं - अन्यथा 2 का उपयोग किया जाएगा या नहीं, last_round सच है
स्टुअर्ट मूर

3
यह जानबूझकर किया गया है। यह हमेशा अपनी बारी को समाप्त करने पर अग्रणी होने की कोशिश करता है।
स्पिटमास्टर

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 ऐसा इसलिए था क्योंकि मेरे द्वारा उपयोग किए जा रहे टैग्स में हस्तक्षेप करने वाले प्रतीक <और >चिह्न थे<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 से जाओ" बॉट के बराबर है, लेकिन हमारे पास अभी तक इनमें से एक नहीं है
स्टुअर्ट मूर

1
@StuartMoore ... यह एक बहुत ही सही बात है, हाँ
Cain

जब मैंने इसे अपने विंडोज मशीन पर चलाया तो मैं कंट्रोलर के साथ आपके मुद्दों में भाग गया। किसी तरह यह मेरे लिनक्स मशीन पर ठीक चला। मैं नियंत्रक को अपडेट कर रहा हूं, और एक बार पोस्ट करने के बाद इसे अपडेट कर दूंगा।
अधिकतम

@maxb धन्यवाद, शायद कुछ इस बारे में है कि विभिन्न प्रक्रिया में कौन से चर उपलब्ध हैं। FYI करें इसे भी अपडेट किया, मैंने उपज के आसपास एक मूर्खतापूर्ण त्रुटि की: /
Cain

2

BlessRNG

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

BlessRNG फ्रेंकरज़ गेबिन BlessRNG


2

FortyTeen

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

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

अंतिम राउंड तक 14 अंकों के लिए प्रयास करें, फिर मान लें कि हर कोई 14 अंकों के लिए प्रयास करने जा रहा है और उस स्कोर को टाई करने की कोशिश करता है।


मैं TypeError: unsupported operand type(s) for -: 'list' and 'int'आपके बॉट के साथ मिल गया ।
tsh

मैं यह मान रहा हूं कि max_projected_scoreपूरी सूची के बजाय आपकी सूची अधिकतम होनी चाहिए, क्या मैं सही हूं? अन्यथा मुझे टीश के समान ही मुद्दा मिलता है।
अधिकतम

ओह, ठीक करने के लिए संपादित किया गया।
हिस्टोक्रेट

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)

खैर यह काफी सुरुचिपूर्ण है :) इसके अलावा, मुख्य प्रतियोगिता में पहले और दूसरे दोनों स्थान प्राप्त करने पर बधाई!
दानी ओ

स्वाभाविक रूप से मैंने ट्विक किया है HarkonnenBotताकि Rebelअब खुद को अनपिस नहीं किया जा सके); और मैंने भी ट्विक किया है TleilaxuBotताकि Rebelइसका पता न चले !
दानी ओ

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 से पहले 5 रोल करेंगे। जब हम करते हैं, तो कैश आउट करते हैं।


अगर हम 1 के बजाय रुकते हैं, तो यह धीमी प्रगति करता है, लेकिन एक ही बाउंड में 40 के होने की संभावना अधिक है।
Mnemonic

मेरे परीक्षणों में, टेकऑफ़ के 24.262 अंकों की तुलना में टेकऑन को प्रति राउंड 20.868 अंक मिले (और 0.291 से 0.259 तक जीत भी लाए)। इसलिए मुझे नहीं लगता कि यह इसके लायक है।
स्पिटमास्टर

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 संपादित करें: अंतिम दौर में सोने की रणनीति को जोड़ा]

[संपादित करें २: अद्यतन तर्क क्योंकि मैंने गलती से सोचा था कि एक बॉट केवल ४० बॉट स्कोरिंग के बजाय ४० पर स्कोर करेगा]

[संपादित 3: अंतिम गेम में चेज़र को थोड़ा अधिक रक्षात्मक बना दिया]


PPCG में आपका स्वागत है! नीट का विचार न केवल पकड़ने की कोशिश करता है, बल्कि पहली जगह भी पास करता है। मैं अभी एक सिमुलेशन चला रहा हूं, और मैं आपको शुभकामनाएं देता हूं!
अधिकतम

धन्यवाद! प्रारंभ में मैंने एक निश्चित राशि (पिछले 6 और 20 के बीच मूल्यों की कोशिश की) द्वारा पिछले नेता को पार करने की कोशिश की, लेकिन यह सिर्फ दो बार और सीढ़ियों को फेंकना बेहतर निकला।
एकरेल


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 में आपका स्वागत है! आपका बॉट निश्चित रूप से खेल की भावना से खेलता है! मैं आज शाम बाद में एक सिमुलेशन चलाऊंगा।
अधिकतम

धन्यवाद! मैं उत्सुक हूं कि यह कितना अच्छा होगा, लेकिन मुझे लगता है कि यह कम पक्ष में होगा।
विलियम पोर्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.