Dans ce défi, vous allez jouer au dilemme du prisonnier itéré bruyant.
Le dilemme du prisonnier est un scénario de la théorie des jeux où il y a deux joueurs, chacun avec deux options: coopérer ou faire défaut. Chaque joueur fait mieux pour lui-même s’il fait défaut que s'il coopère, mais les deux joueurs préfèrent le résultat où les deux joueurs coopèrent à celui où les deux joueurs font défaut.
Le dilemme du prisonnier itéré est le même jeu, sauf que vous jouez contre le même adversaire à plusieurs reprises et que vous savez ce que votre adversaire a joué dans le passé. Votre objectif est toujours d'accumuler le score le plus élevé pour vous-même, quel que soit le résultat de vos adversaires.
Le dilemme du prisonnier itéré bruyant introduit du bruit dans la communication. Du bruit sera introduit dans votre connaissance de ce que votre adversaire a joué dans le passé. Vous saurez également ce que vous avez fait dans le passé. Le taux de bruit est constant sur un tour contre le même adversaire, mais différent entre les tours.
Défi
Dans ce défi, vous allez écrire un programme Python 3 pour jouer le dilemme bruyant du prisonnier itéré.
Votre programme recevra trois entrées:
Vos propres mouvements, sans inversion aléatoire appliquée.
Les mouvements de votre adversaire, avec des retournements aléatoires appliqués.
Une variable d'état, qui commence par une liste vide à chaque tour et que vous pouvez modifier si vous le souhaitez. Vous pouvez l'ignorer si vous ne voulez pas l'utiliser.
Votre programme devrait sortir 'c'
coopérer ou 'd'
défecter.
Par exemple, voici un programme qui coopère si l’opposant a coopéré au moins 60% du temps dans le passé, après l’application de lancers aléatoires et pour les 10 premiers lancers:
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'
Si vous ne connaissez pas Python, écrivez votre soumission en pseudocode et une personne (moi-même ou un autre membre du site) peut créer le programme Python correspondant.
Gameplay
Le coureur du tournoi peut être trouvé ici: noisy-game . Courez noisy-game.py
pour lancer le tournoi. Je garderai ce référentiel à jour avec les nouvelles soumissions. Des exemples de programmes peuvent être trouvés dans basic.py
.
Le score global d'un programme est le total de ses scores sur 100 parties du jeu.
Une partie consiste en des confrontations à tour de rôle de chaque joueur contre chaque joueur, y compris le sien. Un match consiste en 100 rounds. Un tour consiste en 300 mouvements, chacun impliquant la sortie 'c'
ou'd'
.
Votre soumission jouera un match contre toutes les soumissions, y compris la vôtre. Chaque match comprendra 100 rounds. Au cours de chaque tour, une probabilité de retournement sera choisie uniformément de manière aléatoire [0, 0.5]
.
Chaque tour consistera en 300 mouvements. À chaque déplacement, les deux programmes recevront toutes les lectures précédentes qu'ils ont tentées et toutes les lectures précédentes que l'autre programme a effectuées après l'application des flips, ainsi qu'une variable d'état, qui est une liste modifiable que le programme peut modifier s'il le souhaite. Les programmes vont sortir leurs mouvements.
Les coups sont marqués comme suit: Si un programme joue un 'c'
, le programme adverse obtient 2 points. Si un programme joue une'd'
, ce programme gagne 1 point.
Ensuite, chaque coup est retourné indépendamment avec une probabilité égale à la probabilité de retournement et stocké pour être montré à l'adversaire.
Une fois que tous les tours ont été joués, nous additionnons le nombre de points que chaque joueur a obtenus lors de chaque match. Ensuite, nous utilisons le système de notation suivant pour calculer le score de chaque joueur pour la partie. Cette notation est effectuée une fois que toutes les confrontations sont terminées.
Notation
Nous allons utiliser la notation évolutive. Chaque programme commence avec un poids égal. Ensuite, les poids sont mis à jour comme suit, pour 100 itérations, en utilisant les totaux de points du jeu:
Le nouveau poids de chaque programme est proportionnel au produit de son poids précédent et de son total de points moyen pondéré par le poids de ses adversaires.
100 mises à jour sont appliquées et les poids finaux sont les scores de chaque programme pour cette phase du jeu.
Les scores globaux seront la somme sur 100 manches du match.
Les joueurs seront tous des réponses valables à ce défi, ainsi que six programmes de base pour nous aider à démarrer.
Mises en garde
Ne modifiez pas les entrées. Ne tentez pas d’affecter l’exécution d’un autre programme, sauf en coopérant ou en échouant. Ne faites pas de soumission sacrificielle qui tente de reconnaître une autre soumission et de bénéficier cet adversaire à ses propres frais. Les échappatoires standard sont interdites.
EDIT: Les soumissions peuvent ne pas dupliquer exactement les programmes de base ou les soumissions antérieures.
Si vous avez des questions, n'hésitez pas à les poser.
Résultats actuels
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
Résultats avec uniquement des réponses à cette question et programmes de base ignorant le jeu de l'adversaire:
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
Gagnant
Le concours restera ouvert indéfiniment, au fur et à mesure que de nouvelles soumissions seront publiées. Cependant, je vais déclarer un gagnant (accepter une réponse) sur la base des résultats 1 mois après la publication de cette question.
exploit_threshold()
plusieurs fois en tant que exploit_threshold1()
, etc. et les ai ajoutés à la players
liste. Pourquoi ai-je des résultats très différents pour des stratégies identiques?