Das Spiel
Sie spielen ein (fast) Standardspiel von Connect-4 . Leider ist es ein Korrespondenzspiel und jemand hat jede zweite Reihe von unten schwarz mit Klebeband versehen, so dass Sie keine Bewegungen Ihres Gegners in diesen Reihen sehen können.
Alle Züge in bereits vollen Spalten gelten als bestanden, und wenn ein Spiel länger als die 6 * 7
Runden läuft , wird es als Unentschieden gewertet.
Herausforderungsspezifikation
Ihr Programm sollte als Python 3-Funktion implementiert sein. Das erste Argument ist eine "Ansicht" des Bretts, die den bekannten Brettstatus als 2D-Liste von Zeilen von unten nach oben darstellt, wobei 1
es sich um einen Zug des ersten Spielers, 2
einen Zug des zweiten Spielers und 0
eine leere Position oder eine versteckte handelt bewege dich an deinem Gegner vorbei.
Das zweite Argument ist eine Zugnummer, aus der indiziert wird 0
, und ihre Parität gibt an, welcher Spieler Sie sind.
Das letzte Argument ist ein beliebiger Status, None
der zu Beginn jedes Spiels initialisiert wurde und mit dem Sie den Status zwischen den Runden beibehalten können.
Sie sollten ein 2-Tupel des Spaltenindex zurückgeben, den Sie spielen möchten, und den neuen Status, der Ihnen in der nächsten Runde zurückgegeben werden soll.
Wertung
Ein Gewinn zählt als +1
, ein Unentschieden als 0
und ein Verlust als -1
. Ihr Ziel ist es, die höchste durchschnittliche Punktzahl in einem Round-Robin-Turnier zu erzielen. Ich werde versuchen, so viele Spiele wie nötig durchzuführen, um einen eindeutigen Gewinner zu ermitteln.
Regeln
Jeder Konkurrent sollte höchstens einen konkurrierenden Bot gleichzeitig haben, aber es ist in Ordnung, Ihren Eintrag zu aktualisieren, wenn Sie Verbesserungen vornehmen. Bitte versuchen Sie, Ihren Bot auf ~ 1 Sekunde Denkzeit pro Runde zu beschränken.
Testen
Hier ist der Quellcode für den Controller zusammen mit einigen nicht konkurrierenden Beispiel-Bots als Referenz:
import itertools
import random
def get_strides(board, i, j):
yield ((i, k) for k in range(j + 1, 7))
yield ((i, k) for k in range(j - 1, -1, -1))
yield ((k, j) for k in range(i + 1, 6))
yield ((k, j) for k in range(i - 1, -1, -1))
directions = [(1, 1), (-1, -1), (1, -1), (-1, 1)]
def diag(di, dj):
i1 = i
j1 = j
while True:
i1 += di
if i1 < 0 or i1 >= 6:
break
j1 += dj
if j1 < 0 or j1 >= 7:
break
yield (i1, j1)
for d in directions:
yield diag(*d)
DRAWN = 0
LOST = 1
WON = 2
UNDECIDED = 3
def get_outcome(board, i, j):
if all(board[-1]):
return DRAWN
player = board[i][j]
strides = get_strides(board, i, j)
for _ in range(4):
s0 = next(strides)
s1 = next(strides)
n = 1
for s in (s0, s1):
for i1, j1 in s:
if board[i1][j1] == player:
n += 1
if n >= 4:
return WON
else:
break
return UNDECIDED
def apply_move(board, player, move):
for i, row in enumerate(board):
if board[i][move] == 0:
board[i][move] = player
outcome = get_outcome(board, i, move)
return outcome
if all(board[-1]):
return DRAWN
return UNDECIDED
def get_view(board, player):
view = [list(row) for row in board]
for i, row in enumerate(view):
if i % 2:
continue
for j, x in enumerate(row):
if x == 3 - player:
row[j] = 0
return view
def run_game(player1, player2):
players = {1 : player1, 2 : player2}
board = [[0] * 7 for _ in range(6)]
states = {1 : None, 2 : None}
for turn in range(6 * 7):
p = (turn % 2) + 1
player = players[p]
view = get_view(board, p)
move, state = player(view, turn, states[p])
outcome = apply_move(board, p, move)
if outcome == DRAWN:
return DRAWN
elif outcome == WON:
return p
else:
states[p] = state
return DRAWN
def get_score(counts):
return (counts[WON] - counts[LOST]) / float(sum(counts))
def run_tournament(players, rounds=10000):
counts = [[0] * 3 for _ in players]
for r in range(rounds):
for i, player1 in enumerate(players):
for j, player2 in enumerate(players):
if i == j:
continue
outcome = run_game(player1, player2)
if outcome == DRAWN:
for k in i, j:
counts[k][DRAWN] += 1
else:
if outcome == 1:
w, l = i, j
else:
w, l = j, i
counts[w][WON] += 1
counts[l][LOST] += 1
ranks = sorted(range(len(players)), key = lambda i: get_score(counts[i]), reverse=True)
print("Round %d of %d\n" % (r + 1, rounds))
rows = [("Name", "Draws", "Losses", "Wins", "Score")]
for i in ranks:
name = players[i].__name__
score = get_score(counts[i])
rows.append([name + ":"] + [str(n) for n in counts[i]] + ["%6.3f" % score])
lengths = [max(len(s) for s in col) + 1 for col in zip(*rows)]
for i, row in enumerate(rows):
padding = ((n - len(s)) * ' ' for s, n in zip(row, lengths))
print(''.join(s + p for s, p in zip(row, padding)))
if i == 0:
print()
print()
def random_player(view, turn, state):
return random.randrange(0, 7), state
def constant_player(view, turn, state):
return 0, state
def better_random_player(view, turn, state):
while True:
j = random.randrange(0, 7)
if view[-1][j] == 0:
return j, state
def better_constant_player(view, turn, state):
for j in range(7):
if view[-1][j] == 0:
return j, state
players = [random_player, constant_player, better_random_player, better_constant_player]
run_tournament(players)
Fröhliches KoTHing!
Vorläufige Ergebnisse
Name Draws Losses Wins Score
zsani_bot: 40 5377 94583 0.892
better_constant_player: 0 28665 71335 0.427
constant_player: 3 53961 46036 -0.079
normalBot: 38 64903 35059 -0.298
better_random_player: 192 71447 28361 -0.431
random_player: 199 75411 24390 -0.510
quelle
Antworten:
Dieser Bot nimmt alle sicheren Siege und fällt zurück, um die Rivalen zu blockieren, sie vertikal und horizontal zu erraten oder zufällige Züge zu machen.
Danke, dass du run_game repariert hast!
Änderungsprotokoll:
quelle
normalBot geht davon aus, dass Flecken in der Mitte wertvoller sind als Flecken an den Enden. Daher wird eine in der Mitte zentrierte Normalverteilung verwendet, um die Auswahl zu bestimmen.
quelle
Dies ist offensichtlich nicht konkurrierend, aber dennoch sehr nützlich zum Debuggen ... und überraschend lustig, wenn Sie Ihren Bot gut genug kennen, um zu schummeln: D Ich poste also in der Hoffnung, weitere Einsendungen zu inspirieren:
Das Gitter steht auf dem Kopf (die unterste Reihe ist am höchsten). Um die Gewinnerankündigungen zu erhalten, müssen Sie den Gamecontroller patchen und eine Druckanweisung hinzufügen, bevor Sie den Gewinn zurückgeben:
quelle