Во всяком случае, чтобы сделать эту головоломку, которая не грубой силой (и как вы грубой силой)? - PullRequest
2 голосов
/ 14 марта 2012

Вы пытаетесь решить, кто победит в игре, предполагая оптимальный выбор. Идея в том, что есть x количество пробелов, _ _ _ _ _ _ _. Первый игрок идет и отмечает два подряд места. Затем второй игрок идет и делает то же самое. Первый игрок, который не может выиграть. Так что, учитывая доску раньше, это одна из возможностей:

P1: x x _ _ _ _ _

P2: x x _ x x _ _

P1: х х _ х х х х

Итак, игрок 2 выигрывает. Вам дан массив, где 0 представляет свободное пространство, а 1 представляет отмеченное место. Массив может иметь уже отмеченные пятна.

Единственный способ, которым я могу думать, это проверять каждый ход, а за каждым ходом проверять каждый возможный ход после результатов в выигрыше. Я даже не могу полностью понять, как я это сделаю, но я надеялся, что есть лучший способ. Есть идеи?

Ответы [ 4 ]

3 голосов
/ 14 марта 2012

Вы должны работать с такими проблемами задом наперед.

Учитывая все возможные игровые состояния, пройдите и решите, какая победа для игрока 1 (W), а какая победа для игрока 2 (L).Изначально вы знаете ответ только для тех штатов, где никто не может играть.В этом случае ответом будет

  • W, если нет двух последовательных пятен, а общее количество взятых пятен равно 4k
  • L, если нет двух последовательных пятен и общее количествозанято пятен 4k+2

Теперь работать в обратном направлении.Если есть какая-либо позиция на доске, из которой игрок 1 может перейти к W, пометьте это как W, и если есть какая-либо позиция на доске, с которой игрок 2 может перейти к L, отметьте это как L,Опять же, это не сразу помечает все позиции, но повторяет это.Итерационная часть - это

  • W, если есть 4k+2 пятна и два последовательных пятна, которые при заполнении дают позицию, помеченную W
  • L, если естьэто 4k пятен и двух последовательных пятен, которые при заполнении дают позицию, помеченную L

Пример : Рассмотрим доску _ _ _ _ _ _.Оценить из конечных состояний, работающих в обратном направлении

Второй игрок, чтобы переместиться:

X X X X X X (L - terminal)

Первый игрок, чтобы переместить:

X X _ X X _ (W - terminal)
_ X X _ X X (W - terminal)
_ X X X X _ (W - terminal)
X X X X _ _ (L - must move to X X X X X X)
_ _ X X X X (L - must move to X X X X X X)

Второй игрок, чтобы переместиться:

X X _ _ _ _ (L - can move to X X X X _ _) 
_ X X _ _ _ (W - must move to _ X X _ X X or _ X X X X _)
_ _ X X _ _ (L - can move to X X X X _ _)
_ _ _ X X _ (W - must move to X X _ X X _ or _ X X X X _)
_ _ _ _ X X (L - can move to X X _ _ X X)

Первый игрок для перемещения:

_ _ _ _ _ _ (W - can move to _ X X _ _ _)

Вы можете запрограммировать это рекурсивно так, чтобы каждая позиция была оценена как W или L.Пусть каждая позиция доски P будет представлена ​​двоичным вектором длиной n, где 1 обозначает занятую точку, а 0 обозначает открытую точку.Вот псевдокод для doesPlayerOneWin:

// STORE NUMBER OF ONES
int m = 0;
for (int i=0; i<n; ++i)
    m += P[i];
// LOOK FOR LEGAL MOVES
bool canMove = false;
for (int i=0; i<n-1; ++i)
    int[] newP = P;
    if (P[i]+P[i+1] == 0) {
        canMove = true;
        newP[i] = 1;
        newP[i+1] = 1;
        // PLAYER ONE CAN MOVE TO WIN
        if (m % 4 == 0 && doesPlayerOneWin(newP))
            return true;
        // PLAYER TWO CAN MOVE TO WIN
        if (m % 4 == 2 && !doesPlayerOneWin(newP))
            return false;
     }
}
// IF NO LEGAL MOVES, PLAYER TO MOVE WINS
if (!canMove && m % 4 == 0)
    return true;
else if (!canMove && m % 4 == 2)
    return false;
// OTHERWISE IF LOOP RUNS, PLAYER TO MOVE LOSES
if (m % 4 == 0)
    return false;
else
    return true;
2 голосов
/ 15 марта 2012

Существует теория, которая позволяет решать такие игры.

Ваша игра - беспристрастная игра, в которой оба игрока имеют одинаковые ходы с каждой позиции.Шахматы не беспристрастны, поскольку белые могут контролировать только белые фигуры.Игра заканчивается, когда у игрока нет хода, затем он проигрывает.Предположим, что каждая игра заканчивается в ограниченное время.

Вы можете проанализировать позиции и пометить их, как предложил PengOne, L и W. Проигрышная позиция - это та, где все возможные ходы приводят к выигрышной позиции и выигрышной.позиция - та, где есть по крайней мере один ход в проигрышную позицию.Рекурсивная, но четко определенная маркировка.Когда у игрока нет хода, все последовательные позиции выигрывают ( пустая правда ), поэтому это помечается как проигрышная позиция.

Вы можете вычислить немного больше информации, которая поможетвы.Назовите mex (A) наименьшим неотрицательным целым числом не в A. Например, mex ({0,1,5}) = 2 и mex ({1,2,3}) = 0.Теперь вы помечаете каждую позицию меткой всех меток, куда вы можете перейти.Это также рекурсивная и четкая маркировка.Позиция проигрывает, если ее значение равно 0. В соответствии с этой классификацией позиция с меткой 0 проигрывает, но у вас есть точная классификация выигрышных позиций с номерами 1,2, ....

Эти числа позволяютВы рассчитать стоимость суммы двух игр.Вы можете добавить две игры, играя в них самостоятельно.Во время хода вы можете играть в первой или во второй игре.Позиция в вашей игре ___X__X__ на самом деле представляет собой сумму трех игр ___, __, __.

Теорема Спрейга-Гранди. Сумма из N оцененных игрa_1, a_2, ..., a_N ценится a_1 xor a_2 xor ... a_N.Поэтому сумма N игр теряется, если их значения xor равны 0.

Ваша начальная позиция - это сумма K независимых игр, разделенных Xs.Вам нужно найти значение Sprague-Grundy для каждой пустой полосы ___...__, xor их и вернуть, если результат равен 0. Я думаю, вы можете получить подсказку о том, как вычислить значения, если вы попытаетесь вычислить первые 50 из них.

Поскольку я не люблю использовать этот сайт в качестве замены для работы, я останавливаюсь здесь.Надеюсь, вы сможете закончить, если вы застряли, задавайте вопросы.

1 голос
/ 15 марта 2012

Для чего бы то ни было, вот решатель грубой силы, написанный на Python.Веселье!Вы даже можете запустить его с N> 2, чтобы записывать большие отрезки частей за каждый ход.Обратите внимание, что проигравший игрок просто играет самый левый действительный ход.

Сначала вывод.Я запускал игру на разных размерах доски (от 2 до 16 здесь).

Size = 2
..
11
Player 1 Wins!

Size = 3
...
11.
Player 1 Wins!

Size = 4
....
.11.
Player 1 Wins!

Size = 5
.....
11...
1122.
Player 2 Wins!

Size = 6
......
..11..
2211..
221111
Player 1 Wins!

Size = 7
.......
11.....
1122...
112211.
Player 1 Wins!

Size = 8
........
.11.....
.1122...
.112211.
Player 1 Wins!

Size = 9
.........
11.......
1122.....
112211...
11221122.
Player 2 Wins!

Size = 10
..........
....11....
22..11....
22..1111..
22221111..
2222111111
Player 1 Wins!

Size = 11
...........
11.........
1122.......
112211.....
11221122...
1122112211.
Player 1 Wins!

Size = 12
............
.11.........
.1122.......
.112211.....
.11221122...
.1122112211.
Player 1 Wins!

Size = 13
.............
...11........
22.11........
22.11.11.....
22.11.1122...
22.11.112211.
Player 1 Wins!

Size = 14
..............
......11......
22....11......
22....1111....
2222..1111....
2222..111111..
222222111111..
22222211111111
Player 1 Wins!

Size = 15
...............
11.............
11...22........
1111.22........
1111.22.22.....
1111.22.2211...
1111.22.221122.
Player 2 Wins!

Size = 16
................
.....11.........
22...11.........
2211.11.........
2211.1122.......
2211.112211.....
2211.11221122...
2211.1122112211.
Player 1 Wins!

Вот код:

N = 2 # number of pieces placed per turn

CACHE = {}

def compute_moves(board):
    gaps = [0] * len(board)
    previous = 0
    for i in range(len(board) - 1, -1, -1):
        if board[i]:
            previous = 0
            gaps[i] = 0
        else:
            previous += 1
            gaps[i] = previous
    return [i for i, gap in enumerate(gaps) if gap >= N]

def do_move(board, index, player):
    for i in range(N):
        board[index + i] = player

def undo_move(board, index):
    for i in range(N):
        board[index + i] = 0

def search(board):
    key = tuple(board)
    if key in CACHE:
        return CACHE[key]
    moves = compute_moves(board)
    for move in moves:
        do_move(board, move, 1)
        a, _ = search(board)
        undo_move(board, move)
        if not a:
            result = (True, move)
            break
    else:
        result = (False, moves[0] if moves else None)
    CACHE[key] = result
    return result

def play(board):
    player = 0
    while True:
        print ''.join(str(x or '.') for x in board)
        _, index = search(board)
        if index is None:
            break
        do_move(board, index, player + 1)
        player = int(not player)
    print 'Player %d Wins!' % (int(not player) + 1)

for size in range(2, 17):
    print 'Size = %d' % size
    board = [0] * size
    play(board)
    print
1 голос
/ 14 марта 2012

Assuming optimal choices на самом деле является оптимальной стратегией.

Распространенный способ «выбрать» ходы для игрока - это использование min-max алгоритма .[это на самом деле алгоритм, который был использован темно-синий , чтобы выиграть Каспарова].Min-Max «выбирает» оптимальный ход для каждого игрока, чтобы выбрать ход, который он будет производить.

min-max алгоритм обычно эвристический и дает оценку каждому состоянию игры, однако - в вашемВ этом случае, так как вы ищете оптимальные ходы и решение грубой силы, вам нужно использовать min-max в каждой итерации, пока игра не закончится с победителем / проигравшим, и это единственная оценка доски.

Используя этот метод, вы можете определить, какой игрок выиграет, используя значение начального состояния - он укажет, является ли игрок, которого вы рассматривали как «я», победителем или проигравшим.

Обратите внимание, что для этогоВ конкретном случае, когда плата оценивается только в соответствии с ее конечным состоянием, min-max превращается во что-то очень похожее на решение @ PengOne.

...