In dieser Frage wurde ein Spiel entwickelt, in dem sich die Spieler im Gefangenendilemma paarweise gegenüberstehen, um zu bestimmen, welche iterative Strategie die höchste Punktzahl gegen andere erzielt.
In dieser Frage habe ich mir eine Möglichkeit ausgedacht, mit der mehrere Personen gleichzeitig das Gefangenendilemma gegeneinander spielen können. Bei dieser Variante ist die Auszahlungsmatrix nicht erforderlich, wobei jedes Ergebnis zwischen jedem Paar von zwei Spielern die Summe von zwei funktional unabhängigen Entscheidungen ist.
Ihre Aufgabe ist es, eine KI zu bauen, um diese symmetrische, verallgemeinerte Version des Mehrspieler-Gefangenendilemmas zu spielen, die die höchstmögliche Punktzahl erzielt.
Spielregeln
In jeder Runde dieses Multiplayer-Mehrrunden-Gefangenen-Dilemmas kann sich ein Spieler A
entscheiden, von einem anderen Spieler "1" zu nehmen B
. Unter diesen Umständen A
erhöht sich die Punktzahl um 1, während sich B
die Punktzahl um 2 verringert. Diese Entscheidung kann zwischen jedem bestellten Spielerpaar getroffen werden.
Dies ist die einzige Entscheidung, die für jeden Spieler getroffen wird - entweder "nimm 1" oder nicht "nimm 1" von jedem anderen Spieler, der für die Übergabe bzw. die Zusammenarbeit homolog ist. Die effektive Auszahlungsmatrix zwischen zwei Spielern P1
und P2
sieht wie folgt aus :
P1/P2 P1 Take1 P1 Don't
P2 Take1 -1/-1 -2/+1
P2 Don't +1/-2 0/ 0
Turnierablauf
Das Spiel besteht aus P * 25
Runden, in denen P
die Anzahl der teilnehmenden Spieler angegeben ist. Alle Spieler beginnen mit einer Punktzahl von 0
. Jede Runde besteht aus dem folgenden Verfahren:
Zu Beginn einer Runde erhält jedes Programm eine Historie der vorherigen Runden aus der Standardeingabe in folgendem Format:
Eine Zeile , die 3 Zahlen
P
,D
undN
.P
ist die Gesamtzahl der Spieler im Spiel. Jedem Spieler wird1
zuP
Beginn des Spiels nach dem Zufallsprinzip eine ID-Nummer von bis zugewiesen .D
ist die ID des aktuellen Spielers.N
ist die Anzahl der Runden, die gespielt wurden.
N
Linien, wobei jede Linie die Ergebnisse einer Runde darstellt. In Zeilek
vonN
, wird es einige Zahl seinn_k
geordneter Paare(a, b)
durch Zwischenräume getrennt, die darstellen , dass der Spieler mit der IDa
von dem Spieler „1 nahm“ mit der IDb
in dieser Runde.Eine gleichmäßig zufällige Zahl
R
von0
bis18446744073709551615
(2 64 - 1), die als Pseudozufallskeim fungiert. Diese Zahlen werden aus einer vorgenerierten Datei gelesen, die am Ende des Turniers veröffentlicht wird, damit die Teilnehmer die Ergebnisse selbst überprüfen können.Eine zusätzliche Zeile, die eine Art von Status darstellt, der in Ihr Programm eingelesen werden soll, wenn Ihr Programm in der vorherigen Runde eine solche Ausgabe erzeugt hat. Zu Beginn des Spiels ist diese Zeile immer leer. Diese Zeile wird weder vom Scoring-Code noch von anderen Programmen geändert.
Jedes Programm verwendet dann seine Strategie, um Folgendes für die Standardausgabe zu erzeugen :
Eine Liste von
K
Zahlen, die die IDs der Programme sind, die von dieser Runde "1" erhalten. Eine leere Ausgabe bedeutet, dass sie nichts bewirkt.Optional eine zusätzliche Zeile, die eine Art von Status darstellt, der an spätere Runden weitergegeben werden soll. Diese genaue Zeile wird in der nächsten Runde an das Programm zurückgemeldet.
Nachfolgend finden Sie ein Eingabebeispiel für den Beginn des Spiels für einen Spieler mit Ausweis 3
in einem 4-Spieler-Spiel:
4 3 0
4696634734863777023
Nachfolgend finden Sie eine Beispieleingabe für dasselbe Spiel mit einigen bereits gespielten Runden:
4 3 2
(1, 2) (1, 3) (1, 4) (4, 2)
(1, 3) (2, 1) (2, 4) (3, 1) (4, 1)
4675881156406346380
Jedes Programm erhält für eine Runde genau die gleiche Eingabe, mit Ausnahme der ID-Nummer, D
die für jedes Programm eindeutig ist.
Unten sehen Sie eine Beispielausgabe, in der der Spieler 3
von allen anderen eine 1 nimmt:
1 2 4
Am Ende aller erforderlichen Runden gewinnt der Spieler mit der höchsten Endpunktzahl.
Zeitleiste
Die Kodierung für dieses Turnier dauert insgesamt 7 Tage. Einsendeschluss ist 2014-05-09 00:00 UTC
.
Veröffentlichen Sie keine aktuellen Programme vor diesem Datum. Veröffentlichen Sie den SHA256-Hash des Quellcodes Ihres Programms als Verpflichtung. Sie können diesen Hash jederzeit vor Ablauf der Frist ändern, nach Ablauf der Frist eingegangene Verpflichtungen werden jedoch nicht zur Entscheidung angenommen. (Bitte benutzen Sie die Basis 64 Notation für Ihre Hashes, da mein Verifizierungsprogramm die Basis 64 ausspuckt und es eine kompaktere Notation ist.)
Nach Ablauf der Frist haben Sie 1 Tag (bis 2014-05-10 00:00 UTC
) Zeit, um den tatsächlichen Quellcode Ihres Programms für Ihre Einreichung zu veröffentlichen. Wenn der SHA256-Hash Ihres veröffentlichten Quellcodes mit keinem Hash übereinstimmt, den Sie vor Ablauf der Frist veröffentlicht haben, wird Ihr Code nicht in das Turnier aufgenommen.
Danach lade ich alle Einsendungen auf meinen eigenen Computer herunter und führe alle Turnierteilnahmen in diesem Battle Royale durch. Hoffentlich veröffentliche ich die Ergebnisse innerhalb von 2 Tagen 2014-05-12 00:00 UTC
.
Ich werde die Antwort mit der höchsten Punktzahl akzeptieren und dieser Antwort eine Prämie von +100 gewähren, wenn ihre endgültige Punktzahl größer als ist 0
.
Nach dem Ende des Turniers werde ich die zufällige Startdatei veröffentlichen, die zur Durchführung des Wettbewerbs verwendet wurde, und möglicherweise werden andere Lösungen veröffentlicht, die versuchen, die im Turnier verwendeten zu übertreffen. Sie zählen jedoch nicht für die Annahme oder das Kopfgeld.
Die Host-Maschine
Ich werde diese Lösungen auf einer virtuellen Maschine auf meinem Computer ausführen. Auf dieser virtuellen Maschine wird Ubuntu Linux 14.04 mit 2 Gigabyte RAM ausgeführt. Mein Basiscomputer verfügt über einen Intel i7-2600K-Prozessor mit 3,40 GHz.
Bedarf
Ihr Programm muss in einer Sprache geschrieben sein, für die es einen Compiler oder Interpreter gibt, der Ihr Programm kompiliert, und der für die neueste Version von Ubuntu Linux verfügbar ist, damit ich alle Einsendungen ausführen und sie in einer virtuellen Maschine beurteilen kann.
Ihr Programm darf nicht mehr als 2.000 seconds
eine Runde dauern . Wenn Ihr Programm keine Zeit mehr hat oder einen Fehler erzeugt, wird die Ausgabe für diese Runde als leer betrachtet.
Ihr Programm muss deterministisch sein; Das heißt, es muss immer dieselbe Ausgabe für dieselbe Eingabe zurückgegeben werden. Pseudozufallslösungen sind zulässig; Ihre Zufälligkeit muss jedoch von dem zufälligen Startwert abhängen, der ihr als Eingabe gegeben wird, und von nichts anderem. Die Seed-Datei wurde mit Python's generiert os.urandom
. Es enthält insgesamt 500 Zeilen (bei Bedarf werden weitere Zeilen generiert) und der SHA256-Hash lautet K+ics+sFq82lgiLanEnL/PABQKnn7rDAGmO48oiYxZk=
. Sobald das Turnier vorbei ist, wird es hier hochgeladen.
Pflanzen
Zum Auftakt gibt es vier "Pflanzen", die anfängliche naive Strategien darstellen. Diese werden zusammen mit Ihren Einsendungen im Turnier gespielt. In dem unwahrscheinlichen Fall, dass einer von ihnen gewinnt, wird die höchste Punktzahl eines anderen Spielers als einer Pflanze als Sieger gewertet.
Um den Hash jeder Pflanzendatei zu berechnen, ersetzen Sie jede Gruppe von 4 Leerzeichen mit einem Tabulator, da der Formatierer hier keine Tabulatorzeichen zu mögen scheint.
Der Faule - macht nie etwas.
n1bnYdeb/bNDBKASWGywTRa0Ne9hMAkal3AuVZJgovI=
pass
The Greedy - nimmt immer 1 von allen anderen.
+k0L8NF27b8+Xf50quRaZFFuflZhZuTCQOR5t5b0nMI=
import sys
line1 = sys.stdin.readline()
n = [int(i) for i in line1.split()]
for i in range(n[0]):
if i+1 != n[1]:
print i+1,
print
The Wrathful - nimmt 1 von allen anderen in der ersten Runde und 1 von allen, die danach in der vorherigen Runde 1 davon genommen haben.
Ya2dIv8TCh0zWzRfzUIdFKWj1DF9GXWhbq/uN7+CzrY=
import sys
import re
line1 = [int(i) for i in sys.stdin.readline().split()]
players = line1[0]
pid = line1[1]
rounds = line1[2]
lines = []
if rounds == 0:
for i in range(players):
if i+1 != pid:
print i+1,
print
else:
for i in range(rounds):
lines.append(sys.stdin.readline())
lastline = lines[-1]
takes = re.findall(r'\([0-9]+, [0-9]+\)', lastline)
for take in takes:
sides = [int(i) for i in re.findall(r'[0-9]+', take)]
if sides[1] == pid:
print sides[0],
print
The Envious - Nimmt 1 von den 50% der Spieler mit der aktuell höchsten Punktzahl ohne sich selbst und rundet ab.
YhLgqrz1Cm2pEcFlsiIL4b4MX9QiTxuIOBJF+wvukNk=
import sys
import re
line1 = [int(i) for i in sys.stdin.readline().split()]
players = line1[0]
pid = line1[1]
rounds = line1[2]
lines = []
scores = [0] * players
if rounds == 0:
for i in range(players):
if i+1 != pid:
print i+1,
print
else:
for i in range(rounds):
takes = re.findall(r'\([0-9]+, [0-9]+\)', sys.stdin.readline())
for take in takes:
sides = [int(i) for i in re.findall(r'[0-9]+', take)]
scores[sides[0] - 1] += 1
scores[sides[1] - 1] -= 2
score_pairs = [(i+1, scores[i]) for i in range(players)]
score_pairs.sort(key=lambda x:(x[1], x[0]))
score_pairs.reverse()
taken = 0
j = 0
while taken < (players) / 2:
if score_pairs[j][0] != pid:
print score_pairs[j][0],
taken += 1
j += 1
In einem Turnier mit 100 Runden erhalten sie unter diesen vier Punkten:
Lazy: -204
Greedy: -100
Wrathful: -199
Envious: -199
Bewertungsprogramm
Ich habe das Richterprogramm veröffentlicht, das ich bei Github verwenden werde . Laden Sie es herunter und testen Sie es. (Und vielleicht ein oder zwei Fehler beheben, wenn Sie einen finden.: P)
Es gibt momentan keine Kompilierungsoptionen für etwas anderes als Python. Ich werde diese später einbeziehen - wenn die Leute Zusammenstellungs- oder Interpretationsskripte für andere Sprachen beisteuern könnten, wäre ich sehr dankbar.
Phase 2: Einreichung des Quellcodes
Ich habe tournament
für den Wettbewerb einen neuen Zweig ins Github-Repository gestellt, der die Datei pd_rand und andere Pflanzeneinträge enthält. Sie können Ihren Quellcode entweder hier veröffentlichen oder als Pull-Anfrage an diese Filiale senden.
Die Reihenfolge der Teilnehmer ist wie folgt:
'begrudger'
'regular'
'patient'
'lazy'
'backstab'
'bully'
'lunatic'
'envious'
'titfortat'
'greedy'
'wrathful'
'judge'
'onepercent'
Endergebnisse
Die Ausgabe meines Testprogramms:
Final scores:
begrudger -2862
regular -204
patient -994
lazy -2886
backstab -1311
bully -1393
lunatic -1539
envious -2448
titfortat -985
greedy -724
wrathful -1478
judge -365
onepercent -1921
Ranglisten:
1. regular -204
2. judge -365
3. greedy -724
4. titfortat -985
5. patient -994
6. backstab -1311
7. bully -1393
8. wrathful -1478
9. lunatic -1539
10. onepercent -1921
11. envious -2448
12. begrudger -2862
13. lazy -2886
Es stellt sich also heraus, dass der Gewinner tatsächlich ein Spieler ist - es ist The Regular mit -204 Punkten!
Leider war die Punktzahl nicht positiv, aber wir können kaum erwarten, dass in einer Simulation des Dilemmas des Iterierten Gefangenen, in dem alle spielen, um zu gewinnen.
Einige überraschende Ergebnisse (zumindest, was ich für überraschend hielt):
Der Gierige erzielte mehr als Tit für Tat, und in der Tat im Allgemeinen höher als die meisten Torschützen überhaupt.
Der Richter, der eigentlich eine Art "Moral-Vollstrecker" sein sollte (im Grunde genommen hat er 1 von demjenigen genommen, der 1 von einem überdurchschnittlich oft genommen hatte), erzielte ziemlich hohe Punkte, während dies bei Simulationstests tatsächlich der Fall war bekomme eine eher niedrige Punktzahl.
Und andere, die (ich dachte) nicht so überraschend waren:
Der Patient erzielte volle 484 Punkte mehr als The Wrathful. Es lohnt sich wirklich, das erste Mal zusammenzuarbeiten.
Ein Prozent hatte sehr schnell fast niemanden zu treten, während sie unten waren. Scheint, dass die 1% nur so bleiben können, weil sie mehr Spieler im Spiel haben.
Jetzt, da das Turnier vorbei ist, können Sie so viele zusätzliche Spieler einstellen, wie Sie möchten, und mit ihnen mithilfe des Richterprogramms herumprobieren.