Небесная бюрократия КОТ


14

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

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

  • Божественная бюрократия состоит из неотрицательных целочисленных рангов, начиная с 0. Каждый член (бот) бюрократии принадлежит одному рангу. Каждый ранг может содержать произвольно много членов, но не может быть пустым, если все ранги выше не пусты
  • В начале игры все участники имеют ранг 0
  • Каждый ход, каждый член бюрократии должен ответить на экзамен. Экзамен состоит из правильного угадывания логических значений списка. Длина списка - это номер ранга над членом.
  • Экзаменационные вопросы подготовлены случайным членом ранга выше. Члены высшего ранга получают свои вопросы непосредственно от JadeEmperor(см. Ниже)
  • Участник, набравший не менее 50% на экзамене, имеет право на повышение. Участник, набравший менее 50% на экзамене, может быть понижен в должности.
  • У участника, имеющего право на понижение в должности, его ранг уменьшается на единицу только в том случае, если на его место претендует участник, имеющий право на повышение в звании ниже.
  • Всем участникам, имеющим право на повышение по службе, свой ранг повышается на единицу до тех пор, пока это положение не оставляет пустым.
  • Если не все правомочные участники могут быть понижены в должности или повышены в должности, предпочтение отдается тем, кто имеет наименьшее значение (для понижения в должности), соответственно. наивысший (для продвижения) балл. Галстуки рвутся случайно.
  • Ранг участника может меняться не более чем на 1 каждый ход.

Правила игры:

  • Каждому боту будет случайным образом присвоен идентификатор в начале игры, который не изменится в течение его хода. JadeEmperorИмеет идентификатор -1, а все остальные имеют последовательные идентификаторы неотрицательных, начиная с 0.
  • Все боты соревнуются одновременно
  • Игра длится 100 ходов, оценка бота - это его среднее звание за все время.
  • Общий балл получается путем запуска 1000 игр и усреднения результатов.
  • Каждый бот представляет собой класс Python 3, реализующий следующие четыре функции:
    • ask(self,n,ID), который делает экзамен, возвращая listлогическое значение длины n. ID - это идентификатор бота, который должен угадать этот список. ask()может быть вызван много раз в течение одного раунда для любого бота, но также и вовсе.
    • answer(self,n,ID), которая является попыткой ответить на экзамен, возвращая listлогическое значение длины n. ID - это идентификатор бота, ask()сгенерировавшего экзамен. answer()вызывается ровно один раз за раунд для каждого бота.
    • update(self,rankList,ownExam,otherExams)вызывается после того, как контроллер выполнил все про- и понижения. Его аргументы: список целых чисел, перечисляющий все ранги по идентификатору всех ботов; кортеж, состоящий из двух списков, сначала экзаменационные вопросы, затем ответы, которые дал бот (на случай, если он забыл); затем список кортежей, аналогично состоящих из пар экзамен-ответ, на этот раз для всех экзаменов, которые раздает бот.
    • __init__(self, ID, n) передает боту свой собственный идентификатор и количество конкурирующих ботов.
  • Классы могут реализовывать другие функции для частного использования.
  • Определение дополнительных переменных и их использование для хранения данных о прошедших экзаменах разрешено в явном виде.
  • Запрещены метаэффекты программирования, то есть любые попытки прямого доступа к коду других ботов, к коду контроллера, вызывающие исключения или аналогичные. Это конкурс стратегий для экзаменов, а не взлома кода.
  • Боты, пытающиеся помочь друг другу, явно разрешены, если они делают это не через метаэффекты, а исключительно посредством информации, передаваемой через update()
  • Другие языки разрешены только в том случае, если они могут быть легко преобразованы в Python 3.
  • Библиотека NumPy будет импортирована как np. Версия 1.6.5 означает, что она использует старую случайную библиотеку. Если у вас numpy 1.7, старые функции доступны numpy.random.mtrandдля тестирования. Пожалуйста, не забудьте убрать mtrand для отправки.
  • Если бот вызывает исключение во время выполнения, он дисквалифицируется. Любой бот, код которого настолько запутан, что невозможно определить, генерирует ли он список длины n, когда ask()или answer()вызван, также будет дисквалифицирован. Бот, принуждающий меня к глубокому копированию, получает -1 на счету.
  • Имена классов должны быть уникальными
  • Допускается использование нескольких ботов на человека, но из итеративно обновленных ботов будет взята только последняя версия.
  • Поскольку, похоже, существует некоторая путаница в отношении сходства ботов:
    • Вы не можете опубликовать копию другого бота. Это единственная стандартная лазейка, которая действительно применяется в этой задаче.
    • Вам разрешено делиться кодом с другими ботами, включая ботов других людей.
    • Вам не разрешено представлять бота, который отличается от другого только тривиальным изменением стратегии (например, изменением начального числа для генерации вопроса), если вы не можете доказать, что количество таких копий-ботов является минимально необходимым для успешного выполнения. принятие их стратегии (обычно это два бота для сотрудничества).

Пример ботов:

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.870821StudiousBotДигниссимус - Спам7.538537СантаянаСара Дж7.095528Плагиатор6.522047CountOracularIFcoltransG5.881175ТомасЧужеродные @ System5.880041противоречащийDraco18s5.529652Марксsugarfi5.433808пьяница5.328178Инь ЯньФиолетовый П5.102519Эквалайзермнемонический4.820996TitForTatанонимное3,35801

Конкурсы будут проводиться с каждой новой записью в обозримом будущем.


1
Копии ботов являются стандартной лазейкой, так что нет. Если вы попытаетесь использовать несколько ботов в соответствии с правилом автора, отправляя почти, но не совсем копии, я его удалю.
AlienAtSystem

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

2
Почему аргументы конструктора, ID, nа другие аргументы метода n, ID?
Фиолетовый P

1
@DonThousand, потому что я считаю, что в соответствии с данными ограничениями было бы достаточно сделать два бота, которые А) успешно рукопожатие (обратите внимание, что Плагиаризатор может случайно сыграть человека посередине) и Б) затем принять стратегию, которая надежно помогает этому боту но нет другого, чтобы подняться.
AlienAtSystem

1
@ кто-то занимает верхнюю строчку. Вы начинаете с 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

Учебный Бот

Этот бот готовится к тестам! Он пытается найти шаблоны в тестах, выдаваемых различными ботами, и действует согласованно.

С моей стороны, пока этот бот превосходит всех других ботов, которых я мог получить на своем компьютере, кроме Alpha, Beta и Gamma (которые были запрограммированы для совместной работы). Бот не использует тот факт, что совместная работа разрешена, потому что я чувствовал, что это было немного похоже на обман и немного грязный. Хотя, глядя на это, кажется, что объединение в команду кажется весьма эффективным.

Бот пытается распознать, когда ответы на тесты случайны и в ответе совпадают, что, как мы надеемся, составляет в среднем 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ě (по-китайски «ученый»), что по совпадению звучит как «switcher».
Фиолетовый P

Я взломал его и провел экзамены на моей машине. Любопытно, что он превзошел Studious Bot, но не Wi Qe Lu.
Фиолетовый P

@PurpleP Ха-ха! Это звучит очень интересно, я не думаю, что у меня достаточно времени, чтобы улучшить своего бота, но вы можете опубликовать его здесь в качестве представления
Dignissimus - Spammy

3

Граф оракул

Этот бот использует алгоритм, который усредняет экзамены всех других работающих ботов (учитывая количество раундов и некоторую ужасную эвристику) для определения того, что каждый бот будет устанавливать в качестве экзамена.
Граф просит свои экзамены, используя хэш md5. Поэтому и его вопросы, и ответы на них являются детерминированными. Он игнорирует большинство входных данных, спрашивая и отвечая на те же самые последовательности булевых значений, дождя или блеска, в том числе против Jade Emporer.

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

Отличная идея в теории, но в своем первом конкурсе граф Оракулар выступил хуже, чем Инь Янь, несмотря на его попытки симулировать Инь Янь.
Фиолетовый P

1
@PurpleP Да, это не очень хорошо. Причина в том, что он пытается выбрать «в целом оптимальную» стратегию, усредняя все конкретные стратегии вместе. Например, он не использует стратегию, предназначенную для победы над YinYang, когда сталкивается с YinYang. Он даже не использует конкретную стратегию для Jade Emporer: он просто добавляет стратегию Jade Emporer к среднему значению. Это будет лучше, чем случайно, но не намного.
IFcoltransG

Маркс был исправлен. Вы должны обновить Count 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 "wi qe lu" примерно переводится как "Я дорога пингвина".
Фиолетовый Р

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

Альфа

Прочитайте чат, прежде чем понизить. Эти боты не нарушают никаких правил. ОП даже поощряет сотрудничающих ботов.

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

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]

Я считаю, что эти три бота нарушают правила OP, как указано в подсказке и комментариях.
Дон Тысяча

@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
Сара 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.