Частково спостерігається підключення-4


8

Гра

Ви будете грати в (майже) стандартну гру 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)

Щасливий KoTHing!

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

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 

Чи можете ви пояснити, чому ви перевіряєте view [-1] [j] == 0? Я не зовсім впевнений, що бачу, де ви їх заповнили, і мої пітонські знання здаються трохи іржавими.
Варвар772

@ Barbarian772 Я перевіряю, чи є в цьому стовпці пробіл. Зауважте, що існує 6 рядків, тому верхній ряд повністю дотримується.
user1502040

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

@soktinpk Це не просто додає ще один рівень стратегії? Зрештою, Connect-4 - це вирішена гра, тому коефіцієнт пропуску повороту може бути достатньою для зміни правил, що учасники не можуть просто використовувати стандартні алгоритми.
mypetlion

1
Нульова індексація знизу, чи є накреслені рядки (0,2,4,6) або (1,3,5)? Деякі мистецтва ASCII були б корисні.
SIGSTACKFAULT

Відповіді:


6

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

імпорт pprint, математика, колекції, копія
def zsani_bot_2 (перегляд, поворот, стан):
    if state == None: # first first own turn - завжди для середнього
        state = (1, 2), якщо повернути == 0 else (2, 1) # (my_symbol, твій символ)
        #print (pprint.pformat (view) + 'Turn:' + str (оборот) + 'Player:' + str (стан [0]))
        повернення 3, держ

    # знайдіть очевидні моменти
    для i в діапазоні (1, 6): #skip перший рядок
        для j у діапазоні (len (view [i])): #TODO: Оптимізуйте за допомогою zip. Перейдіть на ясність зараз
            якщо view [i] [j]! = 0 і переглянути [i-1] [j] == 0:
                view [i-1] [j] = стан [1]
    ворога_точки = математичний поверх (поворот / 2)
    ++ ворожі_точки, якщо стан [0] == 2 ще ворожі_точки
    known_points = sum ([i.count (стан [1]) для i view])
    missing_points = ворог_points - відомі_points

    #get впевнено перемагає в будь-якому напрямку
    для j у діапазоні (0, 7): # кожний стовпець
        для i в діапазоні (4, -1, -1):
            якщо view [i] [j]! = 0:
                break #find найвища відома заповнена точка
        якщо (не бракує_точок або i + 1 у {1, 3, 5}):
            view1 = copy.deepcopy (перегляд)
            спроба = apply_move (view1, стан [0], j)
            якщо спроба == WON:
               # print (pprint.pformat (перегляд) + 'Поворот:' + str (поворот) + 'Player:' + str (стан [0]) + 'переміщення переможця')
                повернути j, стан

    #block впевнений, що ворог перемагає в будь-якому напрямку
    для j в діапазоні (0, 7):
        для i в діапазоні (4, -1, -1):
            якщо view [i] [j]! = 0:
                break #find найвища відома заповнена точка
        якщо (не бракує_точок або (i + 1 у {1, 3, 5})):
            view1 = copy.deepcopy (перегляд)
            спроба = apply_move (view1, стан [1], j)
            якщо спроба == WON:
              # print (pprint.pformat (вид) + 'Поворот:' + str (поворот) + 'Player:' + str (стан [0]) + 'збереження переміщення')
                повернути j, стан

    #блок стіни
    для i в діапазоні (0, 3): # неможливо отримати 4 підряд, коли стовпчик заповнений
        для j в діапазоні (0, 6):
            якщо view [i] [j]! = 0 і view [i] [j] == view [i + 1] [j] та view [i + 2] [j] == view [i + 3] [j ] == 0:
             # print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'move move column))
                повернути j, стан

    #block платформи, якщо вони мають ідеальну інформацію в рядку нижче та крапці
    для i в діапазоні (0, 5):
        для j в діапазоні (0, 3):
            stats = collection.Counter ([view [i] [j], view [i] [j + 1], view [i] [j + 2], view [i] [j + 3]])
            якщо статистика [0] == 2 і (статистика [стан [0]] == 2 або статистика [стан [0]] == 2):
                для k в діапазоні (0, 3):
                    якщо переглянути [i] [j + k] == 0:
                        перерва
                якщо (i == 0 або перегляньте [i-1] [j + k]! = 0) і (не вистачає_точок або i в {1, 3, 5}):
                    #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'platform platform')
                    повернення j + k, стан
                ще:
                    для l в діапазоні (k, 3):
                        якщо переглянути [i] [j + l] == 0:
                            перерва
                        якщо (i == 0 або перегляньте [i-1] [j + l]! = 0) та (не вистачає_точок або i в {1, 3, 5}):
                     # print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'platform platform')
                            повернення j + l, стан

    #fallback -> випадковий
    істинно:
        j = випадковий.помаранчевий (0, 7)
        якщо переглянути [-1] [j] == 0:
            #print (pprint.pformat (перегляд) + 'Поворот:' + str (поворот) + 'Player:' + str (стан [0]) + 'випадковий хід')
            повернути j, стан

Дякуємо, що виправили run_game!

Журнал змін:

  • v2 додає горизонтальне блокування - якщо в ряду 4 є два порожніх місця і два плями, заповнені тим самим гравцем, він спробує заповнити одне з них, щоб було три підряд / заблокував ряд противника, що, сподіваємось з великої літери в наступних оборотах.

3
Ласкаво просимо на сайт. Я проголосував за відхилення редагування для зміни коду, найкраще залишити коментар, таким чином, як ОП може вирішити, що робити з кодом.
Спеціальний мисливець на Garf Garf

У мене недостатньо репутації, щоб коментувати головне повідомлення. Як я можу зняти правки?
Сифер Полскі

Не потрібно знімати правки (я не думаю, що ви все одно можете). Надалі коментарі будуть достатніми, але оскільки ви сказали це у своїй відповіді, цілком ймовірно, що ОП побачить. Плюс я думаю, що ОП побачить, що ви запропонували та відредагували, навіть якщо це було відхилено.
Ad Hoc Garf Hunter

Причина, яку я хотів би зняти з редагування, полягає в тому, що я пропустив один рядок змін, без яких відредагований код повністю не запуститься. Я включив його до пропозиції щодо редагування у своєму дописі. Дякую за твою допомогу!
Сифер Полскі

2

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

def normalBot(view, turn, state):
    randomNumber = round(np.random.normal(3, 1.25))
    fullColumns = []
    for i in range(7):
        if view[-1][i] != 0:
            fullColumns.append(i)
    while (randomNumber > 6) or (randomNumber < 0) or (randomNumber in fullColumns):
        randomNumber = round(np.random.normal(3, 1.25))
    return randomNumber, state

-1

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

import math, pprint
def manual_bot(view, turn, state):
    if state == None:
        state = (1, 2) if turn == 0 else (2, 1) #(my_symbol, your symbol)

#locate obvious points
    for row in range (1, 6):
        for j in range(len(view[row])):
            if view[row][j] != 0 and view[row-1][j] == 0:
                view[row-1][j] = state[1]

#if you're second, the opponent has one more point than half the turns
    enemy_points = math.ceil(turn/2)
    known_points = sum([row.count(state[1]) for row in view])
    missing_points = enemy_points - known_points

    print(pprint.pformat(view) + ' Turn: ' + str(turn) + ' Player: ' + str(state[0]) + ' Missing points: ' + str(missing_points))
    while True:
        try:
            move = int(input("What is your move?(0-6) "))
        except ValueError:
            continue
        if move in {0, 1, 2, 3, 4, 5, 6}:
            return move, state

Сітка перевернута догори дном (нижній ряд найвищий). Щоб отримати оголошення про переможця, вам потрібно проклеїти контролер гри, додавши заяву про друк перед поверненням виграшу:

elif outcome == WON:
    print(pprint.pformat(board) + ' Turn: ' + str(turn) +' Winner: '+ str(p))
    return p

[[0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 0 Гравець: 1 Пропущені бали: 0
Який ваш хід? (0-6) 3
[[0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 2 Гравець: 1 Пропущені бали: 0
Який ваш хід? (0-6) 2
[[0, 0, 1, 1, 0, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 4 Гравець: 1 Пропущені очки: 1
Який ваш хід? (0-6) 4
[[0, 0, 1, 1, 1, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 6 Гравець: 1 Пропущені очки: 2
Який ваш крок? (0-6) 1
[[2, 1, 1, 1, 1, 2, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Поворот: 6 Переможець: 1
[[0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 1 Гравець: 2 Пропущені очки: 1
Який ваш хід? (0-6) 2
[[0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 3 Гравець: 2 Пропущені очки: 2
Який ваш хід? (0-6) 3
[[0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 5 Гравець: 2 Пропущені бали: 1
Який ваш хід? (0-6) 4
[[0, 0, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 7 Гравець: 2 Пропущені бали: 2
Який ваш крок? (0-6) 1
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Черга: 9 Гравець: 2 Пропущені бали: 1
Який ваш хід? (0-6) 2
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 11 Гравець: 2 Пропущені бали: 1
Який ваш хід? (0-6) 4
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 2, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 13 Гравець: 2 Пропущені очки: 2
Який ваш хід? (0-6) 4
[[0, 2, 2, 1, 2, 0, 0],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 1, 0, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 15 Гравець: 2 Пропущені бали: 1
Який ваш хід? (0-6) 3
[[0, 2, 2, 1, 2, 0, 0],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 1, 2, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 17 Гравець: 2 Пропущені очки: 2
Який ваш хід? (0-6) 5
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 0, 1, 2, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 19 Гравець: 2 Пропущені бали: 0
Який ваш хід? (0-6) 
Який ваш хід? (0-6) 6
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 0, 1, 2, 1, 0, 2],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 21 Гравець: 2 Пропущені бали: 1
Який ваш крок? (0-6) 1
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 23 Гравець: 2 Пропущені бали: 1
Який ваш хід? (0-6) 3
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Черга: 25 Гравець: 2 Пропущені очки: 2
Який ваш хід? (0-6) 6
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 2, 2, 0, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Черга: 27 Гравець: 2 Пропущені очки: 1
Який ваш хід? (0-6) 5
[[1, 2, 2, 1, 2, 1, 1],
 [1, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 2, 2],
 [0, 1, 1, 2, 2, 0, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Черга: 29 Гравець: 2 Пропущені бали: 0
Який ваш хід? (0-6) 5
[[1, 2, 2, 1, 2, 1, 1],
 [1, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 2, 2],
 [0, 1, 1, 2, 2, 2, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Поворот: 29 Переможець: 2
1 раунд 1

Назва Нічия Втрати Виграє Оцінка
manual_bot: 0 0 2 1.000 zsani_bot_2: 0 2 0 -1.000

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.