Білий слон


11

Це Різдво в липні, тож який кращий спосіб відсвяткувати, ніж віртуальний обмін подарунками білого слона!

Для цього завдання King of the Hill ви повинні створити бота, який грає в симуляцію білого слона , намагаючись отримати найцінніший подарунок, який він може.

Правила гри

  • Гра буде проводитися протягом багатьох раундів, кожен з яких складається зі змінною кількістю оборотів.
  • Кругла настройка : Буде стільки подарунків, скільки гравців у грі, кожен оцінюється випадковим чином рівномірно в діапазоні [0 ... 1), причому це значення буде невідомим, поки подарунок не "відкриється". Гравці будуть розміщені у випадковому порядку у черзі. Перший гравець вискакує з передньої частини черги.
  • Коли настає черга гравця, вони можуть або відкрити подарунок, або викрасти подарунок іншого гравця, передаючи черга гравцеві, подарунок якого було викрадено.
    • Кожен подарунок може бути вкрадений до 3 разів.
    • Ви не можете вкрасти у гравця, який щойно вкрав у вас.
    • Кожен гравець може мати лише одного присутнього за раз.
  • Після того, як подарунок відкриється, грайте вперед до наступного гравця, що вискочив з передньої частини черги. Це буде наступний гравець по черзі, який ще не мав черги.
  • Круглий кінець : Коли всі подарунки були відкриті, раунд закінчується, і значення подарунка, який тримає кожен гравець, додається до балу цього гравця. Починається новий раунд, кожен гравець тепер не тримає подарунка, і замовлення гравця змінюється.
  • Закінчення гри : Гра закінчиться, коли хоча б один гравець набрав 100 500 балів, при цьому перемога присуджується гравцеві з найбільшою загальною вартістю подарунків.

Кодування

Усі матеріали повинні бути сумісні з Python 3.7. Ви повинні написати клас, який безпосередньо успадковується WhiteElephantBot. Наприклад:

class FooBot(WhiteElephantBot):
    # Your implementation here

Ви можете надати __init__метод (який бере один аргумент name) у вашому ботовому класі, який повинен викликати super().__init__(name). Ваш клас повинен мати take_turnметод, який очікує наступних аргументів у цьому порядку:

  • players: Список імен гравців, у свою чергу, усіх гравців, які ще не мають подарунків.
  • presents: Словник, який відображає імена гравців на 2-кортежі, що містять теперішнє значення, яке утримує цей програвач, і кількість вкрадених присутніх. Це стосуватиметься лише інших гравців, які наразі проводять подарунки.
  • just_stole: Якщо останньою дією було вкрадено, це буде ім'я гравця, який щойно вкрав. Якщо ні, то буде None.

Кожен аргумент буде незмінним або новим об'єктом, так що мутація будь-якого з них не вплине на гру. Ви можете зберегти копію будь-якого з аргументів, якщо цього хочете.

Приклад значення для presents:

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

Ваш take_turnметод повинен повертати ім'я гравця, у якого ви хочете вкрасти або Noneвідкрити подарунок. Якщо це викликає виняток, повертає щось інше , ніж strабо None, або ім'я гравця , якого ви не можете вкрасти з, ви будете відкривати подарунок за замовчуванням.

Ваш конструктор буде викликаний на початку кожного раунду, тому вам не запам’ятати стан від раунду до раунду.

Спадковуючи від WhiteElephantBot, ви отримаєте доступ до steal_targetsметоду, який прийматиме подарунковий just_stoleнабір і повертає список імен гравців, у яких ви можете викрасти.

Будь-які модулі, потрібні вашому сценарію, повинні бути імпортовані вгорі вашої записи.

Тест-драйвер

Тестовий драйвер можна знайти тут . Вам не потрібно включати from white_elephant import WhiteElephantBotу свою опубліковану відповідь, однак для цього потрібно буде місцевий модуль.

Базові конкуренти

  • Випадково : Вибирає випадковим чином, відкривати новий подарунок чи красти, при цьому крадіжка вибирається рівномірно випадково.
  • Жадібний : викрасти найцінніший подарунок, який можна вкрасти. Якщо подарунків неможливо вкрасти, відкрийте подарунок.
  • Приємно : Завжди відкриває новий подарунок. Ніколи не краде.

Додаткові правила

  • Ви несете відповідальність за зловлення всіх винятків. Якщо ваш клас не зможе знайти виняток, він буде дискваліфікований. Крім того, будь ласка, не вловлюйте KeyboardInterrupts.
  • Не використовуйте файли чи інші методи, щоб обійти неможливість збереження стану між іграми. Наприклад, ви не можете зберігати стан нейронної мережі у файл середнього періоду.
  • Ваш бот повинен бути самостійним у коді класу та пов'язаних константах.
  • Ви можете використовувати лише стандартний імпорт бібліотеки.
  • Суворих вимог до виконання не існує. Будьте розумними та розважливими. Якщо продуктивність стає проблемою, я залишаю за собою право додавати часові межі.
  • Один запис на людину. Якщо ви подаєте більше одного запису, ваші боти можуть не працювати разом. Я зараз дозволю кілька записів на людину, хоча я можу позбавити її згодом, якщо це стане проблемою.
  • Це відкритий конкурс без чіткої дати закінчення. Це буде повторене будь-коли, коли я зможу, коли відбулися суттєві зміни.

EDIT1: Змінено виграшний бал від 100 до 500, щоб рейтинги були більш узгодженими. Тестовий драйвер має новий виправлення, а також відображає зміни в оцінці виграшу.

EDIT2: Уточнююча записка про необхідний імпорт.


Табло (станом на 8 серпня 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. Поріг порогу (390.480)
  8. Азартний гравець (313.362)
  9. NiceBot (275.536)
  10. RandomBot (256.172)
  11. GoodSamaritan (136.298)

Чи може бути якась кількість крадіжок поспіль? Коли я граю, зазвичай є обмеження в 2 крадіжки підряд чи щось таке, і третя людина з нього повинна буде відкрити її. Це запобігає крадіжці одного і того ж подарунка не один раз за обіг.
mbomb007

@ mbomb007 Так. Викрадення ланцюгів необмежене, за винятком інших правил, які роблять певні подарунки незахищеними до крадіжки: кожен подарунок може бути вкрадений лише 3 рази, і ви не можете вкрасти у гравця, який щойно вкрав у вас.
Beefster

Чи можете ви вкрасти подарунок, а потім повторно вкрасти оригінал, який у вас був?
Ерік Аутгольфер

@EriktheOutgolfer: так, поки між ними була чергова черга. Ви просто не зможете повторно викрасти одразу після того, як ваш украй подарунок.
Beefster

1
Янкі своп !? Що далі, спільний день народження?
ngm

Відповіді:


3

LastMinuteBot

(Велика подяка @Mnemonic за каркас коду, оскільки я ледве знаю 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

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


Простий, але прекрасний
r_j

2

Од Тодд

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

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


Помилка синтаксису в рядку 11. Вам потрібно ==замість =розуміння списку.
Beefster

виправлено, спасибі! Не використовуйте 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

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


1

ПорігBot

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)

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


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]

Запускає 2000 моделювання, коли кожен гравець жадібно діє і вибирає найкращі дії.


Що саме робить цей бот?
Beefster

@Beefster Запускає 2000 випадкових ігор, коли кожен гравець жадібно діє, і вибирає ходу з найвищим середнім кінцевим балом.
user1502040

Помилка імені. Вам потрібно імпортувати випадковим чином.
Beefster

1

Робін Гуд

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

Вкрасти у багатих, які не заслужили подарунок


У вас помилка відступу.
Beefster

0

GoodSamaritan

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

Дайте нещасним людям ще один шанс на удачу


0

Азартний гравець

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

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


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)

Цей бот не намагається отримати найкращий можливий подарунок, але намагається отримати подарунок, який оцінюється> = (n-3) / n, де n - кількість подарунків. У більшості випадків настільки цінуються подарунки, і Top3Bot намагатиметься взятись за один із них, але йому зовсім не байдуже, хто з тих, хто його отримує.


у вас __init__відсутній selfаргумент
Beefster
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.