Игра
Вы будете играть (почти) в стандартную игру Connect-4 . К сожалению, это игра по переписке, и кто-то поместил черную ленту на каждом втором ряду, начиная снизу, чтобы вы не могли видеть какие-либо движения вашего противника в этих рядах.
Любые ходы в уже заполненных столбцах будут считаться проходящими ваш ход, и если игра длится дольше 6 * 7
ходов, она будет рассматриваться как ничья.
Спецификация вызова
Ваша программа должна быть реализована как функция Python 3. Первый аргумент - это «представление» доски, представляющее известное состояние доски в виде двухмерного списка строк снизу вверх, где 1
происходит движение первым игроком, 2
движение вторым игроком и 0
пустая позиция или скрытая двигаться вашим противником.
Второй аргумент - это номер хода, индексируемый 0
, и его четность говорит вам, какой вы игрок.
Последний аргумент - это произвольное состояние, инициализируемое None
в начале каждой игры, которое вы можете использовать для сохранения состояния между ходами.
Вы должны вернуть 2 кортежа индекса столбца, который вы хотите воспроизвести, и новое состояние, которое будет возвращено вам в следующем ходу.
счет
Выигрыш считается как +1
, ничья как 0
, а проигрыш как -1
. Ваша цель - набрать наивысший средний балл в круговом турнире. Я постараюсь провести столько матчей, сколько потребуется, чтобы определить явного победителя.
правила
Любой участник должен иметь не более одного конкурирующего бота одновременно, но все в порядке, если вы вносите улучшения. Пожалуйста, постарайтесь ограничить вашего бота до ~ 1 секунды времени на обдумывание.
тестирование
Вот исходный код для контроллера, вместе с несколькими неконкурентными примерами ботов для справки:
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)
С Днем Рождения!
Предварительные результаты
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
источник
Ответы:
Этот бот берет все верные победы и отступает, чтобы блокировать соперников, затем угадывать их по вертикали и горизонтали или делать случайные шаги.
Спасибо за исправление run_game!
Changelog:
источник
normalBot играет при условии, что пятна в середине более ценны, чем пятна на концах. Таким образом, он использует нормальное распределение по центру, чтобы определить свой выбор.
источник
Это явно не конкурирует, но, тем не менее, очень полезно для отладки ... и удивительно весело, если вы достаточно хорошо знаете своего бота, чтобы обманывать: D, поэтому я пишу в надежде вдохновить больше представлений:
Сетка вверх ногами (нижний ряд самый высокий). Чтобы получать объявления о победителях, вам необходимо исправить игровой контроллер, добавив инструкцию печати перед возвратом выигрыша:
источник