Шумна ітерована дилема в'язня


35

У цьому виклику ви зіграєте голосну ітераційну дилему в'язня.

У Дилема укладеного це сценарій в теорії ігор , де є два гравці, кожен з двома варіантами: співпрацювати або дефект. Кожен гравець робить краще для себе, якщо вони перемогли, ніж якщо вони співпрацюють, але обидва гравці вважають за краще результат, коли обидва гравці співпрацюють з тим, де обидва гравці перемагають.

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

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

Виклик

У цьому виклику ви напишете програму Python 3, щоб розіграти шумну ітераційну дилему в'язня.

Ваша програма отримає три входи:

  • Ваші власні рухи, без випадкових переворотів.

  • Рух опонента з накладеними випадковими перекидами.

  • Змінна стану, яка починається як порожній список кожного раунду, і яку ви можете змінити, якщо хочете. Ви можете проігнорувати це, якщо не хочете ним користуватися.

Ваша програма повинна виходити 'c'на співпрацю або 'd'на дефекти.

Наприклад, ось програма, яка співпрацює, якщо опонент співпрацював щонайменше 60% часу в минулому, після того, як були застосовані випадкові повороти, і для перших 10 обертів:

def threshold(my_plays, their_flipped_plays, state):
    if len(their_flipped_plays) < 10:
        return 'c'
    opp_c_freq = their_flipped_plays.count('c')/len(their_flipped_plays)
    if opp_c_freq > 0.6:
        return 'c'
    else:
        return 'd'

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

Ігровий процес

Турнір-турнір можна знайти тут: шумна гра . Біжи, noisy-game.pyщоб запустити турнір. Я буду постійно оновлювати це сховище новими поданнями. Приклади програм можна знайти в basic.py.

Загальний бал програми - це загальна його оцінка понад 100 ігор гри.

Гра складається з круглобільних поєдинків кожного гравця проти кожного гравця, включаючи самого себе. Матч складається з 100 раундів. Круглий складається з 300 кроків, кожен з яких включає в себе висновок 'c'або 'd'.

Ваше представлення відтворює збіг проти кожного подання, включаючи ваше власне. Кожен матч складається з 100 раундів. Під час кожного раунду вірогідність відкидання вибиратиметься рівномірно випадково [0, 0.5].

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

Рухи оцінюються наступним чином: Якщо програма грає в 'c', протилежна програма отримує 2 бали. Якщо програма грає 'd', програма отримує 1 бал.

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

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

Оцінка балів

Ми будемо використовувати еволюційне підрахунок. Кожна програма починається з однакової ваги. Потім ваги оновлюються наступним чином для 100 ітерацій, використовуючи підсумки балів від гри:

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

Застосовується 100 таких оновлень, а підсумкові ваги - це оцінка кожної програми за цей процес гри.

Загальний бал становитиме суму, що перевищує 100 запусків гри.

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

Коваджі

Не змінюйте входи. Не намагайтеся вплинути на виконання будь-якої іншої програми, за винятком співпраці чи дефектів. Не робіть жертвоприношення, яке намагається визнати інше підпорядкування та виграти цього опонента за власні кошти. Стандартні лазівки заборонені.

EDIT: Подання можуть не точно копіювати жодну з базових програм або будь-яку попередню подачу.

Якщо у вас є якісь питання, не соромтеся задавати питання.

Поточні результати

nicht_genug: 40.6311
stealer: 37.1416
enough: 14.4443
wait_for_50: 6.947
threshold: 0.406784
buckets: 0.202875
change_of_heart: 0.0996783
exploit_threshold: 0.0670485
kickback: 0.0313357
tit_for_stat: 0.0141368
decaying_memory: 0.00907645
tit_for_whoops: 0.00211803
slider: 0.00167053
trickster: 0.000654875
sounder: 0.000427348
tit_for_tat: 9.12471e-05
stubborn_stumbler: 6.92879e-05
tit_for_time: 2.82541e-05
jedi2sith: 2.0768e-05
cooperate: 1.86291e-05
everyThree: 1.04843e-05
somewhat_naive: 4.46701e-06
just_noise: 1.41564e-06
growing_distrust: 5.32521e-08
goldfish: 4.28982e-09
vengeful: 2.74267e-09
defect: 3.71295e-10
alternate: 2.09372e-20
random_player: 6.74361e-21

Результати, лише відповіді на це питання та основні програми, які ігнорують гру суперника:

nicht_genug: 39.3907
stealer: 33.7864
enough: 20.9032
wait_for_50: 5.60007
buckets: 0.174457
kickback: 0.0686975
change_of_heart: 0.027396
tit_for_stat: 0.024522
decaying_memory: 0.0193272
tit_for_whoops: 0.00284842
slider: 0.00153227
sounder: 0.000472289
trickster: 0.000297515
stubborn_stumbler: 3.76073e-05
cooperate: 3.46865e-05
tit_for_time: 2.42263e-05
everyThree: 2.06095e-05
jedi2sith: 1.62591e-05
somewhat_naive: 4.20785e-06
just_noise: 1.18372e-06
growing_distrust: 6.17619e-08
vengeful: 3.61213e-09
goldfish: 3.5746e-09
defect: 4.92581e-10
alternate: 6.96497e-20
random_player: 1.49879e-20

Перемога

Конкурс залишатиметься відкритим нескінченно, оскільки публікуються нові матеріали. Однак я оголошу переможця (прийму відповідь) за результатами через 1 місяць після опублікування цього питання.


Як tit_for_whoops ігнорує гру суперника?
LyricLy

@LyricLy Я припускаю, що категорія стосується основних програм, які надає Ісаак, які ігнорують своїх супротивників.
FryAmTheEggman

1
Я правильно розумію, що ви можете використовувати змінну стану для запису всіх ваших кроків під час подання, і тому знаєте як свої справжні ходи, так і перевернуті, і оцінювати ймовірність перевертання?
xnor

1
@xnor Вам завжди повідомляються справжні кроки. Повернутись можуть лише ходи супротивників.
Мнемонічна

1
@isaacg Я exploit_threshold()кілька разів намагався копіювати як exploit_threshold1()і т. д. і додав їх до playersсписку. Чому я отримую сильно різні результати за однакові стратегії?
ngn

Відповіді:


4

Genug ist nicht genug

(можна також назвати enough2або stealback)

def nicht_genug(m,t,s):
    if not s:
        s.append("c")
        return "c"
    if s[0]=="t":
        return "d"
    if m[-42:].count("d")>10 or len(t)+t.count("d")>300:
        s[0]="t"
        return "d"
    if t[-1]=="d":
        if s[0]=="d":
            s[0]="c"
            return "d"
        else:
            s[0]="d"
            return "c"
    else:
        if t[-3:].count("d")==0:
            s[0]="c"
        return "c"

Я дізнався, що оригінальна синиця для двох татів чекала двох послідовних татів, як tit_for_whoopsце робиться, і справді, здається, ми повинні пробачити і забути (ну, майже ...) раніше одинакові тати. І в останніх турах чимало гравців дефектують. Я все ще вважаю за краще бути приємним, коли досі все було добре, але бар толерантності бота все менше знижується.


11

Тит-за-Уопс

Натхненний стратегією від ncase.me/trust

def tit_for_whoops(m, t, s):
    if len(t) < 2:
        return 'c'
    else:
        return 'd' if all([x == 'd' for x in t[-2:]]) else 'c'

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


Дякуємо за ваше подання! Майте на увазі, що оскільки вірогідність відкидання в середньому становить 1/4, то відбувається подвійне перевертання кожні 16 рухів або близько того.
isaacg

Я додав змінну стану, яку ви можете ігнорувати, якщо не хочете її використовувати.
isaacg

9

Зміна серця

def change_of_heart(m, t, s):
    return 'c' if len(t) < 180 else 'd'

Має зміна серця наскрізь. На диво добре


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

9

Straa Stealer

Натхненний достатньою, change_of_heart та tit-for-whoops. Слід трохи прощати. Я намагався підробити цифри для найкращих результатів, але вони не хотіли багато змінювати.

def stealer(mine, theirs, state):
    if len(mine) == 0:
        state.append('c')
        return 'c'
    elif len(mine) > 250:
        return "d"
    elif state[0] == 't':
        return 'd'
    elif mine[-40:].count('d') > 10:
        state[0] = 't'
        return 'd'
    elif theirs[-1] == 'd':
        if state[0] == 'd':
            state[0] = 'c'
            return 'd'
        else:
            state[0] = 'd'
            return 'c'
    elif all([x == 'c' for x in theirs[-3:]]):
        state[0] = 'c'
        return 'c'
    else:
        return 'c'

Ласкаво просимо до PPCG!
Джузеппе

Вітаємо вас з головою!
isaacg

8

Тит-на-час

def tit_for_time(mine, theirs, state):
    theirs = theirs[-30:]
    no_rounds = len(theirs)
    return "c" if no_rounds < 5 or random.random() > theirs.count("d") / no_rounds else "d"

Якщо ви витрачаєте більшу частину часу, боліючи за мене, я просто зашкоджую вам спину. Мабуть.


Приємного подання! Ви зараз на 1-му місці, не знаючи опонентів основних програм.
isaacg

7

Зростає недовіра

import random

def growing_distrust(mine, theirs, state):
    # Start with trust.
    if len(mine) == 0:
        state.append(dict(betrayals=0, trust=True))
        return 'c'

    state_info = state[0]

    # If we're trusting and we get betrayed, trust less.
    if state_info['trust'] and theirs[-1] == 'd':
        state_info['trust'] = False
        state_info['betrayals'] += 1

    # Forgive, but don't forget.
    if random.random() < 0.5 ** state_info['betrayals']:
        state_info['trust'] = True

    return 'c' if state_info['trust'] else 'd'

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


Так, ні про яку державну річ не прикро, але я хотів, щоб матеріали були рівномірними, тому це найкраще, що я міг придумати. Чи маєте ви ідею, як додати стан?
isaacg

Просто є stateаргумент, що за замовчуванням - це список? Списки змінні, тому стан можна легко змінити.
LyricLy

Як так? Я не бачу, як це могло бути.
LyricLy

@Mnemonic Я думаю, що я знаю, як це здійснити. Я дам йому кружляння.
isaacg

Я додав змінну стану, яка по суті є порожнім списком, і яку ви можете змінити.
isaacg

7

Jedi2Sith

Починає все приємно і самовіддано, але з часом вплив темної сторони неухильно посилюється, аж до точки не повернення. Це не зупиняє цього впливу, але всі погані речі, які він бачить, лише сприяють потужності темної сторони ...

def jedi2sith(me, them, the_force):
  time=len(them)
  bad_things=them.count('d')
  dark_side=(time+bad_things)/300
  if dark_side>random.random():
    return 'd'
  else:
    return 'c'

Спробуйте в Інтернеті!


6

Повзунок

def slider(m, t, s):
    z = [[2, 1], [0, 1], [2, 3], [2, 1]]
    x = 0
    for y in t:
      x = z[x][y == 'c']
    return 'c' if x < 2 else 'd'

Починається з «с», і поступово ковзає в бік «d» або від нього.


Я зробив функціонально еквівалентний перепис цього для використання змінної стану, тому що він працював досить повільно. Однак вам нічого не потрібно змінювати.
isaacg

6

Впертий заїкач

def stubborn_stumbler(m, t, s):
    if not t:
        s.append(dict(last_2=[], last_3=[]))
    if len(t) < 5:
        return 'c'
    else:
        # Records history to state depending if the last two and three
        # plays were equal
        s = s[0]
        if t[-2:].count(t[-1]) == 2:
            s['last_2'].append(t[-1])
        if t[-3:].count(t[-1]) == 3:
            s['last_3'].append(t[-1])
    c_freq = t.count('c')/len(t)
    # Checks if you've consistently defected against me
    opp_def_3 = s['last_3'].count('d') > s['last_3'].count('c')
    opp_def_2 = s['last_2'].count('d') > s['last_2'].count('c')
    # dist func from 0 to 1
    dist = lambda x: 1/(1+math.exp(-5*(x-0.5)))
    # You've wronged me too much
    if opp_def_3 and opp_def_2:
        return 'd'
    # Otherwise, if you're consistently co-operating, co-operate more
    # the less naive you are
    else:
        return 'c' if random.random() > dist(c_freq) - 0.5 else 'd'

Виходячи з вашої стратегії використання порогу використання лише послідовних відслідковуваних відтворень для переключення між дефектами та здебільшого співпрацювати

ОНОВЛЕННЯ: Відслідковує як дві послідовні, так і три послідовні п'єси, караючи лише за жорстких умов та додаючи випадковий вибір, коли не впевнений

ОНОВЛЕННЯ 2: Видалено стан та додану функцію розподілу


Домовленості щодо написання першої програми, щоб взяти на себе ініціативу!
isaacg

6

Шум боту

def just_noise(m,t,s):
    return 'c' if random.random() > .2 else 'd'

Я напевно співпрацюю ботом. Це просто шум.


6

Гарненького потроху

def enough(m,t,s):
    if not s:
        s.append("c")
        return "c"
    if s[0]=="t":
        return "d"
    if m[-42:].count("d")>10:
        s[0]="t"
        return "d"
    if t[-1]=="d":
        if s[0]=="d":
            s[0]="c"
            return "d"
        else:
            s[0]="d"
            return "c"
    else:
        return "c"

Починається як синиця для двох татів, де два тати не повинні бути послідовними (на відміну від них tit_for_whoops). Якщо доводиться грати dзанадто часто, це переходить до dзагального.


Вітаємо, що взяли на себе головну роль!
isaacg

6

Золота рибка

def goldfish(m,t,s):
    return 'd' if 'd' in t[-3:] else 'c'

Золота рибка ніколи не прощає, але швидко забуває.


6

хитрість (знову відновлено)

Лише останні 10 п’єс враховуються, але поділяються на два блоки з п’яти, які в середньому по кожній класифікуються як добрі чи погані.

Якщо опонент грає в середньому "приємно", то хитрість грає все менше і менш приємно. Якщо результати неоднозначні, хитромудрому приємно заманювати опонента в безпеку. Якщо супротивник, здається, грає "погано", хитрість помститься.

Ідея полягає в тому, щоб час від часу збирати очки у наївних гравців, а ловлячи оманливих людей рано.

import random
def trickster(player,opponent,state):
    pBad = 0.75
    pNice = 0.8
    pReallyBad =0.1
    decay = 0.98
    r = random.random()
    if len(player)<20: #start off nice
        return 'c' 
    else: #now the trickery begins
        last5 = opponent[-5:].count('c')/5.0 > 0.5
        last5old = opponent[-10:-5].count('c')/5.0  > 0.5
        if last5 and last5old: #she is naive, punish her
            pBad = pBad*decay #Increase punishment
            if r<pBad:
                return 'c'
            else:
                return 'd'
        elif last5 ^ last5old: #she is changing her mind, be nice!
            if r<pNice:
                return 'c'
            else:
                return 'd'
        else: #she's ratting you out, retaliate
            pReallyBad = pReallyBad*decay #Retaliate harder
            if r<pReallyBad:
                return 'c'
            else:
                return 'd'

Відмова: Я ніколи раніше не публікував тут, якщо я роблю щось не так> будь ласка, скажіть, і я виправлюсь.


Ласкаво просимо на сайт! На жаль, ваш код наразі не працює. У вас є еліф за іншим. Ви могли б це виправити? Спасибі
isaacg

Я здогадуюсь, що все, починаючи з еліфу, слід відступити ще раз?
isaacg

Правильно, я відступ.
Гектор-Ваартгард

@isaacg Я оновив свою відповідь новим кодом. Я не маю достатньо репутації, щоб сказати це вам у питанні-коментарях. Я не впевнений, що я правильно використовую змінну стану, я припускаю, що це порожній список, до якого я можу додати все, що хочу, правильно?
Гектор-Ваартгард

2
Це не спрацює. Після кожного ходу вирішується, чи буде поточний хід відвернутим чи ні (незалежно для двох гравців). Це рішення потім фіксується. Ви завжди побачите той самий перший хід, який може бути перевернутий чи ні, але він не зміниться.
Крістіан Сіверс

5

Занепадаюча пам'ять

def decaying_memory(me, them, state):
    m = 0.95
    lt = len(them)

    if not lt:
        state.append(0.0)
        return 'c'

    # If it's the last round, there is no reason not to defect
    if lt >= 299: return 'd'

    state[0] = state[0] * m + (1.0 if them[-1] == 'c' else -1.0)

    # Use a gaussian distribution to reduce variance when opponent is more consistent
    return 'c' if lt < 5 or random.gauss(0, 0.4) < state[0] / ((1-m**lt)/(1-m)) else 'd'

Більше важить недавня історія. Повільно забуває минуле.


5

Відбій

def kickback(m, t, s):
  if len(m) < 10:
    return "c"
  td = t.count("d")
  md = m.count("d")
  f = td/(len(t)+1)
  if f < 0.3:
    return "d" if td > md and random.random() < 0.1 else "c"
  return "c" if random.random() > f+2*f*f else "d"

Деякі невиразні ідеї ...


Вітаємо, що зайняли лідируючу роль у версії, де адаптовані основні заклинання зняті.
isaacg

Спасибі. Я думаю, що дивно, наскільки два результати відрізняються!
Крістіан Сіверс

4

Дійсно не отримати цілу "шумну" річ

def vengeful(m,t,s):
    return 'd' if 'd' in t else 'c'

Ніколи не прощає зрадника.


4

ехолот:

редагувати: додано відплату за певних сценаріїв із низьким рівнем шуму

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

якщо наш опонент робить багато дефектів у цих поворотах (2 і більше), ми просто відхиляємо їх назад. якби це був лише шум, шум би все одно вплинув на наші кроки.

в іншому випадку, якщо лише 1 хід був дефектом, ми просто робимо просту синицю для решти гри.

def sounder(my, their, state):
    if len(my)<4:
        if their.count("d")>1:
            return "d"
        return "c"
    elif len(my) == 4:
        if all(i == "c" for i in their):
            state.append(0)
            return "d"
        elif their.count("c") == 3:
            state.append(1)
            return "c"
        else:
            state.append(2)
    if state[0] == 2:
        return "d"
    if state[0] == 0:
        if not "d" in my[-4:]:
            return "d"
        return their[-1]
    else:
        return their[-1]

3

Чергуйте

def alternate(m, t, s):
    if(len(m)==0):
        return 'c' if random.random()>.5 else 'd'
    elif(len(m)>290):
        return 'd'
    else:
        return 'd' if m[-1]=='c' else 'c'

Вибирається випадковим чином у першому раунді, потім чергується. Завжди дефекти за останні 10 раундів.


3

Зачекайте 50

def wait_for_50(m, t, s):
  return 'c' if t.count('d') < 50 else 'd'

Після 50 дефектів, нехай це буде!


Я виправив ваш пітон, зберігаючи ваші наміри.
isaacg

Вітаємо з переходом на 3 місце.
isaacg

2

Деякі наївні

def somewhat_naive(m, t, s):
    p_flip = 0.25
    n = 10
    if len(t) < n:
        return 'c' if random.random() > p_flip else 'd'
    d_freq = t[-n:].count('d')/n
    return 'c' if d_freq < p_flip else 'd'

Я просто припускаю, що якщо ви перемогли менше, ніж вірогідність перевертання (приблизно) в останні n оборотів, це був шум, і не те, що ви маєте на увазі!

Не знайшов найкращого росіянина , можливо, це вивчить далі.


2

Кожні три

def everyThree(me,him,s):
    if len(me) % 3 == 2:
        return "d"
    if len(me) > 250:
        return "d"
    if him[-5:].count("d")>3:
        return "d"
    else:
        return "c"

Дефекти кожні три витки незалежно. Також дефекти останніх 50 витків. Також дефекти, якщо його опонент переміг 4 з 5 останніх раундів.


2

Відра

def buckets(m, t, s):
    if len(m) <= 5:
        return 'c'
    if len(m) >= 250:
        return 'd'
    d_pct = t[-20:].count('d')/len(t[-20:])
    if random.random() > (2 * d_pct - 0.5):
        return 'c'
    else:
        return 'd'

Грає приємно для початку. Дивиться на останні 20, якщо <25% d повертає c,> 75% d, повертає d, а між ними вибирає випадковим чином лінійну функцію ймовірності. Останні 50, дефекти. Це було нарешті 10, але було помічено багато останніх 50 дефектів.

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


Якщо ви хочете перевірити речі локально, ви можете клонувати сховище та запустити noisy-game.py. Це займе деякий час, тому ви, можливо, захочете видалити деяких опонентів playersдля швидких ітерацій.
isaacg

Спасибі Ісаак - мені доведеться пограти з цим і попрацювати.
brian_t

1

Tit-For-Stat

Дефекти, якщо опонент зазнав поразки більше половини часу.

def tit_for_stat(m, t, s):
  if t.count('d') * 2 > len(m):
    return 'd'
  else:
    return 'c'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.