Scambio di elefanti bianchi


11

È Natale a luglio, quindi quale modo migliore per festeggiare di uno scambio di regali di elefante bianco virtuale!

Per questa sfida di King of the Hill, devi creare un robot che gioca in una simulazione di scambio di White Elephant , cercando di ottenere il regalo più prezioso che può.

Regole del gioco

  • Il gioco si svolgerà in molti round, ognuno composto da un numero variabile di turni.
  • Round Setup : ci saranno tanti regali quanti sono i giocatori nel gioco, ognuno valutato casualmente in modo uniforme nell'intervallo [0 ... 1), con questo valore sconosciuto fino a quando il presente non viene "aperto". I giocatori verranno messi in un ordine casuale in una coda. Il primo giocatore verrà espulso dalla parte anteriore della coda.
  • Quando è il turno di un giocatore, possono aprire un regalo o rubare il regalo di un altro giocatore, passando il turno al giocatore il cui regalo è stato rubato.
    • Ogni regalo può essere rubato fino a 3 volte.
    • Non puoi rubare al giocatore che ti ha appena rubato.
    • Ogni giocatore può avere solo un regalo alla volta.
  • Dopo che un regalo è stato aperto, il gioco avanza al giocatore successivo saltato fuori dalla coda. Questo sarà il prossimo giocatore in ordine di turno che non ha ancora avuto un turno.
  • Fine del round : quando tutti i regali sono stati aperti, il round termina e il valore del presente detenuto da ciascun giocatore viene aggiunto al punteggio di quel giocatore. Inizia un nuovo round, con ogni giocatore che ora non ha regali e l'ordine dei giocatori viene mischiato.
  • Fine del gioco : il gioco termina quando almeno un giocatore ha totalizzato 100 500 punti, con la vittoria assegnata al giocatore con il valore totale più alto di regali.

Coding

Tutti gli invii devono essere compatibili con Python 3.7. Devi scrivere una classe che eredita direttamente da WhiteElephantBot. Per esempio:

class FooBot(WhiteElephantBot):
    # Your implementation here

Puoi fornire un __init__metodo (che accetta un argomento name) nella tua classe bot, che deve chiamare super().__init__(name). La tua classe deve avere un take_turnmetodo che prevede i seguenti argomenti in questo ordine:

  • players: L'elenco dei nomi dei giocatori, in ordine di turno, di tutti i giocatori che non hanno ancora regali.
  • presents: Un dizionario che mappa i nomi dei giocatori su 2 tuple contenente il valore attuale detenuto da quel giocatore e il numero di volte che il presente è stato rubato. Ciò includerà solo altri giocatori che sono attualmente in possesso di regali.
  • just_stole: Se l'ultima azione intrapresa è stata un furto, questo sarà il nome del giocatore che ha appena rubato. In caso contrario, lo sarà None.

Ogni argomento sarà immutabile o un nuovo oggetto in modo che la mutazione di uno qualsiasi di essi non abbia alcun effetto sul gioco. Se lo desideri, puoi conservare una copia di qualsiasi argomento.

Un valore di esempio per presents:

{
    'Alice':   (0.35, 0),
    'Bob':     (0.81, 2),
    'Charlie': (0.57, 1)
}

Il tuo take_turnmetodo dovrebbe restituire il nome del giocatore da cui vuoi rubare o Noneaprire un regalo. Se genera un'eccezione, restituisce qualcosa di diverso da un stro None, o il nome di un giocatore da cui non puoi rubare, aprirai un regalo di default.

Il tuo costruttore verrà chiamato all'inizio di ogni round, quindi non riuscirai a ricordare lo stato da round a round.

Ereditando da WhiteElephantBot, avrai accesso a un steal_targetsmetodo che prenderà i regali just_stolee restituirà un elenco di nomi di giocatori dai quali puoi rubare.

Tutti i moduli necessari per lo script devono essere importati nella parte superiore della voce.

Test Driver

Il driver di prova può essere trovato qui . Non è necessario includere from white_elephant import WhiteElephantBotnella risposta pubblicata, tuttavia un modulo locale dovrà farlo.

Concorrenti di base

  • Casuale : sceglie a caso se aprire un nuovo regalo o rubare, con il bersaglio rubato scelto in modo uniforme in modo casuale.
  • Greedy : ruba il regalo più prezioso che può essere rubato. Se non è possibile rubare regali, apri un regalo.
  • Bello : apre sempre un nuovo regalo. Non ruba mai.

Regole aggiuntive

  • È tua responsabilità cogliere tutte le eccezioni. Se la tua classe non riesce a rilevare un'eccezione, verrà squalificata. Inoltre, non intercettare KeyboardInterrupts.
  • Non utilizzare file o altri metodi per bypassare l'impossibilità di salvare lo stato tra i giochi. Ad esempio, non è possibile salvare uno stato di rete neurale in un file a metà corsa.
  • Il bot deve essere autonomo nel codice di classe e nelle costanti correlate.
  • È possibile utilizzare solo importazioni di librerie standard.
  • Non vi sono requisiti di prestazione rigorosi. Sii ragionevole e prudente. Se la prestazione diventa un problema, mi riservo il diritto di aggiungere limiti di tempo.
  • Una voce per persona. Se invii più di una voce, i tuoi robot potrebbero non funzionare insieme. Consentirò più voci per persona per ora, anche se potrei riprogrammarlo in seguito se dovesse diventare un problema.
  • Questa è una competizione aperta senza una data di fine distinta. Sarà rieseguito ogni volta che sono in grado quando ci sono stati cambiamenti significativi.

EDIT1: modificato il punteggio vincente da 100 a 500 in modo che le classifiche siano più coerenti. Il driver di test ha un nuovo bugfix e riflette anche le modifiche del punteggio di vittoria.

EDIT2: chiarimento della nota sulle importazioni richieste.


Classifica (all'8 agosto 2018)

  1. SampleBot (500.093)
  2. LastMinuteBot (486.163)
  3. RobinHood (463.160)
  4. OddTodd (448.825)
  5. GreedyBot (438.520)
  6. SecondPlaceBot (430.598)
  7. ThresholdBot (390.480)
  8. Giocatore d'azzardo (313.362)
  9. NiceBot (275.536)
  10. RandomBot (256.172)
  11. GoodSamaritan (136.298)

Può esserci un numero qualsiasi di furti di fila? Quando ho giocato, di solito c'è un limite di 2 rubate di fila o qualcosa del genere, e la terza persona dovrebbe aprirne una. Questo impedisce che lo stesso regalo venga rubato più di una volta per turno.
mbomb007,

@ mbomb007 Sì. Il furto a catena è illimitato, tranne per le altre regole che rendono certi regali immuni al furto: ogni regalo può essere rubato solo 3 volte e non puoi rubare al giocatore che ti ha appena rubato.
Beefster,

Puoi rubare un regalo e poi ri-rubare quello originale che hai avuto?
Erik the Outgolfer,

@EriktheOutgolfer: sì, purché ci sia stata un'altra svolta nel mezzo. Non puoi semplicemente rubare di nuovo immediatamente dopo che il tuo regalo è stato rubato.
Beefster,

1
Yankee swap !? Qual è il prossimo, una festa di compleanno condivisa?
ngm

Risposte:


3

LastMinuteBot

(Grazie mille a @Mnemonic per lo scheletro del codice, dato che conosco a malapena Python.)

class LastMinuteBot(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):
        targets = self.steal_targets(presents, just_stole)
        if len(targets) <= 1:
            return None

        target = None

        # If most of the presents are already distributed, try to steal an 
        #  un-restealable gift of high value
        if len(presents) > (len(players) + len(presents)) * 0.75:
            at_threshold = [t for t in targets if presents[t][1]==2 and presents[t][0]>=0.8]
            if at_threshold:
                target = max(at_threshold, key=lambda x: presents[x][0])

        # Otherwise, take the best available
        if not target:
            target = max(targets, key=lambda x: presents[x][0])

        return target if presents[target][0] > 0.5 else None

Approfitta del fatto che i regali non possono essere rubati più di tre volte, fai il terzo rubando te stesso se trovi un regalo di alto valore e la maggior parte dei regali sono stati aperti.


Semplice ma bello
r_j

2

Odd Todd

class OddTodd(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):

        targets = self.steal_targets(presents, just_stole)

        # if none to steal, pick present
        if len(targets) <= 1:
            return None

        # steals the best gift that he can, as long as he's the 1st/3rd steal
        targets = [t for t in targets if presents[t][1] % 2 == 0]
        if targets:
            return max(targets, key=lambda x:presents[x][0])

        else:
            return None

Ruba il regalo migliore che può, ma non vuole essere la seconda persona a rubare un regalo, perché se gli viene rubato non può recuperarlo.


Errore di sintassi nella riga 11. È necessaria una comprensione al ==posto di a =nella lista.
Beefster,

risolto, grazie! Non usare molto Python.
brian_t,

1

SecondPlaceBot

class SecondPlaceBot(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):
        targets = self.steal_targets(presents, just_stole)
        if len(targets) <= 1:
            return None

        # If most of the presents are already distributed, take the second best.
        if len(presents) > (len(players) + len(presents)) * 0.8:
            target = sorted(targets, key=lambda x: presents[x][0])[-2]
        # Otherwise, take the best and hope someone steals it later.
        else:
            target = max(targets, key=lambda x: presents[x][0])

        return target if presents[target][0] > 0.5 else None

Tutti combatteranno per il dono più prezioso. Il prossimo miglior regalo è quasi altrettanto buono, ma molto meno probabile che venga rubato.


1

ThresholdBot

import random

class ThresholdBot(WhiteElephantBot):
    def __init__(self, name):
        self.name = name
        # Choose a minimum value to be happy.
        self.goal = 1 - random.random() ** 2

    def take_turn(self, players, presents, just_stole):
        # Find who has a gift that's sufficiently valuable.
        targets = self.steal_targets(presents, just_stole)
        targets = [x for x in targets if presents[x][0] >= self.goal]
        targets = sorted(targets, key=lambda x: presents[x][0])

        if not targets:
            return None

        # Choose a target (biased toward the best gifts).
        weighted = []
        for i, target in enumerate(targets, 1):
            weighted += [target] * i ** 2
        return random.choice(weighted)

Non ci interessa davvero ottenere il miglior regalo, solo qualcosa di abbastanza buono . Finché c'è qualcosa che vale la pena rubare, lo faremo.


1

SampleBot

import random

class SampleBot(WhiteElephantBot):
    def rollout(self, values, counts, just_stole, next_move):
        targets = set()
        move_chosen = False
        for i, (v, n) in enumerate(zip(values, counts)):
            if v and n < 3 and i != just_stole and i != 0:
                targets.add(i)
        for i in range(len(values)):
            if values[i]:
                break
            while True:
                if not targets:
                    break
                if move_chosen:
                    j = max(targets, key=lambda i: values[i])
                    if values[j] < 0.5:
                        break
                else:
                    move_chosen = True
                    if next_move is None:
                        break
                    j = next_move
                values[i] = values[j]
                counts[i] = counts[j] + 1
                values[j] = 0
                counts[j] = 0
                if just_stole is not None and counts[just_stole] < 3:
                    targets.add(just_stole)
                if j in targets:
                    targets.remove(j)
                just_stole = i
                i = j
            values[i] = random.random()
            for player in (just_stole, i):
                if player is not None and values[player] and counts[player] < 3:
                    targets.add(player)
        return values[0]
    def take_turn(self, players, presents, just_stole, n_rollouts=2000):
        names = [self.name] + players + list(presents.keys())
        values = [presents[name][0] if name in presents else None for name in names]
        counts = [presents[name][1] if name in presents else 0 for name in names]
        if just_stole is not None:
            just_stole = names.index(just_stole)
        targets = [None]
        for i, (v, n) in enumerate(zip(values, counts)):
            if v and n < 3 and i != just_stole and i != 0:
                targets.append(i)
        if len(targets) == 1:
            return targets[0]
        scores = [0. for _ in targets]
        n = n_rollouts // len(targets)
        for i, target in enumerate(targets):
            for _ in range(n):
                scores[i] += self.rollout(list(values), list(counts), just_stole, target) / float(n)
        target_index = targets[scores.index(max(scores))]
        if target_index is None:
            return None
        return names[target_index]

Esegue 2000 simulazioni con ogni giocatore che agisce avidamente e sceglie l'azione migliore.


Cosa fa esattamente questo bot?
Beefster,

@Beefster Esegue 2000 partite casuali con ogni giocatore che agisce avidamente e sceglie la mossa con il punteggio finale medio più alto.
user1502040

Errore nel nome. Devi importare in modo casuale.
Beefster,

1

Robin Hood

class RobinHood(WhiteElephantBot):       
    def take_turn(self, players, presents, just_stole):
        #get the possible steal targets
        targets = self.steal_targets(presents, just_stole)
        #who stole his gift?
        targets = [x for x in targets if presents[x][1] > 0]
        #sort by value
        targets = sorted(targets, key=lambda x: presents[x][0])        
        #only steal back if it's worth it        
        targets = [x for x in targets if presents[x][0] > 0.5]

        if len(targets)>0:
           return targets.pop()

Ruba ai ricchi che non hanno guadagnato il loro regalo


Hai un errore di rientro.
Beefster,

0

Buon Samaritano

class GoodSamaritan(WhiteElephantBot):     
    def take_turn(self, players, presents, just_stole):  
        targets = self.steal_targets(presents, just_stole)

         #if only one player has a gift, don't steal it!
        if len(presents)<=1 or len(targets)==0:
             return None
        else:       
             #Steal the worst present  
             return min(targets, key=lambda x: presents[x][0])

Offri alle persone sfortunate un'altra possibilità di fortuna


0

Giocatore

class Gambler(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):        
        #get the possible steal targets
        targets = self.steal_targets(presents, just_stole)        

        #last player 
        if len(players)==0:
            #lets gamble! Try and get the highest score
            return None

        #If you are not last, steal the best gift that can be restolen so maybe you can become the last player
        targets = [t for t in targets if presents[t][1]<2 ]
        if targets:
            return max(targets, key=lambda x: presents[x][0])   

Il giocatore d'azzardo è dipendente, cerca di essere l'ultimo giocatore, quindi gioca un nuovo regalo per battere tutti gli altri giocatori.


0

Top3Bot

class Top3Bot(WhiteElephantBot):
    def __init__(self, name):
        super().__init__(name)
        self.firstturn = True

    def take_turn(self, players, presents, just_stole):
        if self.firstturn:
            num_presents = len(players) + len(presents) + 1
            self.value_limit = (num_presents - 3) / num_presents
            self.firstturn = False

        targets = self.steal_targets(presents, just_stole)

        if players:
            targets += None

        return max(
            targets,
            key=lambda name: self.steal_ranking(name, presents, len(players))
        )


    def steal_ranking(self, name, presents, presents_remaining):
        if name is None:
            return (0, 0)

        present_value = presents[name][0]
        num_steals = presents[name][1]
        if present_value >= self.value_limit:
            if num_steals == 2:
                return (5, present_value)
            elif  num_steals == 0:
                return (4, -presemt_value)
            elif num_steals == 1 and presents_remaining == 0:
                return (3, -present_value)
            else:
                return (-1, present_value)
        else:
            if num_steals < 2:
                return (2, present_value)
            else:
                return (-2, present_value)

Questo bot non cerca di ottenere il miglior regalo possibile, ma cerca di ottenere un regalo valutato> = (n-3) / n, dove n è il numero di regali. Nella maggior parte dei casi, ci saranno regali molto apprezzati e Top3Bot proverà a mettere le mani su uno di questi, ma a lui non importa davvero quale di quelli che ottiene.


ti __init__manca il suo selfargomento
Beefster
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.