Піднебесна бюрократія KoTH


14

В Імперському Китаї чини в суспільстві визначалися не народженням, ні багатством, а здатністю людини до успіху в імператорських іспитах. Імператор нефриту, божественний правитель Небес, закликав всіх його підданих обстежитися, щоб визначити їхню цінність, а кому далі дати Божественний мандат для управління Китаєм.

Правила бюрократії:

  • Божественна бюрократія складається з негативних цілочисельних рангів, починаючи з 0. Кожен член (бот) бюрократії належить до одного рангу. Кожен ранг може містити довільно багато членів, але не може бути порожнім, якщо всі ранги вище не порожні
  • На початку гри всі учасники мають ранг 0
  • Щоразу, кожен член бюрократії повинен відповідати на іспит. Екзамен складається з правильного відгадування булевих значень списку. Довжина списку - це число рангу над членом.
  • Екзаменаційні питання готуються випадковим членом рангу вище. Члени вищого рангу отримують свої запитання безпосередньо у JadeEmperor(див. Нижче)
  • Учасник, який на іспиті набрав принаймні 50%, має право на Акцію. Член, який на іспиті набрав менше 50%, може брати участь у програмі Demotion.
  • Учасник, який має право на демо-версію, їхній ранг зменшився на одного лише у тому випадку, якщо є член, який має право на Акцію в нижчому ранзі, щоб зайняти своє місце.
  • Усім учасникам, які мають право на Акцію, їхній ранг збільшився на 1, доки цей ранг не залишиться порожнім.
  • Якщо не всі члени, які мають право на участь, можуть бути демонованими або просунутими, перевага надається тим, хто має найменший (для демонтажу) респ. найвищий (для Акції) бал. Краватки розриваються випадковим чином.
  • Звання члена може змінюватися лише максимум на 1 кожен крок.

Правила гри:

  • Кожному боту буде випадково присвоєно ідентифікатор на початку гри, який не зміниться протягом його перебігу. Ідентифікатор JadeEmperorмає -1, усі інші мають послідовні негативні ідентифікатори, починаючи з 0.
  • Усі боти змагаються одночасно
  • Гра триває 100 оборотів, оцінка бота - це її середній ранг, який мав за той час.
  • Загальна оцінка набирається за допомогою 1000 ігор та усереднення результатів.
  • Кожен бот - клас Python 3, що реалізує наступні чотири функції:
    • ask(self,n,ID), який складає іспит, повертаючи listбулеві довжини n. ID - це ідентифікатор бота, який повинен відгадати цей список. ask()можна викликати багато разів протягом одного раунду для будь-якого бота, але також зовсім не.
    • answer(self,n,ID), що є спробою відповісти на іспит, повернувши listбулеві довжини n. Ідентифікатор - ідентифікатор бота, ask()згенерований іспитом answer()називається рівно один раз на раунд для кожного бота.
    • update(self,rankList,ownExam,otherExams)викликається після того, як Контролер виконав усі Про-та Демо-версії. Його аргументами є: Список цілих чисел, перелік усіх рангів за ідентифікацією всіх ботів; кортеж, що складається з двох списків, спочатку іспитні питання, потім відповіді, які дав бот (на випадок, якщо він забув); потім список кортежів, аналогічно складаючись з пар на відповіді на іспит-відповіді, цього разу для всіх іспитів, які видав бот.
    • __init__(self, ID, n) передає боту власний ідентифікатор та кількість конкуруючих ботів.
  • Класи дозволяють реалізувати інші функції для приватного використання
  • Визначати подальші змінні та використовувати їх для зберігання даних про минулі іспити явно дозволяється.
  • Програмування метаефектів заборонено, тобто будь-які спроби прямо отримати доступ до коду інших ботів, коду Контролера, спричиняючи Винятки або подібне. Це змагання стратегій для іспитів, а не злому коду.
  • Ботів, які намагаються допомогти один одному, явно дозволено, якщо вони не роблять це за допомогою мета-ефектів, а виключно за допомогою інформації, що передається через update()
  • Інші мови дозволені лише в тому випадку, якщо їх можна легко перетворити на Python 3.
  • Бібліотека numpy буде імпортовано як np. Версія 1.6.5, тобто використовує стару випадкову бібліотеку. Якщо у вас nummy 1.7, старі функції доступні numpy.random.mtrandдля тестування. Не забудьте зняти mtrand для подання.
  • Якщо бот викликає виняток під час виконання, його дискваліфікують. Будь-який бот, чий код настільки затуманений, що неможливо сказати, чи генерує він список довжини n при виклику ask()або answer()викликається, також буде дискваліфіковано попередньо. Бот, який змушує мене надруковувати копії, отримує -1 балів.
  • Назви класів мають бути унікальними
  • Дозволено декілька ботів на людину, але лише ітераційно оновленими ботами буде взята лише остання версія.
  • Оскільки, схоже, існує деяка плутанина щодо подібності бота:
    • Вам заборонено публікувати копію іншого бота. Це єдина стандартна лазівка, яка реально застосовується в цьому виклику.
    • Вам дозволяється ділитися кодом з іншими ботами, включаючи ботів інших людей.
    • Вам не дозволяється подавати бота, який відрізняється від іншого лише тривіальним зміною стратегії (наприклад, зміною насіння для генерації запитань) якщо ви не зможете довести, що кількість таких копіювання є мінімально необхідним для успіху введення в дію своєї стратегії (зазвичай це два боти для співпраці).

Приклад ботів:

The JadeEmperor завжди є частиною гри, але не змагається; він служить генератором для іспитів ботів найвищого рангу. Його іспити є випадковими, але не рівномірно, щоб дозволити розумним ботам спосіб просунутися.

class JadeEmperor:
    def __init__(self):
        pass

    def ask(self,n,ID):
        num=min(np.random.exponential(scale=np.sqrt(np.power(2,n))),np.power(2,n)-1)
        bi=list(np.binary_repr(int(num),width=n))
        return [x=='0' for x in bi]

П'яниця виробляє іспити і відповіді абсолютно випадково. Він буде частиною гри.

class Drunkard:
    def __init__(self,ID,n):
        pass

    def ask(self,n,ID):
        return list(np.random.choice([True,False],size=n,replace=True))

    def answer(self,n,ID):
        return list(np.random.choice([True,False],size=n,replace=True))

    def update(self,rankList,ownExam,otherExams):
        pass #out

Плагіатор тільки копія попередніх іспитів. Він також буде частиною гри.

class Plagiarist:
    def __init__(self,ID,n):
        self.exam=[True]

    def ask(self,n,ID):
        return (self.exam*n)[0:n]

    def answer(self,n,ID):
        return (self.exam*n)[0:n]

    def update(self,rankList,ownExam,otherExams):
        self.exam=ownExam[0]

Доступний код контролера тут . Для тестування ви можете помістити власний клас у файл Contestants.py у тій же папці, і вони будуть імпортовані.

Чат-кімнати можна знайти чаткою .

Екзамени починаються!

Поточний рахунок, з більшою точністю (10000 пробіжок) за 20 жовтня:

ВступникАвторОцінкаАльфаSleafar9.669691ГаммаSleafar9.301362Бета-версіяSleafar9.164597WiQeLuФіолетовий Р7.870821StudiousBotDignissimus - спам7,538537СантаянаСара Дж7.095528Плагіат6.522047CountOracularIFcoltransG5.881175ТомасAlien @ System5.880041НавпакиDraco18s5.529652Марксцукрові5.433808П’яниця5.328178Інь ЯньФіолетовий Р5.102519ЕквалайзерМнемонічний4.820996TitForTatАнонімний3.35801

Конкурси будуть проводитись з кожним новим конкурсом у найближчому майбутньому.


1
Копії ботів - це стандартна лазівка, тому ні. Якщо ви спробуєте зловживати кількома ботами на авторське правило, надсилаючи майже-але не зовсім копії, я його видалю.
AlienAtSystem

1
@AlienAtSystem Чому ви дозволяєте ботам допомагати один одному? Просто схоже на те, що більше хаосу та випадковості мати справу.
Дон Тисяча

2
Чому аргументи конструктора, ID, nа не аргументи інших методів n, ID?
Фіолетовий P

1
@DonThousand, тому що я вважаю, що за наведених обмежень, це досить подвиг, щоб зробити двох ботів, які A) успішно рукостискають (зауважте, що Plagiarizer може випадково грати в людину посередині) і B) потім прийняти стратегію, яка надійно допомагає цьому боту але більше ніхто не підніметься.
AlienAtSystem

1
@someone ранги рахуються вгору. Ви починаєте з 0 і працюєте над більшими цифрами
AlienAtSystem

Відповіді:


4

Сантаяна

Ті, хто не може згадати минуле, засуджуються повторити його. Таким чином, ми приймаємо наші рішення, виходячи з того, як діяли інші в минулому, відповідаючи на те, яку відповідь запитувач зазвичай очікував від нас за заданим індексом, і просимо відповіді, яку вони нам найменше давали в заданому індексі .

import numpy as np

class Santayana:
    """
    Those who cannot remember the past are condemned to repeat it
    """
    def __init__(self, ID, num_competitors):
        self.ID = ID
        self.exams_taken = {}
        self.exams_issued = {}
        self.last_exam_asker = None
        self.recent_exam_takers = []

        for i in range(num_competitors):
            self.exams_taken[i] = []
            self.exams_issued[i] = []

    def ask(self, length, taker_ID):
        # Remember who asked
        self.recent_exam_takers.append(taker_ID)
        new_exam = []

        # At every index, expect the answer they've given the least often (default to False if equal)
        for i in range(length):
            trues = 0
            falses = 0
            for exam in self.exams_issued[taker_ID]:
                if len(exam) <= i: continue
                if exam[i]:
                    trues += 1
                else:
                    falses += 1
            new_exam.append(trues < falses)
        return new_exam

    def answer(self, num_answers, asker_ID):
        self.last_exam_asker = asker_ID
        if asker_ID == -1:
            # Copy emperor's process to hopefully get a similar exam
            num = min(np.random.exponential(scale=np.sqrt(np.power(2,num_answers))),np.power(2,num_answers)-1)
            as_bin = list(np.binary_repr(int(num),width=num_answers))
            return [x=='0' for x in as_bin]
        else:
            new_answer = []

            # At every index, give the answer that's been correct the greatest number of times (default to True if equal)
            for i in range(num_answers):
                trues = 0;
                falses = 0;
                for exam in self.exams_taken[asker_ID]:
                    if len(exam) <= i: continue
                    if exam[i]:
                        trues += 1
                    else:
                        falses += 1
                new_answer.append(trues >= falses)
            return new_answer

        return [True for i in range(num_answers)]

    def update(self, rank_list, own_exam, other_exams):
        if self.last_exam_asker > -1:
            # Save the exam we took, unless it was from the Emperor - we already know how he operates
            self.exams_taken[self.last_exam_asker].append(own_exam[0])
        for i in range(len(self.recent_exam_takers)):
            # Save the responses we got
            self.exams_issued[i].append(other_exams[i][1])

        self.recent_exam_takers = []

3

Досліджений бот

Цей бот вивчає тести! Він намагається знайти зразки в тестах, які видаються різними ботами, і діє гармонійно.

На моєму кінці, поки що, цей бот перевершує всі інші боти, які я міг би працювати на своєму комп’ютері, крім альфа, бета-версії та гамми (які запрограмовані працювати разом). Бот не використовує той факт, що командування дозволено, тому що я відчував, що це трохи схоже на обман і трохи брудний. Однак, дивлячись на це, схоже, що командування здається досить ефективним.

Бот намагається розпізнати, коли відповіді на тести випадкові і у відповідях збігаються із середніми 50% на тести.

Бот також намагається розпізнати, коли бот просто перевернув свої відповіді, щоб скинути інших ботів, які зобов'язуються передбачити свою поведінку, однак я ще не запрограмував його конкретно діяти з цього приводу.

Я коментував код з кількома коментарями, щоб полегшити читання

import random
import numpy as np


class StudiousBot:
    GRAM_SIZE = 5
    def __init__(self, identifier, n):
        self.id = identifier
        self.ranks = {i: 0 for i in range(n)} # Stores ranks
        self.study_material = {i: [] for i in range(n)} # Stores previous exam data
        self.distribution = {i: [] for i in range(n)} # Stores the percentage of answers that were `True` on a Bot's tests over time
        self.last_examiner = None

    def ask(self, n, identifier):
        # This bot gives random tests, it doesn't bother making them difficult based on answers to them
        # The reason for this is that I can't personalise the tests for each bot
        return [random.choice([True, False]) for i in range(n)] 

    def answer(self, n, examiner_id):
        self.last_examiner = examiner_id
        if examiner_id == -1:
            return StudiousBot.answer_emperor(n) # Easy win, I know the distribution of answers for the Emperor's tests

        bother_predicting = True # Whether or not the Bot will attempt to predict the answers to the exam
        study_material = self.study_material[examiner_id]
        distribution = self.distribution[examiner_id]
        if len(distribution) > 0: # If there is actually data to analyse
            sd = StudiousBot.calculate_standard_deviation(distribution)
            normalised_sd = StudiousBot.calculate_normalised_standard_deviation(distribution)

            if abs(30 - sd) < 4: # 30 is the expected s.d for a random distribution
                bother_predicting = False # So I won't bother predicting the test 

            if abs(sd - normalised_sd * 2) > 4: # The bot is merely inverting answers to evade being predicted
                pass # However, at this time, I'm not certain how I should deal with this. I'll continue to attempt to predict the test 


        if bother_predicting and len(study_material) >= StudiousBot.GRAM_SIZE:
            return StudiousBot.predict(study_material, n)

        return [random.choice([True, False]) for i in range(n)]

    def predict(study_material, n): # Predicts the answers to tests with `n` questions
        grams = StudiousBot.generate_ngrams(study_material, StudiousBot.GRAM_SIZE) # Generate all n-grams for the study material
        last_few = study_material[-(StudiousBot.GRAM_SIZE - 1):] # Get the last 9 test answers
        prediction = None
        probability = -1
        for answer in [True, False]: # Finds the probabiility of the next answer being True or False, picks the one with the highest probability
            new_prediction = last_few + [answer]
            new_probability = grams.count(new_prediction)         

            if new_probability > probability:
                prediction = answer
                probability = new_probability

        if n == 1:
            return [prediction]

        return [prediction] + StudiousBot.predict(study_material + [prediction], n-1)          


    @staticmethod
    def calculate_standard_deviation(distribution):
        return np.std(distribution)

    def calculate_normalised_standard_deviation(distribution): # If the answers happen to be inverted at some point, this function will return the same value for answers that occured both before and after this point  
        distribution = list(map(lambda x: 50 + abs(50-x), distribution))
        return StudiousBot.calculate_standard_deviation(distribution)   

    @staticmethod
    def generate_ngrams(study_material, n):
        assert len(study_material) >= n
        ngrams = []
        for i in range(len(study_material) - n + 1):
            ngrams.append(study_material[i:i+n])

        return ngrams

    def update(self, ranks, own_exam, other_exams):
        self.ranks = dict(enumerate(ranks))
        if self.last_examiner != -1:
            self.study_material[self.last_examiner] += own_exam[0]
            self.distribution[self.last_examiner].append(own_exam[0].count(True) / len(own_exam[0]) * 100) # Stores the percentage of the answers which were True

    @staticmethod
    def answer_emperor(n): # Algorithm to reproduce Emperor's distribution of test answers  
        exp = np.random.exponential(scale=np.sqrt(np.power(2,n)))
        power = np.power(2,n) - 1        
        num = min(exp, power)
        bi = list(np.binary_repr(int(num), width=n))
        return [x == '0' for x in bi]

Судячи з нашої ефективності, у вас найкращий алгоритм відповіді, а Wi Qe Lu має найкращий алгоритм запитання. Я пропоную об'єднати наших ботів в єдиного бота, який називається Xuézhě (китайський для "вченого"), який збігається так, як "перемикач".
Фіолетовий П

Я зламав це і склав іспити на своїй машині. Цікаво, що це перевершило студію Бота, але не Wi Qe Lu.
Фіолетовий P

@PurpleP Ха-ха! Це звучить дуже цікаво, я не думаю, що у мене є достатньо часу для вдосконалення мого бота, але ви можете опублікувати його як подання тут
Dignissimus -

3

Граф Оракуляр

Цей бот використовує алгоритм, який в середньому оцінює іспити всіх інших робочих ботів (з огляду на кругле число та деякі жахливі евристики) для вирішення того, що кожен інший бот встановить у якості іспиту.
Граф запитує іспити, використовуючи хеш md5. Отже, і його питання, і відповіді є детермінованими. Він ігнорує більшість внесків, запитуючи та відповідаючи на ті самі послідовності булевих, дощу чи блиску, в тому числі проти Джейда Емпорера.

import numpy as np
import hashlib

class CountOracular:
    '''Uses very little external data to make heuristical statistical
    deterministic predictions about the average exam.
    (Assonance not intended.)
    To generate its own exams, uses a deterministic hash.'''
    def __init__(self, id, number_of_bots):
        self.last_round = []
        #functions for calculating what other bots will likely do.
        self.bots_calculators = [
            self._jad, #Jade Emporer
            self._alp, #Alpha
            self._bet, #Beta
            self._gam, #Gamma
            self._wiq, #Wi Qe Lu
            self._stu, #StudiousBot
            self._pla, #Plagiarist
            self._san, #Santayana
            self._tho, #Thomas
            self._dru, #Drunkard
            self._yin, #YinYang
            self._con, #Contrary
            self._tit, #TitForTat
            self._equ, #Equalizer
            self._mar, #Marx
        ]
        self.bot_types = len(self.bots_calculators)
    def ask(self, n, id):
        #if we can, show that hardcoding is no match for the power of heuristics:
        if n == 2:
            return [False, True]
        #otherwise, refer to the wisdom of Mayor Prentiss in order to command The Ask
        #i.e. hashes a quote, and uses that as the exam.
        salt = b"I AM THE CIRCLE AND THE CIRCLE IS ME " * n
        return self._md5_from(salt, n)
    def answer(self, n, id):
        #uses the power of heuristics to predict what the average bot will do
        #ignores all inputs except the length of the output
        #very approximate, and deterministic
        #i.e. every game, Count Oracular will send the same lists of answers, in the same order
        best_guess_totals = [0.5] * n #halfway between T and F
        for bot in self.bots_calculators:
            exam, confidence = bot(n)
            if not exam:
                continue
            while len(exam) < n:
                #ensure exam is long enough
                exam += exam[:1]
            exam = exam[:n] #ensure exam is short enough
            #map T and F to floats [0,1] based on confidence
            weighted_exam = [0.5+confidence*(0.5 if q else -0.5) for q in exam]
            best_guess_totals = [current+new for current,new in zip(best_guess_totals, weighted_exam)]
        best_guess_averages = [total/self.bot_types
            for total
            in best_guess_totals
        ]
        best_guess = [avg > 0.5 for avg in best_guess_averages]
        self.last_round = best_guess
        return best_guess
    def update(self, ranks, own, others):
        pass
    def _md5_from(self, data, n):
        md5 = hashlib.md5(data)
        for i in range(n):
            md5.update(data)
        exam = []
        while len(exam) < n:
            exam += [x == "0"
                for x
                in bin(int(md5.hexdigest(), 16))[2:].zfill(128)
            ]
            md5.update(data)
        return exam[:n]
    def _invert(self, exam):
        return [not val for val in exam]
    def _digits_to_bools(self, iterable):
        return [char=="1" for char in iterable]
    def _plagiarise(self, n):
        copy = (self.last_round * n)[:n]
        return copy

    '''functions to calculate expected exams for each other bot:
       (these values, weighted with corresponding confidence ratings,
       are summed to calculate the most likely exam.)'''
    def _jad(self, n):
        '''Calculate the mean of _jad's distribution, then
        use that as the guess'''
        mean = max(int(np.sqrt(np.power(2,n))), (2<<n)-1)
        string_mean = f"{mean}".zfill(n)
        exam = self._invert(self._digits_to_bools(string_mean))
        return exam, 0.5
    def _alp(self, n):
        '''Alpha uses a predictable hash,
        until it figures out we aren't Beta,
        modelled by the probability of giving or solving
        Alpha's exam'''
        #probability that Alpha thinks we're Beta
        #assuming we fail to pretend to be Beta if we meet Alpha
        chance_beta = ((1 - 1/self.bot_types) ** n) ** 2
        return self._md5_from(b"Beta", n), chance_beta
    def _gam(self, n):
        '''Gamma is like Beta, except after realising,
        switches to 50-50 random choice of inverse
        either Beta or Alpha's hash'''
        #probability that Gamma thinks we're Alpha still
        #(Unlikely that Gamma will think we're Beta;
        #we'd need to fail Alpha but pass Beta,
        #therefore, not accounted for)
        chance_unknown = ((1 - 1/self.bot_types) ** n) ** 2
        #default exam that assumes that Gamma thinks we're Alpha
        exam = self._md5_from(b"Beta", n)
        if chance_unknown > 0.5:#there exists a better heuristic here
            #assume Gamma will consider us Alpha
            confidence = chance_unknown
        else:
            #assume Gamma considers us neither Alpha nor Beta
            alpha = self._invert(self._md5_from(b"Beta", n))
            beta = self._invert(self._md5_from(b"Alpha", n))
            #check for bools where both possible exams match
            and_comp = [a and b for a, b in zip(alpha, beta)]
            nor_comp = [not (a or b) for a, b in zip(alpha, beta)]
            #count up matches vs times when fell back on default
            #to calculate ratio of default
            #to bools where hashes agree
            confidence_vs_default = (sum(and_comp)+sum(nor_comp)) / n
            confidence = confidence_vs_default * chance_unknown + (1 - confidence_vs_default) * (1 - chance_unknown)
            for i in range(n):
                if and_comp[i]:
                    exam[i] = True
                if nor_comp[i]:
                    exam[i] = False
        return exam, confidence
    def _bet(self, n):
        '''Beta is like Alpha, but with a different hash'''
        #probability we haven't matched with Beta yet
        #i.e. probability that Beta still thinks we're Alpha
        chance_alpha = ((1 - 1/self.bot_types) ** n) ** 2
        return self._md5_from(b"Alpha", n), chance_alpha
    def _wiq(self, n):
        '''Wi Qe Lu is hard to model, so we pretend
        that it mimicks Plagiarist for the most part'''
        if n == 1:
            #first round is random
            return [False], 0
        #other rounds are based on exams it met
        #leaning towards same as the previous exam
        return self._plagiarise(n), 0.1
    def _stu(self, n):
        '''StudiousBot is random'''
        return [False] * n, 0
    def _pla(self, n):
        '''Plagiarist copies the exams it received,
        which can be modelled with the standard prediction
        calculated for the previous round, padded with its first
        element.'''
        if n == 1:
            return [True], 1
        return self._plagiarise(n), 0.3
    def _san(self, n):
        '''Santayana is based on answers, which we don't predict.
        Modelled as random.'''
        #mostly random, slight leaning towards default False
        return [False] * n, 0.1
    def _tho(self, n):
        '''Thomas has an unpredictable threshold.'''
        #for all intents, random
        return [False] * n, 0
    def _dru(self, n):
        '''Drunkard is utterly random.'''
        return [False] * n, 0
    def _yin(self, n):
        '''YinYang inverts itself randomly, but not unpredictably.
        We can model it to find the probability. Also notably,
        one index is inverted, which factors into the confidence
        especially for lower n.'''
        if n == 1:
            #one element is inverted, so whole list must be False
            return [False], 1
        if n == 2:
            #split half and half randomly; can't predict
            return [True] * n, 0
        #cumulative chance of mostly ones or mostly zeros
        truthy = 1
        for _ in range(n):
            #simulate repeated flipping
            truthy = truthy * 0.44 + (1-truthy) * 0.56
        falsey = 1 - truthy
        if falsey > truthy:
            return [False] * n, falsey - 1/n
        return [True] * n, truthy - 1/n
    def _con(self, n):
        '''Contrary is like Jade Emporer, but inverts itself
        so much that modelling the probability of inversion
        is not worth the effort.'''
        #there are some clever ways you could do statistics on this,
        #but I'm content to call it uniform for now
        return [False] * n, 0
    def _tit(self, n):
        '''TitForTat is most likely to give us False
        but the confidence drops as the chance of having
        met TitForTat increases.
        The square root of the probability we calculate for
        Alpha, Beta and Gamma, because those also care about what
        we answer, whereas TitForTat only cares about what we ask'''
        #probability that we've not given TitForTat an exam
        chance_friends = (1 - 1/self.bot_types) ** n
        return [False] * n, chance_friends
    def _equ(self, n):
        '''Equalizer always asks True'''
        #certain that Equalizer's exam is all True
        return [True] * n, 1
    def _mar(self, n):
        '''Marx returns mostly True, randomised based on our rank.
        We don't predict our rank.
        There's ~50% chance an answer is random'''
        #75% chance we guess right (= 50% + 50%*50%)
        return [True] * n, 0.75

Відмінна ідея в теорії, але на своєму першому конкурсі граф Оракуляр виступив гірше, ніж YinYang, незважаючи на його зусилля з імітації YinYang.
Фіолетовий П

1
@PurpleP Так, це не дуже добре. Причина полягає в тому, що вона намагається вибрати "загалом оптимальну" стратегію шляхом усереднення всіх конкретних стратегій разом. Наприклад, він не використовує стратегію, пристосовану для перемоги над YinYang, коли вона стикається з YinYang. Він навіть не використовує конкретної стратегії Jade Emporer: вона просто додає стратегію Jade Emporer до середнього. Це буде краще, ніж випадкове, але не набагато.
IFcoltransG

Маркса виправлено. Ви повинні оновити графа Oracular, щоб передбачити це.
Фіолетовий P

@PurpleP Маркса зараз слід підтримувати. Наче знову 1917 рік.
IFcoltransG

2

Інь Янь

Відповіді або всі, Trueабо всі False, за винятком одного індексу, випадково вибраного як протилежний. Запитує протилежне тому, що відповідає. Міняє випадковим чином, щоб скинути супротивників.

import random

class YinYang:
    def __init__(self, ID, n):
        self.exam = True

    def update(self, rankList, ownExam, otherExams):
        if random.random() < 0.56:
            self.exam = not self.exam

    def answer(self, n, ID):
        a = [not self.exam] * n
        a[random.randint(0, n-1)] = self.exam
        return a

    def ask(self, n, ID):
        e = [self.exam] * n
        e[random.randint(0, n-1)] = not self.exam
        return e

Wi Qe Lu (Switcheroo)

Відповідає і запитує випадковим чином у першому турі. Згодом він використовує відповіді з попереднього іспиту та змінює питання, якщо вище середня кількість конкурентів отримала право.

class WiQeLu:
    def __init__(self, ID, n):
        self.rounds = 1
        self.firstexam = True
        self.firstanswer = True
        self.lastexaminer = -1
        self.exam = []
        self.pastanswers = {}

    def update(self, rankList, ownExam, otherExams):
        questions, lastanswers = ownExam
        self.pastanswers[self.lastexaminer] = questions

        if len(otherExams) == 0:
            return
        correctCounts = [0 for i in otherExams[0][0]]
        for ourExam, response in otherExams:
            for i in range(len(response)):
                if ourExam[i] == response[i]:
                    correctCounts[i] += 1

        newExam = otherExams[0][0]
        meanWhoAnsweredCorrectly = sum(correctCounts) / len(correctCounts)
        for i in range(len(correctCounts)):
            if correctCounts[i] > meanWhoAnsweredCorrectly:
                newExam[i] = not newExam[i]
        self.exam = newExam

    def answer(self, n, ID):
        self.lastexaminer = ID
        if ID not in self.pastanswers:
            randomanswer = [random.randint(0, 1) == 1] * n
            self.pastanswers[ID] = randomanswer
            return randomanswer
        return (self.pastanswers[ID] * n)[:n]

    def ask(self, n, ID):
        if self.firstexam:
            self.firstexam = False
            self.exam = [random.randint(0, 1) == 1] * n
        return (self.exam * n)[:n]

5
За даними Google Translate, "wi qe lu" грубо перекладається як "Я пінгвінова дорога".
Фіолетовий P

2

Один мій бот:

Томас

Мандрівник із далекої землі, має кілька небезпечних уявлень про минулі результати, що свідчать про майбутнє виконання. Він використовує їх, щоб утримати інших ботів, якщо це не заважає його просуванню.

class Thomas:
    def __init__(self,ID,n):
        N=10
        self.ID=ID
        self.myrank=n
        self.lowerank=0
        #The highest number of questions is equal to the number of participants, so we can do this:
        self.probs=[{i:1.0/N for i in np.linspace(0,1,num=N)} for i in np.arange(n)]
        self.output=[0.5]*n

    def ask(self,n,ID):
        if self.myrank==1 and self.lowerrank > 1: #I can't advance without promoting somebody first
            return [self.output[i]>np.random.rand() for i in np.arange(n)]
        #Otherwise, try to step on their fingers by going against the expected probability
        return [self.output[i]<np.random.rand() for i in np.arange(n)]


    def answer(self,n,ID):
        return [self.output[i]>np.random.rand() for i in np.arange(n)]

    def update(self,rankList,ownExam,otherExams):
        #Update our ranks
        self.myrank=len([i for i in rankList if i==rankList[self.ID]])
        self.lowerrank=len([i for i in rankList if i==rankList[self.ID]-1])
        #Update our expectations for each input we've been given
        self.bayesianupdate(ownExam[0])
        for ex in otherExams:
            self.bayesianupdate(ex[1])
        #Compress into output variable
        self.output=[np.sum([l[entry]*entry for entry in l]) for l in self.probs]

    def bayesianupdate(self,data):
        for i in np.arange(len(data)):
            if data[i]: #Got a True
                self.probs[i].update({entry:self.probs[i][entry]*entry for entry in self.probs[i]})
            else: #Got a False
                self.probs[i].update({entry:self.probs[i][entry]*(1-entry) for entry in self.probs[i]})
            s=np.sum([self.probs[i][entry] for entry in self.probs[i]]) #Renormalize
            self.probs[i].update({entry:self.probs[i][entry]/s for entry in self.probs[i]})
```

Ви забули відступити свій код після заяви класу?
pppery

Ось тільки формат SE виловлює мене зненацька. Я
виправлю

2

Альфа

Прочитайте чат, перш ніж зволікати. Ці боти не порушують жодних правил. ОП навіть заохочує співпрацюючих ботів.

Альфа формує команду разом з Beta. Обидва використовують заздалегідь визначений набір іспитів, щоб допомогти один одному піднятись на звання. Також обидва використовують переваги ботів, використовуючи однакові іспити знову і знову.

import numpy as np
import hashlib

class Alpha:
    def __init__(self, ID, n):
        self.alpha = hashlib.md5(b"Alpha")
        self.beta = hashlib.md5(b"Beta")
        self.asker = -1
        self.betas = set(range(n)).difference([ID])
        self.fixed = set(range(n)).difference([ID])
        self.fixedExams = [[]] * n

    def ask(self,n,ID):
        if ID in self.betas:
            return self.md5ToExam(self.alpha, n)
        else:
            return list(np.random.choice([True, False], n))

    def answer(self,n,ID):
        self.asker = ID
        if self.asker == -1:
            return [True] * n
        elif self.asker in self.fixed and len(self.fixedExams[self.asker]) > 0:
            return (self.fixedExams[self.asker] * n)[:n]
        elif self.asker in self.betas:
            return self.md5ToExam(self.beta, n)
        else:
            return list(np.random.choice([True, False], n))

    def update(self,rankList,ownExam,otherExams):
        if self.asker >= 0:
            if self.asker in self.betas and ownExam[0] != self.md5ToExam(self.beta, len(ownExam[0])):
                    self.betas.remove(self.asker)
            if self.asker in self.fixed:
                l = min(len(ownExam[0]), len(self.fixedExams[self.asker]))
                if ownExam[0][:l] != self.fixedExams[self.asker][:l]:
                    self.fixed.remove(self.asker)
                    self.fixedExams[self.asker] = []
                elif len(ownExam[0]) > len(self.fixedExams[self.asker]):
                    self.fixedExams[self.asker] = ownExam[0]
        self.alpha.update(b"Alpha")
        self.beta.update(b"Beta")

    def md5ToExam(self, md5, n):
        return [x == "0" for x in bin(int(md5.hexdigest(), 16))[2:].zfill(128)][:n]

Я вважаю, що ці три боти порушують правила ОП, як зазначено у запиті та коментарях.
Дон Тисяча

@DonThousand Якщо ви прочитаєте дискусію в чаті, ви побачите, що вони не порушують правила. chat.stackexchange.com/rooms/98905/imperial-exams-office
Sleafar

Справедливо. Моє ліжко.
Дон Тисяча

@DonThousand Тож який був сенс відмовити їх у всьому?
Sleafar

Я лише відмовився від Альфи. Я не можу відмовитись. Зробіть зайву редагування, і я це виправлю.
Дон Тисяча

1

Еквалайзер

Усі повинні бути рівними (ні з якою дурною імператорською дурницею), тому забезпечуйте якомога більше соціальної мобільності. Зробіть питання справді простими (відповідь завжди правдивою), щоб люди могли досягти успіху.

class Equalizer:
    def __init__(self, ID, n):
        self.previousAnswers = [[0, 0] for _ in range(n)]
        self.previousAsker = -1

    def ask(self, n, ID):
        return [True] * n

    def answer(self, n, ID):
        if ID == -1:
            return [True] * n

        # Assume that questions from the same bot will usually have the same answer.
        t, f = self.previousAnswers[ID]
        return [t >= f] * n

    def update(self, rankList, ownExam, otherExams):
        if self.previousAsker == -1:
            return

        # Keep track of what answer each bot prefers.
        counts = self.previousAnswers[self.previousAsker]
        counts[0] += ownExam[0].count(True)
        counts[1] += ownExam[0].count(False)

1

Бета-версія

Прочитайте чат, перш ніж зволікати. Ці боти не порушують жодних правил. ОП навіть заохочує співпрацюючих ботів.

Бета формує команду разом з Альфою. Обидва використовують заздалегідь визначений набір іспитів, щоб допомогти один одному піднятись на звання. Також обидва використовують переваги ботів, використовуючи однакові іспити знову і знову.

import numpy as np
import hashlib

class Beta:
    def __init__(self,ID,n):
        self.alpha = hashlib.md5(b"Alpha")
        self.beta = hashlib.md5(b"Beta")
        self.asker = -1
        self.alphas = set(range(n)).difference([ID])
        self.fixed = set(range(n)).difference([ID])
        self.fixedExams = [[]] * n

    def ask(self,n,ID):
        if ID in self.alphas:
            return self.md5ToExam(self.beta, n)
        else:
            return list(np.random.choice([True, False], n))

    def answer(self,n,ID):
        self.asker = ID
        if self.asker == -1:
            return [True] * n
        elif self.asker in self.fixed and len(self.fixedExams[self.asker]) > 0:
            return (self.fixedExams[self.asker] * n)[:n]
        elif self.asker in self.alphas:
            return self.md5ToExam(self.alpha, n)
        else:
            return list(np.random.choice([True, False], n))

    def update(self,rankList,ownExam,otherExams):
        if self.asker >= 0:
            if self.asker in self.alphas and ownExam[0] != self.md5ToExam(self.alpha, len(ownExam[0])):
                    self.alphas.remove(self.asker)
            if self.asker in self.fixed:
                l = min(len(ownExam[0]), len(self.fixedExams[self.asker]))
                if ownExam[0][:l] != self.fixedExams[self.asker][:l]:
                    self.fixed.remove(self.asker)
                    self.fixedExams[self.asker] = []
                elif len(ownExam[0]) > len(self.fixedExams[self.asker]):
                    self.fixedExams[self.asker] = ownExam[0]
        self.alpha.update(b"Alpha")
        self.beta.update(b"Beta")

    def md5ToExam(self, md5, n):
        return [x == "0" for x in bin(int(md5.hexdigest(), 16))[2:].zfill(128)][:n]

1

Гамма

Прочитайте чат, перш ніж зволікати. Ці боти не порушують жодних правил. ОП навіть заохочує співпрацюючих ботів.

Гамма виявив плани Альфа та Бета і намагається скористатися ними обома, маскуючись під одного з них.

import numpy as np
import hashlib

class Gamma:
    def __init__(self, ID, n):
        self.alpha = hashlib.md5(b"Alpha")
        self.beta = hashlib.md5(b"Beta")
        self.asker = -1
        self.alphas = set(range(n)).difference([ID])
        self.betas = set(range(n)).difference([ID])
        self.fixed = set(range(n)).difference([ID])
        self.fixedExams = [[]] * n

    def ask(self,n,ID):
        if ID in self.alphas:
            return self.md5ToExam(self.beta, n)
        elif ID in self.betas:
            return self.md5ToExam(self.alpha, n)
        else:
            return self.md5ToWrongExam(np.random.choice([self.alpha, self.beta], 1)[0], n)

    def answer(self,n,ID):
        self.asker = ID
        if self.asker == -1:
            return [True] * n
        elif self.asker in self.fixed and len(self.fixedExams[self.asker]) > 0:
            return (self.fixedExams[self.asker] * n)[:n]
        elif self.asker in self.alphas:
            return self.md5ToExam(self.alpha, n)
        elif self.asker in self.betas:
            return self.md5ToExam(self.beta, n)
        else:
            return list(np.random.choice([True, False], n))

    def update(self,rankList,ownExam,otherExams):
        if self.asker >= 0:
            if self.asker in self.alphas and ownExam[0] != self.md5ToExam(self.alpha, len(ownExam[0])):
                    self.alphas.remove(self.asker)
            if self.asker in self.betas and ownExam[0] != self.md5ToExam(self.beta, len(ownExam[0])):
                    self.betas.remove(self.asker)
            if self.asker in self.fixed:
                l = min(len(ownExam[0]), len(self.fixedExams[self.asker]))
                if ownExam[0][:l] != self.fixedExams[self.asker][:l]:
                    self.fixed.remove(self.asker)
                    self.fixedExams[self.asker] = []
                elif len(ownExam[0]) > len(self.fixedExams[self.asker]):
                    self.fixedExams[self.asker] = ownExam[0]
        self.alpha.update(b"Alpha")
        self.beta.update(b"Beta")

    def md5ToExam(self, md5, n):
        return [x == "0" for x in bin(int(md5.hexdigest(), 16))[2:].zfill(128)][:n]

    def md5ToWrongExam(self, md5, n):
        return [x == "1" for x in bin(int(md5.hexdigest(), 16))[2:].zfill(128)][:n]

1

TitForTat

Задає вам прості запитання, якщо ви раніше задавали прості запитання. Якщо ви ніколи не здавали іспит, він за замовчуванням ставить до легких питань.

Крім того, не довіряє тому, хто задає складні запитання, і дасть на них непередбачувані відповіді.

import numpy as np

class TitForTat:
    def __init__(self, ID, n):
        self.friendly = [True] * n
        self.asker = -1

    def make_answers(self, n, ID):
        if ID == -1 or self.friendly[ID]:
            return [False] * n
        else:
            return list(np.random.choice([True, False], n))

    def ask(self, n, ID):
        return self.make_answers(n, ID)

    def answer(self, n, ID):
        self.asker = ID
        return self.make_answers(n, ID)

    def update(self, rankList, ownExam, otherExams):
        if self.asker != -1:
            # You are friendly if and only if you gave me a simple exam
            self.friendly[self.asker] = all(ownExam[0])

Цей бот працює добре, якщо інші боти співпрацюють з ним. Наразі співпрацює лише Еквалайзер, але цього, сподіваємось, має бути досить.


На даний момент бот не може змагатися, оскільки він не відповідає технічним умовам. Переконайтеся, що він постійно повертає listоб’єкти. Крім того, як за старими, так і за оновленими правилами, ідеальні копії бота не є дійсними поданнями, тому дозволена кількість екземплярів цього бота працює 1.
AlienAtSystem

Я редагував це, щоб повернути списки. Що стосується ідеальних копій, то немає жодного поточного бота, який би належним чином співпрацював з нею, тому кількість ботових копій ботів - мінімум, необхідний для успішного втілення в життя стратегії - становить щонайменше 1 (цей бот і 1 його копія потрібні ).
Анонім

Ви стверджуєте, що ви маєте право на виняток відповідно до пункту 3, намагаючись подати те, що підпадає під пункт 1: Ідеальні копії бота ніколи не є дійсними, винятків немає. І щоб мати право на виняток із пункту 3, вам доведеться довести, що ваша стратегія суворо вимагає, щоб усі ці партнери реагували на неї, як, наприклад, сигнал рукостискання, який справді марний, коли хтось не слухає. Твій ні. Еквалайзер здасть вам іспити, щоб запустити "дружній" пункт, тим самим спростуючи, що потрібна копія вашого бота.
AlienAtSystem

Добре тоді. Я внесу кілька остаточних коректив.
Анонім

0

Навпаки

Імператор нефриту завжди правий, тому він реалізує функцію запитання імператора нефриту як власну функцію відповіді, коли йому потрібно більше 2 відповідей. За 1 відповідь він відповідає true(гідний шанс на правильність), а за 2 - це відповідьtrue,false (ця відповідь передає "принаймні половину" питань три з чотирьох можливих вікторин, краще, ніж вибір навмання).

У своєму Онові використовується аналогічна логіка щодо того, як вона змінює свою схему запитання, але логіка її запитання схожа на імператор нефриту, лише з іншою вагою. Коливається між більш високими значеннями trueз більш високими значеннями, falseколи занадто багато кандидатів набирають достатньо високого рівня, щоб пройти.

class Contrary:
    def __init__(self,ID,n):
        self.rank = 0
        self.ID = ID
        self.competitors = {}
        self.weight = -2
        pass

    def ask(self,n,ID):
        if self.weight > 0:
            num=min(np.random.exponential(scale=np.sqrt(np.power(self.weight,n))),np.power(2,n)-1)
            bi=list(np.binary_repr(int(num),width=n))
            return [x=='0' for x in bi]
        else:
            num=min(np.random.exponential(scale=np.sqrt(np.power(-self.weight,n))),np.power(2,n)-1)
            bi=list(np.binary_repr(int(num),width=n))
            return [x=='1' for x in bi]

    def answer(self,n,ID):
        if n == 1:
            return [True]
        if n == 2:
            return [True,False]
        num=min(np.random.exponential(scale=np.sqrt(np.power(2,n))),np.power(2,n)-1)
        bi=list(np.binary_repr(int(num),width=n))
        return [x=='0' for x in bi]

    def update(self,rankList,ownExam,otherExams):
        self.rank = rankList[self.ID];
        if len(otherExams) == 0:
            return
        correctCounts = [0 for i in otherExams[0][0]]
        for ourExam, response in otherExams:
            for i in range(len(response)):
                if ourExam[i] == response[i]:
                    correctCounts[i] += 1

        meanWhoAnsweredCorrectly = sum(correctCounts) / len(correctCounts)
        for i in range(len(correctCounts)):
            if correctCounts[i]+1 > meanWhoAnsweredCorrectly:
                self.weight = np.copysign(np.random.uniform(1,3),-self.weight)

1
Не виходить, true, falseякщо іспит false, true?
pppery

Перші кілька рядків answerмають синтаксис і ім'я помилки - trueі falseповинно бути , Trueі False, і ifs не вистачає :S в кінці
Sara J

Дякую вам двом; У мене не було налаштовано Python на своїй машині, оскільки я його не використовую так часто, тому регулярно псую синтаксис.
Draco18s більше не довіряє SE

newExam встановлений, але ніколи не читається в update. passце команда NOP, її можна видалити. (Коментар позаду нього просто гра слів для п'яниці ви скопіювали.) Крім того , ви неявно з допомогою mathі randomмодулі , але не оголосити , що ви імпортували їх. Я переписав це у свій файл конкурсу, np.copysignі np.random.uniformце має зробити те ж саме.
AlienAtSystem

@AlienAtSystem слід виправити зараз.
Draco18s більше не довіряє SE

0

Маркс

Це бот Маркса. Він вважає, що замість бюрократії ми повинні мати комуністичну систему. Щоб допомогти досягти цієї мети, вона дає складніші вікторини для вищих ботів. Він також дає більше випадкових відповідей на вікторини від вищих ботів, тому що вони, ймовірно, розумніші, бо вони вище.

import numpy as np

class Marx():
    def __init__(self, ID, n):
        self.ID = ID
        self.n = n
        self.ranks = [] # The bot rankings
        self.e = [] # Our quiz
        self.rank = 0 # Our rank
    def ask(self, n, ID):
        test = [True] * n
        # Get the rank of the bot being quizzed
        if self.ranks:
            rank = self.ranks[ID]
        else:
            rank = 0
        for i in range(len(test)):
            item = test[i]
            if np.random.uniform(0, rank / self.n) > 0.5:
                # If the bot is higher ranking, make the quiz harder
                item = np.random.choice([True, False], 1)[0]
            test[i] = item
        # IF the test is not long enough, add Falses to the end
        while len(test) < n - 1:
            test.append(False)
        return test
    def answer(self, n, ID):
        # Get the rank of the asking bot
        if self.ranks:
            rank = self.ranks[ID]
        else:
            rank = 0
        if self.e:
            # Pad our quiz with Falses so it will not throw IndexError
            while len(self.e) < n:
                self.e.append(False)
            for i in range(len(self.e)):
                item = self.e[i]
                if np.random.uniform(0, rank / self.n) > 0.5:
                    # Assume that higher ranking bots are cleverer, so add more random answers
                    item = np.random.choice([True, False], 1)[0]
                self.e[i] = item
            if len(self.e) > self.rank + 1:
                self.e = self.e[:self.rank + 1]
            return self.e
        else:
            # If it is the first round, return all Trues
            return [True] * n
    def update(self, rankList, ownExam, otherExams):
        # Update our list of ranks
        self.ranks = rankList
        # Store the quiz we were given, to give to the next bot
        self.e = ownExam[0]
        # Store our rank
        self.rank = rankList[self.ID]

На даний момент Маркс відповідає занадто багато байтів, тому він не може зараз змагатися
AlienAtSystem

Що ви маєте на увазі? Його іспити / відповіді занадто довгі?
sugarfi

Його відповідь - одна запис занадто довга
AlienAtSystem

Гаразд, я це виправив. Зараз має бути добре.
sugarfi

Вибачте, я дав неправильні відгуки: зараз відповіді є байтом занадто коротким. Справжня проблема полягає в тому, що ви розширюєте self.e, коли це занадто коротко (хоча зараз недостатньо), але не обрізайте його, коли Маркса стає зневажено.
AlienAtSystem
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.