Задание 21: кто выигрывает и дерево всех партий

Самое сложное игровое задание: определяем, у кого стратегия, и описываем дерево всех партий при оптимальной защите.

Задание 21 нередко требует: при заданном S указать, что выигрывает второй игрок не позднее второго хода, и построить дерево всех партий при его стратегии.

Что проверяет задание 21

Задание 21 — высокий уровень, 1 балл (но один из самых трудоёмких). Формулировки бывают разные, частая такая: «Найдите значение S, при котором у Вани (второго) есть выигрышная стратегия, позволяющая выиграть не позднее второго хода, причём у Пети нет стратегии, гарантирующей выигрыш за два хода; для найденного S постройте дерево всех партий при выигрышной стратегии Вани». Помимо числа, на бумаге надо нарисовать дерево.

Метод: сначала кодом находим S, потом рисуем дерево

Используем те же функции win_in_1 и win_le. Ваня (второй) выигрывает за ≤ 2 хода, если при ЛЮБОМ ходе Пети получившаяся позиция выигрышна для Вани за ≤ 2 хода. А «у Пети нет стратегии за два хода» — это not win_le(S, 2) для Пети.

Решение на Python

from functools import lru_cache

LIMIT = 49

def moves(s):
    return [s + 1, s * 2]

def win_in_1(s):
    return any(m >= LIMIT for m in moves(s))

@lru_cache(maxsize=None)
def win_le(s, k):
    if s >= LIMIT or k == 0:
        return False
    for m in moves(s):
        if m >= LIMIT:
            return True
        if all(win_le(om, k - 1) for om in moves(m)):
            return True
    return False

def vanya_win_le(s, k):
    # Ваня (второй) выигрывает за <= k: Петя не выигрывает сразу,
    # и при любом ходе Пети Ваня дальше выигрывает за <= k
    if win_in_1(s):
        return False
    return all(win_le(m, k) for m in moves(s))

answer = [s for s in range(1, LIMIT)
          if (not win_le(s, 2)) and vanya_win_le(s, 2)]
print("Подходящие S (задание 21):", answer)

Вывод:

Подходящие S (задание 21): [22, 24]

Подходят S = 22 и S = 24: при них у Пети нет стратегии выиграть за два хода, а Ваня выигрывает не позднее второго хода.

Построение дерева партий (для S = 22)

Дерево рисуют так: в узлах — число камней, на рёбрах — кто ходит. Раскрываем ВСЕ ходы Пети (он играет как угодно) и ходы Вани по его выигрышной стратегии. Покажем код, который печатает дерево с отступами для S = 22.

LIMIT = 49

def moves(s):
    return [s + 1, s * 2]

def win_in_1(s):
    return any(m >= LIMIT for m in moves(s))

def best_vanya_move(s):
    # Ваня выбирает ход: сначала выигрыш сразу, иначе — в проигрышную для Пети
    for m in moves(s):
        if m >= LIMIT:
            return m
    for m in moves(s):
        if all(win_in_1(x) for x in moves(m)):  # Петя в ловушке
            return m
    return moves(s)[0]

def show(s, player, depth):
    pad = "  " * depth
    if s >= LIMIT:
        print(f"{pad}{s}  (партия окончена)")
        return
    print(f"{pad}{s}  -> ходит {player}")
    if player == "Петя":
        for m in moves(s):           # Петя ходит как угодно — все ветки
            show(m, "Ваня", depth + 1)
    else:
        m = best_vanya_move(s)       # Ваня — по своей стратегии (одна ветка)
        show(m, "Петя", depth + 1)

show(22, "Петя", 0)

Вывод:

22  -> ходит Петя
  23  -> ходит Ваня
    24  -> ходит Петя
      25  -> ходит Ваня
        50  (партия окончена)
      48  -> ходит Ваня
        49  (партия окончена)
  44  -> ходит Ваня
    88  (партия окончена)

Читаем дерево: Петя из 22 может пойти в 23 или 44. Если в 44 — Ваня сразу удваивает до 88 ≥ 49 и выигрывает. Если в 23 — Ваня идёт в 24 (ловушка из задания 19!), и какой бы ход Петя ни сделал (25 или 48), Ваня добивает (50 или 49). Во всех ветках побеждает Ваня — это и есть его выигрышная стратегия.

Как оформлять на экзамене

  • В узлах пишите число камней, на рёбрах — кто ходит и какой ход (+1 или ×2).
  • Ветви Пети раскрывайте ПОЛНОСТЬЮ (все его ходы), ветви Вани — только по его стратегии (один ход).
  • Каждый лист должен заканчиваться победой Вани (камней ≥ порога после хода Вани).

Типичные ошибки

  • Раскрывают не все ходы проигрывающего. Для игрока без стратегии (Пети) надо показать ВСЕ его ходы — иначе дерево неполное.
  • Раскрывают лишние ходы выигрывающего. Для Вани показывают только выигрывающий ход, не все.
  • Лист не заканчивается победой нужного игрока. Проверяйте, что последний ход делает именно Ваня.
  • Путают S = 22 и S = 24. Оба подходят, но деревья разные; стройте для того, что указали в ответе.

Итог

  • Задание 21 = найти S и построить дерево всех партий при выигрышной стратегии.
  • Ходы проигрывающего раскрывают полностью, ходы выигрывающего — только по стратегии.
  • Код находит S и печатает дерево с отступами — переносите его на бумагу узлами и рёбрами.
Проверьте себя
1. При построении дерева партий как раскрывают ходы игрока, у которого НЕТ выигрышной стратегии?
AПоказывают только один его ход
BПоказывают ВСЕ его возможные ходы
CНе показывают вовсе
DПоказывают ходы соперника
2. А ходы игрока, у которого ЕСТЬ выигрышная стратегия?
AВсе возможные ходы
BТолько ход(ы), предписанные его выигрышной стратегией
CНи одного
DСлучайный ход
3. Чем должен заканчиваться каждый лист дерева в задании про выигрыш Вани?
AПобедой Пети
BЛюбой позицией
CПобедой Вани (после его хода камней не меньше порога)
DНичьей
Поддержать проект