La burocrazia celeste KoTH


14

Nella Cina imperiale, i ranghi nella società non erano decisi dalla nascita o dalla ricchezza, ma dalla capacità di una persona di eccellere negli esami imperiali. L'Imperatore di giada, divino sovrano dei Cieli, ha chiesto che tutti i suoi sudditi fossero esaminati per determinare il loro valore, e a chi dare successivamente il mandato divino per governare la Cina.

Regole della burocrazia:

  • La burocrazia divina è composta da ranghi non negativi con valori interi, che iniziano con 0. Ogni membro (bot) della burocrazia appartiene a un rango. Ogni grado può contenere arbitrariamente molti membri, ma non può essere vuoto a meno che tutti i gradi sopra non siano vuoti
  • All'inizio del gioco, tutti i membri hanno il grado 0
  • Ogni turno, ogni membro della burocrazia deve rispondere a un esame. L'esame consiste nell'indovinare correttamente i valori booleani di un elenco. La lunghezza dell'elenco è il numero del rango sopra il membro.
  • Le domande dell'esame sono preparate da un membro casuale del grado sopra. I membri di rango più alto ricevono le loro domande direttamente dal JadeEmperor(vedi sotto)
  • Un membro che ottiene almeno il 50% del proprio esame è idoneo per la promozione. Un membro con un punteggio inferiore al 50% sul proprio esame è idoneo per Demotion.
  • Un membro idoneo per Demotion ha il suo grado ridotto di uno solo se c'è un membro idoneo per la promozione nel grado sottostante per prendere il suo posto.
  • Tutti i membri idonei per la promozione hanno il loro rango aumentato di uno a condizione che non rimanga vuoto.
  • Se non tutti i membri idonei possono essere retrocessi o promossi, la preferenza va a quelli più bassi (per Demotion) resp. punteggio più alto (per promozione). I legami si spezzano casualmente.
  • Il grado di un membro può cambiare al massimo di 1 al massimo per turno.

Le regole del gioco:

  • Ad ogni bot verrà assegnato casualmente un ID all'inizio del gioco, che non cambierà nel corso. L' JadeEmperorha l'ID -1, tutti gli altri hanno ID non negativi consecutivi, iniziando con 0.
  • Tutti i robot competono allo stesso tempo
  • Il gioco dura 100 turni, il punteggio del bot è il suo rango medio posseduto in quel periodo.
  • Il punteggio totale viene acquisito eseguendo 1000 partite e calcolando la media dei risultati.
  • Ogni Bot è una classe Python 3 che implementa le seguenti quattro funzioni:
    • ask(self,n,ID), che effettua un esame restituendo un valore listbooleano di lunghezza n. ID è l'ID del bot che deve indovinare quell'elenco. ask()può essere chiamato più volte durante un singolo round per qualsiasi bot, ma anche per niente.
    • answer(self,n,ID), che è un tentativo di rispondere a un esame restituendo un valore listbooleano di lunghezza n. ID è l'ID del bot che ha ask()generato l'esame. answer()viene chiamato esattamente una volta per round per ogni bot.
    • update(self,rankList,ownExam,otherExams)viene chiamato dopo che il Controller ha eseguito tutte le Pro e le Demozioni. I suoi argomenti sono: un elenco di numeri interi, che elenca tutti i ranghi per ID di tutti i bot; una tupla, composta da due elenchi, prima le domande d'esame, quindi le risposte fornite dal bot (nel caso in cui si fosse dimenticato); quindi un elenco di tuple, similmente composto da coppie esame-risposta, questa volta per tutti gli esami che il bot ha distribuito.
    • __init__(self, ID, n) passa al bot il proprio ID e il numero di bot concorrenti.
  • Le classi possono implementare altre funzioni per uso privato
  • La definizione di ulteriori variabili e il loro utilizzo per archiviare dati sugli esami passati è esplicitamente consentito.
  • I meta-effetti di programmazione sono vietati, vale a dire qualsiasi tentativo di accedere direttamente al codice di altri robot, al codice del controllore, causando eccezioni o simili. Questo è un concorso di strategie per gli esami, non di hacking del codice.
  • I robot che cercano di aiutarsi a vicenda sono esplicitamente ammessi, purché non lo facciano tramite meta-effetti, ma puramente dalle informazioni passate update()
  • Altre lingue sono consentite solo nel caso in cui possano essere facilmente convertite in Python 3.
  • La libreria numpy verrà importata come np. La versione è 1.6.5, nel senso che utilizza la vecchia libreria casuale. Se hai intorpidito 1.7, le vecchie funzioni sono disponibili numpy.random.mtrandper il test. Ricorda di eliminare il mtrand per l'invio.
  • Se un bot provoca un'eccezione durante il runtime, viene squalificato. Qualsiasi bot il cui codice è così offuscato che è impossibile dire se genera un elenco di lunghezza n quando ask()o answer()viene chiamato verrà squalificato anche preventivamente. Un bot che mi obbliga a produrre copie in profondità ottiene -1 sulla partitura.
  • I nomi delle classi devono essere univoci
  • Sono consentiti più robot per persona, ma solo i più recenti bot verranno aggiornati in modo iterativo.
  • Dal momento che sembra esserci un po 'di confusione sulla somiglianza dei bot:
    • Non puoi pubblicare una copia di un altro bot. Questa è l'unica scappatoia standard che si applica davvero a questa sfida.
    • È consentito avere un codice condiviso con altri bot, inclusi i bot di altre persone.
    • Non è consentito inviare un bot che differisce da un altro solo per una banale modifica della strategia (come una modifica del seme per la generazione della domanda) a meno che non si possa dimostrare che il numero di tali bot di copia carbone è il minimo richiesto per il successo attuazione della loro strategia (che di solito saranno due robot per una cooperazione).

Robot di esempio:

Fa JadeEmperorsempre parte del gioco, ma non compete; funge da generatore per gli esami di robot di alto livello. I suoi esami sono casuali, ma non in modo uniforme, per consentire ai robot intelligenti di avanzare.

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]

Il Drunkard produce esami e risposte in modo completamente casuale. Farà parte del gioco.

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

Il plagio copia solo gli esami precedenti. Farà anche parte del gioco.

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]

Codice controller disponibile qui . Per i test, puoi inserire la tua classe in un file Contestants.py nella stessa cartella e verranno importati.

Chatroom può essere trovato qui .

Gli esami iniziano!

Punteggio attuale, con maggiore precisione (10000 corse) per Oct20:

concorrenteAutorePuntoAlfaSleafar9.669691GammaSleafar9.301362BetaSleafar9.164597WiQeLuViola P7.870821StudiousBotDignissimus - Spammy7.538537SantayanaSara J7.095528plagiarist6.522047CountOracularIFcoltransG5.881175TommasoAlien @ Sistema5.880041ContrarioDraco18s5.529652Marxsugarfi5.433808Ubriacone5.328178Yin YangViola P5.102519EqualizzatoreMnemonico4.820996TitForTatAnonimo3,35,801 mila

I concorsi verranno organizzati con ogni nuova voce per il prossimo futuro.


1
Le copie dei robot sono una scappatoia standard, quindi no. Se si tenta di abusare di più bot per regola autore inviando copie quasi ma non del tutto, lo rimuoverò.
AlienAtSystem

1
@AlienAtSystem Perché permetti ai robot di aiutarsi a vicenda? Sembra solo più caos e casualità da affrontare.
Don

2
Perché gli argomenti del costruttore sono ID, ngli altri argomenti del metodo n, ID?
Purple P,

1
@DonThousand perché credo che, sotto i vincoli indicati, è piuttosto un'impresa realizzare due robot che A) stringe la mano con successo (nota che il Plagiarizer potrebbe accidentalmente giocare l'uomo nel mezzo) e B) quindi attuare una strategia che aiuti in modo affidabile quel bot ma nessun altro a salire.
AlienAtSystem

1
I ranghi di @soneone contano verso l'alto. Inizi da 0 e ti
avvicini

Risposte:


4

Santayana

Coloro che non ricordano il passato sono condannati a ripeterlo. Quindi prendiamo le nostre decisioni in base al modo in cui gli altri hanno agito in passato, rispondendo in base alla risposta che il richiedente si è generalmente aspettato da noi in un determinato indice e chiedendo la risposta che ci hanno dato il meno spesso in un determinato indice .

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

Studious Bot

Questo bot studia per i test! Tenta di trovare schemi nei test forniti da vari robot e agisce in modo concorde.

Da parte mia, finora, questo bot supera di gran lunga tutti gli altri robot che potrei far funzionare sul mio computer ad eccezione di Alpha, Beta e Gamma (che sono stati programmati per lavorare insieme). Il bot non sfrutta il fatto che il teaming è consentito perché ho pensato che fosse un po 'come imbrogliare e leggermente sporco. Guardandoci però, il team sembra essere abbastanza efficace.

Il bot tenta di riconoscere quando le risposte ai test sono casuali e in risposta corrisponde a quella che speriamo in media del 50% sui test.

Il bot tenta anche di riconoscere quando un bot ha semplicemente capovolto le sue risposte per scartare altri robot che stanno tentando di prevedere il loro comportamento, tuttavia non l'ho ancora programmato per agire specificamente su questo.

Ho annotato il codice con alcuni commenti per facilitarne la lettura

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]

A giudicare dalle nostre prestazioni, hai il miglior algoritmo per rispondere e Wi Qe Lu ha il miglior algoritmo per chiedere. Propongo di unire i nostri robot in un unico bot, chiamato Xuézhě (cinese per "studioso"), che per coincidenza suona un po 'come "switcher".
Viola P

L'ho hackerato e ho eseguito gli esami sulla mia macchina. Curiosamente, ha superato Studious Bot, ma non Wi Qe Lu.
Viola P

@PurpleP Haha! Sembra molto interessante, non credo che ci sia abbastanza tempo per migliorare il mio bot, ma puoi pubblicarlo come presentazione qui
Dignissimus - Spammy,

3

Conte Oracolare

Questo bot utilizza un algoritmo che calcola la media degli esami di tutti gli altri robot funzionanti, (dato il numero circolare e alcune terribili euristiche) per decidere quale altro bot imposterà come esame.
Il Conte chiede i suoi esami usando un hash md5. Sia le sue domande che le sue risposte sono quindi deterministiche. Ignora la maggior parte degli input, chiedendo e rispondendo esattamente alle stesse sequenze di booleani, pioggia o sole, anche contro 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

Una grande idea in teoria, ma nel suo primo concorso il Conte Oracolare è andato peggio di YinYang, nonostante i suoi sforzi per simulare YinYang.
Viola P

1
@PurpleP Sì, non è molto buono. Il motivo è che cerca di scegliere una strategia 'generalmente ottimale' calcolando la media di tutte le strategie specifiche insieme. Ad esempio, non usa una strategia su misura per battere YinYang quando incontra YinYang. Non usa nemmeno una strategia specifica su Jade Emporer: aggiunge semplicemente la strategia di Jade Emporer alla media. Sarà meglio che casuale, ma non di molto.
IFcoltransG

Marx è stato risolto. È necessario aggiornare Count Oracular per prevederlo.
Purple P,

@PurpleP Marx dovrebbe essere supportato ora. È come se fosse di nuovo il 1917.
IFcoltransG

2

Yin Yang

Risposte o tutte Trueo tutte False, ad eccezione di un indice scelto casualmente per essere l'opposto. Chiede il contrario di ciò che risponde. Scambia in modo casuale per respingere gli avversari.

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)

Risponde e chiede a caso al primo turno. Successivamente, utilizza le risposte dell'esame precedente e cambia una domanda se un numero superiore alla media di concorrenti ha ottenuto la risposta corretta.

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
Secondo Google Translate "wi qe lu" è approssimativamente tradotto come "I am penguin road".
Purple P,

2

Un mio bot:

Tommaso

Un viaggiatore proveniente da una terra lontana, ha alcune idee pericolose sui risultati passati indicativi di prestazioni future. Usa quelli per tenere bassi gli altri robot, a meno che ciò non blocchi il suo stesso avanzamento.

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

Hai dimenticato di indentare il tuo codice dopo l'istruzione class?
pepery il

Questa è solo la formattazione SE che mi coglie di sorpresa. Lo risolverò insieme a qualsiasi cosa abbia causato un errore nel test di qualcuno quando si utilizza questo bot
AlienAtSystem

2

Alfa

Leggi la chat prima di effettuare il downgrade. Questi robot non violano alcuna regola. L'OP incoraggia persino i robot cooperanti.

Alpha sta formando una squadra insieme a Beta. Entrambi utilizzano un set predefinito di esami per aiutarsi a vicenda a salire in classifica. Inoltre, entrambi stanno approfittando dei robot che usano ripetutamente gli stessi esami.

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]

Credo che questi tre robot violino le regole dei PO come indicato sia nel prompt che nei commenti.
Don mille,

@DonThousand Se leggi la discussione nella chat, vedrai che non violano le regole. chat.stackexchange.com/rooms/98905/imperial-exams-office
Sleafar

Giusto. Colpa mia.
Don mille,

@DonThousand Allora, qual era il punto nel sottovalutarli tutti?
Sleafar,

Ho solo votato verso il basso su Alpha. Tuttavia, non posso sottovalutare. Apporta una modifica superflua e la riparerò.
Don mille,

1

Equalizzatore

Tutti dovrebbero essere uguali (senza nessuna di queste sciocche sciocchezze da imperatore), quindi fornire la massima mobilità sociale possibile. Rendi le domande davvero facili (la risposta è sempre vera) in modo che le persone possano avere successo.

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

Beta

Leggi la chat prima di effettuare il downgrade. Questi robot non violano alcuna regola. L'OP incoraggia persino i robot cooperanti.

Beta sta formando una squadra insieme ad Alpha. Entrambi utilizzano un set predefinito di esami per aiutarsi a vicenda a salire in classifica. Inoltre, entrambi stanno approfittando dei robot che usano ripetutamente gli stessi esami.

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

Gamma

Leggi la chat prima di effettuare il downgrade. Questi robot non violano alcuna regola. L'OP incoraggia persino i robot cooperanti.

Gamma ha scoperto i piani di Alpha e Beta e sta cercando di trarre vantaggio da entrambi mascherandosi da uno di essi.

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

Ti pone domande facili se hai fatto domande semplici in passato. Se non gli hai mai dato un esame, per impostazione predefinita sono domande facili.

Inoltre, non si fida di nessuno che pone domande difficili e darà loro risposte imprevedibili.

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

Questo bot funziona bene se altri robot collaborano con esso. Attualmente collabora solo Equalizer, ma si spera che ciò sia sufficiente.


Al momento, il bot non può competere perché non segue le specifiche. Assicurarsi che restituisca listoggetti in ogni momento. Inoltre, in base a regole sia vecchie che aggiornate, le copie perfette di un bot non sono invii validi, quindi il numero consentito di istanze di questo bot in esecuzione è 1.
AlienAtSystem

L'ho modificato per tornare alle liste. Per quanto riguarda la cosa delle copie perfette, non esiste un bot attuale che coopera adeguatamente con esso, quindi il numero di bot per le copie carbone - il minimo richiesto per la corretta attuazione della strategia - è almeno 1 (questo bot e 1 copia sono necessari ).
Anonimo

Stai sostenendo che ti qualifichi per un'eccezione ai sensi della clausola 3 mentre cerchi di inviare qualcosa che rientra nella clausola 1: le copie perfette di un bot non sono mai valide, senza eccezioni. E per qualificarsi per l'eccezione della clausola 3, dovresti dimostrare che la tua strategia richiede rigorosamente a tutti questi partner di reagire ad essa, come ad esempio un segnale di stretta di mano, che è davvero inutile senza che qualcuno ascolti. Il tuo no. Equalizer ti consegnerà gli esami per attivare la clausola "amichevole", confutando così che è necessaria una copia del tuo bot.
AlienAtSystem

Bene allora. Apporterò alcune modifiche finali.
Anonimo

0

Contrario

L'Imperatore di Giada ha sempre ragione, quindi implementa la funzione di richiesta dell'Imperatore di Giada come propria funzione di risposta quando ha bisogno di più di 2 risposte. Solo per 1 risposta risponde true(discrete probabilità di essere corretti) e per 2 risponde true,false(questa risposta supera "almeno la metà" delle domande tre su quattro possibili quiz, meglio che scegliere a caso).

Usa una logica simile nel suo aggiornamento per quanto riguarda il modo in cui altera il suo modello di domanda, ma la sua logica di domanda è simile a quella dell'Imperatore di giada, solo con un peso diverso. Fluttua tra valori più alti di truecon valori più alti di falsequando troppi candidati ottengono un punteggio abbastanza alto da superare.

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
Non true, falsefallisce se l'esame è false, true?
pepery

Le prime poche righe answerhanno sintassi ed errori di nome - truee falsedovrebbero essere Truee False, e alla fine ifmancano :s alla fine
Sara J

Grazie a voi due; Python non era configurato sul mio computer in quanto non lo uso così spesso, quindi confondo regolarmente la sintassi.
Draco18s non si fida più di SE

newExam è impostato ma non è mai stato letto update. passè un comando NOP, è possibile eliminarlo. (Il commento dietro di esso è solo un gioco di parole per l'ubriacone è stato copiato sopra.) Inoltre, si sta implicitamente utilizzando mathe randommoduli, ma non ha dichiarato li importato. L'ho riscritto nel mio file contest np.copysigne np.random.uniformquesto dovrebbe fare la stessa cosa.
AlienAtSystem

@AlienAtSystem Ora dovrebbe essere risolto.
Draco18s non si fida più di SE

0

Marx

Questo è il bot Marx. Crede che, invece di una burocrazia, dovremmo avere un sistema comunista. Per aiutare a raggiungere questo obiettivo, fornisce quiz più difficili ai robot di rango più elevato. Fornisce anche risposte più casuali ai quiz provenienti da bot più alti, perché probabilmente sono più intelligenti, perché sono più in alto.

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]

Marx attualmente risponde a un byte di troppo, quindi non può competere in questo momento
AlienAtSystem

Cosa intendi? I suoi esami / risposte sono troppo lunghi?
Sugarfi

La sua risposta è una voce troppo lunga
AlienAtSystem,

OK, l'ho risolto. Ora dovrebbe andare bene.
Sugarfi,

Scusa, ti ho dato un feedback sbagliato: ora le risposte sono troppo brevi. Il vero problema è che ti estendi, cioè quando è troppo corto (anche se non abbastanza in questo momento), ma non tagliarlo quando Marx viene retrocesso.
AlienAtSystem l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.