A burocracia celestial KoTH

14

Na China Imperial, as fileiras da sociedade não eram decididas por nascimento ou riqueza, mas pela capacidade de uma pessoa se destacar nos Exames Imperiais. O Imperador Jade, governante divino dos Céus, pediu que todos os seus súditos fossem examinados para determinar seu valor e a quem, em seguida, atribuir o mandato divino para governar a China.

Regras da burocracia:

  • A Burocracia Divina consiste em classificações com número inteiro não negativas, começando com 0. Cada membro (bot) da burocracia pertence a uma classificação. Cada classificação pode conter muitos membros arbitrários, mas não pode estar vazia, a menos que todos os níveis acima estejam vazios
  • No início do jogo, todos os membros têm classificação 0
  • A cada turno, cada membro da burocracia deve responder a um exame. O exame consiste em adivinhar corretamente os valores booleanos de uma lista. O comprimento da lista é o número da classificação acima do membro.
  • As perguntas do exame são preparadas por um membro aleatório da classificação acima. Os membros do mais alto nível recebem suas perguntas diretamente do JadeEmperor(veja abaixo)
  • Um membro que pontua pelo menos 50% em seu exame é elegível para Promoção. Um membro com pontuação inferior a 50% no exame é elegível para o Demotion.
  • Um membro elegível para o Demotion tem sua classificação reduzida em um somente se houver um membro qualificado para Promoção na classificação abaixo para ocupar o seu lugar.
  • Todos os membros elegíveis para a promoção têm sua classificação aumentada em um, desde que isso não a deixe vazia.
  • Se nem todos os membros elegíveis puderem ser rebaixados ou promovidos, a preferência será para os de menor valor (para rebaixamento). pontuação mais alta (para Promoção). Os laços são quebrados aleatoriamente.
  • A classificação de um membro só pode mudar em no máximo 1 a cada turno.

Regras do jogo:

  • Cada bot receberá um ID aleatoriamente no início do jogo, que não será alterado ao longo do curso. A JadeEmperortem o ID-1, todos os outros têm identificações não-negativos consecutivos, começando com 0.
  • Todos os bots competem ao mesmo tempo
  • O jogo dura 100 turnos, a pontuação do bot é a sua classificação média ao longo desse tempo.
  • A pontuação total é adquirida executando 1000 jogos e calculando a média dos resultados.
  • Cada bot é uma classe Python 3 implementando as quatro funções a seguir:
    • ask(self,n,ID), que faz um exame retornando um listdos booleanos de comprimento n. ID é o ID do bot que precisa adivinhar essa lista. ask()pode ser chamado várias vezes durante uma única rodada para qualquer bot, mas também não.
    • answer(self,n,ID), que é uma tentativa de responder a um exame retornando um listdos booleanos de comprimento n. ID é o ID do bot que ask()gerou o exame. answer()é chamado exatamente uma vez por rodada para cada bot.
    • update(self,rankList,ownExam,otherExams)é chamado assim que o Controlador executa todas as Pro e Demotions. Seus argumentos são: Uma lista de números inteiros, listando todas as classificações por ID de todos os bots; uma tupla, composta por duas listas, primeiro as perguntas do exame, depois as respostas que o bot deu (caso se tenha esquecido); em seguida, uma lista de tuplas, consistindo igualmente em pares de respostas para exames, desta vez para todos os exames que o bot distribuiu.
    • __init__(self, ID, n) passa ao bot seu próprio ID e o número de bots concorrentes.
  • As classes têm permissão para implementar outras funções para uso privado
  • Definir explicitamente outras variáveis ​​e usá-las para armazenar dados sobre exames anteriores é explicitamente permitido.
  • Os meta-efeitos de programação são proibidos, significando qualquer tentativa de acessar diretamente o código de outros bots, o código do Controlador, causando Exceções ou similares. Este é um concurso de estratégias para os exames, não de hackers de código.
  • Bots que tentam ajudar um ao outro são explicitamente permitidos, desde que não o façam por meio de efeitos, mas apenas pelas informações passadas update()
  • Outros idiomas são permitidos apenas no caso de serem facilmente convertidos para Python 3.
  • O numpy da biblioteca será importado como np. A versão é 1.6.5, o que significa que usa a antiga biblioteca aleatória. Se você possui o numpy 1.7, as funções antigas estão disponíveis em numpy.random.mtrandpara teste. Lembre-se de remover o mtrand para envio.
  • Se um bot causa uma exceção durante o tempo de execução, é desqualificado. Qualquer bot cujo código seja tão ofuscado que seja impossível dizer se gera uma lista de comprimento n quando ask()ou answer()é chamado também será desqualificado preventivamente. Um bot que me obriga a copiar cópias em profundidade recebe -1 na pontuação.
  • Os nomes das classes precisam ser exclusivos
  • São permitidos vários bots por pessoa, mas apenas a versão mais recente será obtida dos bots atualizados iterativamente.
  • Como parece haver alguma confusão sobre a semelhança do bot:
    • Você não tem permissão para publicar uma cópia de outro bot. Essa é a única brecha padrão que realmente se aplica a esse desafio.
    • Você tem permissão para compartilhar código com outros bots, incluindo bots de outras pessoas.
    • Você não tem permissão para enviar um bot que difere de outro apenas por uma alteração trivial na estratégia (como uma alteração na semente para a geração de perguntas), a menos que você possa provar que o número desses bots de cópia carbono é o mínimo necessário para obter êxito. promulgação de sua estratégia (geralmente serão dois bots para uma cooperação).

Bots de exemplo:

O JadeEmperoré sempre parte do jogo, mas não compete; ele serve como gerador para exames de bots de classificação mais alta. Seus exames são aleatórios, mas não uniformes, para permitir aos robôs inteligentes uma maneira de avançar.

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]

O Bêbado produz exames e respostas de forma completamente aleatória. Ele fará parte do jogo.

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

O plágio apenas copia os exames anteriores. Ele também fará parte do jogo.

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]

Código do controlador disponível aqui . Para testar, você pode colocar sua própria classe em um arquivo Contestants.py na mesma pasta e eles serão importados.

O Chatroom pode ser encontrado aqui .

Os exames começam!

Pontuação atual, em maior precisão (10.000 execuções) para 20 de outubro:

ParticipanteAutorPontoAlfaSleafar9.669691GamaSleafar9.301362BetaSleafar9.164597WiQeLuP roxo7.870821StudiousBotDignissimus - Spammy7.538537SantayanaSara J7.095528Plagiador6.522047CountOracularIFcoltransG5.881175ThomasAlien @ System5.880041ContrárioDraco18s5.529652Marxsugarfi5.433808Bêbado5.328178YinYangP roxo5.102519EqualizadorMnemônico4.820996TitForTatAnônimo3,35801

Os concursos serão realizados com cada nova inscrição no futuro próximo.

AlienAtSystem
fonte
1
Cópias de bots são uma brecha padrão, então não. Se você tentar abusar dos vários bots por regra de autor enviando cópias quase-mas-não-muito-completas, eu a removerei.
AlienAtSystem 20/09/19
1
@AlienAtSystem Por que você está permitindo que os bots se ajudem? Parece apenas mais caos e aleatoriedade para lidar.
Don Thousand
2
Por que os argumentos do construtor são os argumentos ID, ndo outro método n, ID?
Purple P
1
@DonThousand porque acredito que, de acordo com as restrições dadas, é uma grande façanha fazer dois bots que A) dêem um aperto de mão com sucesso (observe que o Plagiarizer pode jogar acidentalmente com um homem no meio) e B) depois adotem uma estratégia que ajude esse bot de forma confiável mas não há outro para subir.
AlienAtSystem 20/09/19
1
@algumas fileiras contam para cima. Você começa em 0 e trabalho que você forma de números mais altos
AlienAtSystem

Respostas:

4

Santayana

Quem não se lembra do passado é condenado a repeti-lo. Portanto, tomamos nossas decisões com base em como os outros agiram no passado, respondendo com base na resposta que o solicitante normalmente espera de nós em um determinado índice e pedindo a resposta que eles nos deram com menos frequência em um determinado índice .

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 = []
Sara J
fonte
3

Studious Bot

Este bot estuda para testes! Ele tenta encontrar padrões em testes realizados por vários bots e age de acordo.

Até o momento, esse bot supera todos os outros bots que eu poderia trabalhar no meu computador, exceto Alpha, Beta e Gamma (que foram programados para trabalhar juntos). O bot não faz uso do fato de que a formação de equipes é permitida, porque eu senti que era um pouco como trapaça e um pouco suja. Analisando isso, a formação de equipes parece ser bastante eficaz.

O bot tenta reconhecer quando as respostas aos testes são aleatórias e, em resposta, coincide com a esperança de obter uma média de 50% nos testes.

O bot também tenta reconhecer quando um bot simplesmente inverteu suas respostas para expulsar outros bots que estão tentando prever seu comportamento, no entanto, eu não o programei para agir especificamente sobre isso ainda.

Anotei o código com alguns comentários para facilitar a leitura

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]
Dignissimus - Spammy
fonte
A julgar pelo nosso desempenho, você tem o melhor algoritmo para responder e o Wi Qe Lu tem o melhor para perguntar. Proponho que combinemos nossos bots em um único bot, chamado Xuézhě (chinês para "erudito"), que coincidentemente soa como "switcher".
Purple P
Eu o cortei e executei os exames na minha máquina. Curiosamente, superou o Studious Bot, mas não o Wi Qe Lu.
roxo P
@PurpleP Haha! Isso soa muito interessante, eu não acho que há tempo suficiente para eu melhorar meu bot, mas você pode publicá-la como uma submissão aqui
Dignissimus - Spammy
3

Conde Oracular

Esse bot usa um algoritmo que calcula a média dos exames de todos os outros robôs em funcionamento (dado o número da rodada e algumas heurísticas terríveis) para decidir o que cada outro robô definirá como exame.
O Conde faz seus exames usando um hash MD5. Suas perguntas e respostas são, portanto, determinísticas. Ele ignora a maioria das informações, perguntando e respondendo exatamente as mesmas seqüências de booleanos, faça chuva ou faça sol, inclusive contra o 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
IFcoltransG
fonte
Uma ótima idéia em teoria, mas em seu primeiro concurso, o Count Oracular teve um desempenho pior que o YinYang, apesar de seus esforços para simular o YinYang.
Purple P
1
@ RoxpleP Sim, não é muito bom. O motivo é que ele tenta escolher uma estratégia 'geralmente ótima', calculando a média de todas as estratégias específicas juntas. Por exemplo, ele não usa uma estratégia adaptada para vencer o YinYang quando encontra o YinYang. Ele nem usa uma estratégia específica no Jade Emporer: apenas adiciona a estratégia do Jade Emporer à média. Vai ser melhor que aleatório, mas não muito.
IFcoltransG
Marx foi consertado. Você deve atualizar o Count Oracular para prever.
Purple P
@PurpleP Marx deve ser apoiado agora. É como se fosse 1917 novamente.
IFcoltransG
2

YinYang

Responde a todos Trueou a todos False, exceto por um índice escolhido aleatoriamente para ser o oposto. Pergunta o oposto do que responde. Troca aleatoriamente para afastar os adversários.

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)

Responde e pergunta aleatoriamente na primeira rodada. Depois, ele usa as respostas do exame anterior e muda uma pergunta se um número acima da média dos concorrentes acertar.

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]
P roxo
fonte
5
De acordo com o Google Tradutor, "wi qe lu" é traduzido aproximadamente como "eu sou estrada dos pinguins".
roxo P
2

Um bot meu:

Thomas

Um viajante de uma terra longínqua tem algumas idéias perigosas sobre os resultados passados ​​serem indicativos de desempenho futuro. Ele os usa para manter outros bots baixos, a menos que isso sufoque seu próprio avanço.

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]})
```
AlienAtSystem
fonte
Você esqueceu de recuar seu código após a instrução de classe?
precisa saber é
Essa é apenas a formatação SE me pegando de surpresa. Vou corrigi-lo em conjunto com o que causou um erro no teste de alguém quando se usa este bot
AlienAtSystem
2

Alfa

Leia o bate-papo antes da votação. Esses bots não violam nenhuma regra. O PO está até incentivando bots cooperantes.

Alpha está formando uma equipe junto com o Beta. Ambos estão usando um conjunto predefinido de exames para se ajudarem a subir na hierarquia. Além disso, ambos estão aproveitando os bots usando os mesmos exames repetidamente.

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]
Sleafar
fonte
Acredito que esses três bots violam as regras dos OPs, conforme declarado no prompt e nos comentários.
Don Thousand
@ DonThousand Se você ler a discussão no chat, verá que eles não violam as regras. chat.stackexchange.com/rooms/98905/imperial-exams-office
Sleafar
Justo. Foi mal.
22619 Don Thousand
@ DonThousand Então, qual foi o objetivo de reduzir a votação para todos eles?
Sleafar 23/09/19
Eu apenas derrotei o Alpha. No entanto, não posso votar. Faça uma edição supérflua e eu vou consertar.
Don Thousand
1

Equalizador

Todos devem ser iguais (com nada disso bobagem do imperador bobo), para oferecer o máximo de mobilidade social possível. Facilite as perguntas (a resposta é sempre verdadeira) para que as pessoas possam ter sucesso.

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)

fonte
1

Beta

Leia o bate-papo antes da votação. Esses bots não violam nenhuma regra. O PO está até incentivando bots cooperantes.

Beta está formando uma equipe junto com Alpha. Ambos estão usando um conjunto predefinido de exames para se ajudarem a subir na hierarquia. Além disso, ambos estão aproveitando os bots usando os mesmos exames repetidamente.

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]
Sleafar
fonte
1

Gama

Leia o bate-papo antes da votação. Esses bots não violam nenhuma regra. O PO está até incentivando bots cooperantes.

Gamma descobriu os planos de Alpha e Beta e está tentando tirar vantagem de ambos disfarçando-se de um deles.

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]
Sleafar
fonte
1

TitForTat

Faz perguntas fáceis se você fez perguntas fáceis no passado. Se você nunca fez um exame, o padrão é fazer perguntas fáceis.

Além disso, não confia em quem faz perguntas difíceis e fornecerá respostas imprevisíveis.

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

Este bot funciona bem se outros bots cooperarem com ele. Atualmente, apenas o Equalizer coopera, mas espero que isso seja suficiente.

Anônimo
fonte
No momento, o bot não pode competir porque não segue as especificações. Certifique-se de que ele retornelist objetos o tempo todo. Além disso, de acordo com as regras antigas e atualizadas, cópias perfeitas de um bot não são envios válidos; portanto, o número permitido de instâncias desse bot em execução é 1. #
Alien AlienSystem 7/19/19
Eu editei para retornar listas. Quanto à coisa das cópias perfeitas, não existe um bot atual que coopere adequadamente; portanto, o número de bots de cópia carbono - o mínimo necessário para a aprovação bem-sucedida da estratégia - é pelo menos 1 (esse bot e 1 cópia são necessários )
Anônimo
Você está argumentando que se qualifica para uma exceção na cláusula 3 ao tentar enviar algo que se enquadra na cláusula 1: cópias perfeitas de um bot são nunca válidas, sem exceções. E para se qualificar para a exceção da cláusula 3, você precisará provar que sua estratégia exige estritamente que todos esses parceiros reajam a ela, como por exemplo um sinal de handshake, que é realmente inútil sem que alguém ouça. O seu não. O Equalizer fornecerá exames para ativar a cláusula "amigável", negando assim que é necessária uma cópia do seu bot.
AlienAtSystem
Tudo bem então. Vou fazer alguns ajustes finais.
Anónimo
0

Contrário

O Imperador de Jade está sempre certo, por isso implementa a função de pedir do Imperador de Jade como sua própria função de resposta quando precisa de mais de 2 respostas. Para apenas 1 resposta, ela responde true(chances decentes de estar correta) e para 2, responde true,false(essa resposta passa "pelo menos metade" das perguntas, três em cada quatro possíveis questionários, melhor do que escolher aleatoriamente).

Usa lógica semelhante em sua atualização com relação à forma como altera seu padrão de pergunta, mas sua lógica de pergunta é semelhante à do imperador de Jade, apenas com um peso diferente. Flutua entre valores mais altos de truecom valores mais altos de falsequando muitos candidatos obtêm uma pontuação alta o suficiente para passar.

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)
Draco18s não confia mais no SE
fonte
1
Não true, falsefalha se o exame é false, true?
pppery
As primeiras linhas em answertêm erros de sintaxe e nome - truee falsedevem ser Truee False, e ifs estão faltando :s no final #
Sara J
Obrigado a vocês dois; Eu não tinha o Python configurado na minha máquina, porque não o uso com tanta frequência, por isso estrago a sintaxe regularmente.
Draco18s não confia mais em SE
newExam está definido, mas nunca é lido update. passé um comando NOP, você pode excluí-lo. (O comentário por trás disso é apenas um trocadilho com o Drunkard que você copiou.) Além disso, você está usando implicitamente mathe randommodules, mas não declarou que os importou. Eu o reescrevi no meu arquivo de concurso np.copysigne np.random.uniformisso deve fazer a mesma coisa.
AlienAtSystem 22/09/19
@AlienAtSystem Deve ser corrigido agora.
Draco18s não confia mais em SE
0

Marx

Este é o bot de Marx. Ele acredita que, em vez de uma burocracia, deveríamos ter um sistema comunista. Para ajudar a alcançar esse objetivo, oferece testes mais difíceis a bots de classificação mais alta. Também fornece respostas mais aleatórias a questionários de bots mais altos, porque provavelmente são mais inteligentes, porque são mais altos.

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]
sugarfi
fonte
Marx responde atualmente um byte demais, então ele não pode competir agora
AlienAtSystem
O que você quer dizer? Seus exames / respostas são muito longos?
sugarfi
Sua resposta é uma entrada longa demais
AlienAtSystem 10/10/19
OK, eu consertei isso. Deve estar bem agora.
Sugar19
Desculpe, mas você recebeu um feedback errado: agora, as respostas são muito curtas. O verdadeiro problema é que você se estende quando é muito curto (embora não seja suficiente no momento), mas não o reduz quando Marx é rebaixado.
AlienAtSystem