Пишем «Камень, ножницы, бумагу»: выбор, правила и счёт раундов

Практикумобновлено 07.10.20269 мин чтенияесть тренажёр

«Камень, ножницы, бумага» на Python — короткая игра, в которой компьютер случайно выбирает один из 3 предметов, а программа сравнивает его с ходом игрока и ведёт счёт. Разберём случайный выбор через random.choice, правила через условия и через словарь, цикл раундов и матч до 3 побед.

Загружаем…

Правила «Камень, ножницы, бумага» и план программы

Правила умещаются в 3 фразы: камень тупит ножницы, ножницы режут бумагу, бумага накрывает камень. Одинаковые предметы дают ничью. Программе нужно знать именно эти три пары, всё остальное она выведет сама.

ПредметПроигрываетПобеждает
Каменьбумагеножницы
Ножницыкамнюбумагу
Бумаганожницамкамень

Для игры в «Камень, ножницы, бумагу» людям нужны только руки, а программе — список из трёх вариантов, случайный выбор и несколько условий. Варианты удобно хранить строками: их легко выводить на экран и сравнивать с вводом.

Сама программа строится из 5 шагов: компьютер делает случайный выбор, игрок вводит свой вариант, программа проверяет, что ввод корректный, определяет победителя раунда и обновляет счёт. Затем всё повторяется, пока кто-нибудь не наберёт 3 очка. Полная версия уже лежит в редакторе над статьёй: нажмите «▶ Запустить», впишите слово в поле под выводом и нажмите Enter. Если что-то сломается, кнопка «Сбросить» вернёт исходный код.

Игра хороша для тех, кто уже знает input, условия и цикл while, но ещё не пробовал словари. В ней сходятся сразу три базовых понятия вычислительного мышления — условия, циклы и данные, которые Бреннан и Резник выделяют как основу любого проекта[1]. По федеральной образовательной программе ветвления и составные условия изучают в 8 классе, так что игра подходит как практика к школьной теме.

Выбор компьютера: random.choice

Функция random.choice — это способ взять случайный элемент из списка: каждый элемент выпадает с одинаковой вероятностью. Для игры достаточно списка из трёх строк. В официальной документации Python описаны и другие функции модуля, но для выбора одного из трёх вариантов хватит choice.

import random

options = ["камень", "ножницы", "бумага"]
comp = random.choice(options)
print("Компьютер выбрал:", comp)

Можно обойтись без choice: взять random.randint(0, 2) и использовать результат как индекс списка. Работать будет так же, но строчка с choice короче и понятнее, а ошибиться с границами в ней нельзя. По документации Python функция choice падает с ошибкой IndexError, только если список пустой[2], — у нас в нём всегда три варианта. Больше примеров с перемешиванием и случайными числами собрано в статье про модуль random.

Ход игрока и проверка ввода

Чтобы программа не сломалась от лишнего пробела или заглавной буквы, ввод игрока нужно привести к одному виду: убрать пробелы по краям методом strip() и сделать все буквы строчными методом lower().

player = input("Твой ход: ").strip().lower()
while player not in options:
    player = input("Напиши камень, ножницы или бумага: ").strip().lower()

Оператор in проверяет, есть ли строка в списке. Если игрок написал «Камень » с пробелом и большой буквы, после обработки получится «камень», и проверка пройдёт. А на «ножници» с опечаткой программа вежливо переспросит. Без такой проверки любое неожиданное слово попадёт в сравнение и программа объявит победу компьютера, хотя игрок просто ошибся. Можно разрешить и сокращения: если первая буква «к», считать это камнем. Для этого пригодится срез player[0].

Кто победил: цепочка условий

Самый прямой способ определить победителя — 3 проверки: ничья, победа игрока, всё остальное. Победа игрока описывается тремя парами, соединёнными через or.

if player == comp:
    print("Ничья")
elif (
    (player == "камень" and comp == "ножницы")
    or (player == "ножницы" and comp == "бумага")
    or (player == "бумага" and comp == "камень")
):
    print("Ты победил")
else:
    print("Победил компьютер")

Скобки здесь важны: and выполняется раньше or, но явные скобки делают условие читаемым. Ветка else не перечисляет проигрышные сочетания — если не ничья и не победа, остаётся только поражение. Такой код работает, но у него есть слабое место: правила спрятаны внутри условия, и если захочется добавить четвёртый предмет, условие разрастётся до нечитаемого. Про сравнения и логические операторы подробно написано в уроке об условиях if, elif и else.

Словарь правил: короче и надёжнее

Словарь правил — это таблица «кто кого бьёт», записанная прямо в коде: ключ — предмет, значение — тот, кого он побеждает. Тогда проверка победы занимает одну строку. В документации Python словарь описан как набор пар «ключ — значение», где ключи не повторяются.

beats = {"камень": "ножницы", "ножницы": "бумага", "бумага": "камень"}

if player == comp:
    print("Ничья")
elif beats[player] == comp:
    print("Ты победил")
else:
    print("Победил компьютер")

Выражение beats[player] отвечает на вопрос «кого бьёт предмет игрока». Если это ровно то, что выбрал компьютер, раунд за игроком. Данные отделены от логики: чтобы поменять правила, достаточно поправить словарь, а условие останется прежним. Если словари пока незнакомы, начните с урока о словарях в Python.

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

beats = {"камень": "ножницы", "ножницы": "бумага", "бумага": "камень"}
for p in beats:
    for c in beats:
        if p == c:
            result = "ничья"
        elif beats[p] == c:
            result = "победа"
        else:
            result = "поражение"
        print(p, "против", c, "-", result)
камень против камень - ничья
камень против ножницы - победа
камень против бумага - поражение
ножницы против камень - поражение
ножницы против ножницы - ничья
ножницы против бумага - победа
бумага против камень - победа
бумага против ножницы - поражение
бумага против бумага - ничья

В правильной таблице ровно 3 ничьи, 3 победы и 3 поражения, и у каждого предмета одна победа и одно поражение. Такая самопроверка ловит опечатку в словаре быстрее, чем десяток сыгранных партий.

Счёт и цикл раундов

Матч до 3 побед — это цикл while с двумя счётчиками: он продолжается, пока ни у игрока, ни у компьютера нет трёх очков. После цикла остаётся объявить победителя матча.

В программе из редактора есть одна деталь, которую легко пропустить: команда continue после сообщения «Такого варианта нет». Она возвращает игру к началу цикла, и неверный ввод не считается раундом. Ничья тоже не меняет счёт, поэтому матч может длиться и 3 раунда, и 10. Вот как может выглядеть начало партии — конкретные ходы компьютера каждый раз будут другими:

Твой ход (камень, ножницы, бумага): бумага
Компьютер выбрал: камень
Раунд твой!
Счёт 1 : 0
Твой ход (камень, ножницы, бумага): ножници
Такого варианта нет, попробуй ещё раз

Если хочется матч не до 3, а до 5 побед, замените число в двух местах — а лучше заведите переменную target = 5 и используйте её и в цикле, и в итоговой проверке. Так поступают с любыми «магическими числами» в коде.

Вариант с пятью предметами

Существует расширенная версия игры с 5 предметами: к камню, ножницам и бумаге добавляются ящерица и Спок. Каждый предмет побеждает два других и проигрывает двум, поэтому в словаре значением становится список.

beats = {
    "камень": ["ножницы", "ящерица"],
    "ножницы": ["бумага", "ящерица"],
    "бумага": ["камень", "спок"],
    "ящерица": ["спок", "бумага"],
    "спок": ["ножницы", "камень"],
}
player = "спок"
comp = "камень"
if player == comp:
    print("Ничья")
elif comp in beats[player]:
    print("Ты победил")
else:
    print("Победил компьютер")

Проверка стала comp in beats[player] вместо сравнения через ==, а остальной код не изменился. Именно поэтому правила стоит хранить в словаре: игра расширяется правкой данных, а не переписыванием условий. Ради интереса посчитайте, сколько условий понадобилось бы в цепочке if для 5 предметов, — побед там 10. Компьютерная версия «Камень, ножницы, бумага» с пятью вариантами учит главному: удачная структура данных экономит код.

Когда эта игра не подходит

Программа «Камень, ножницы, бумага» не подходит как самый первый проект: в ней 4 новые темы сразу. Вот случаи, когда лучше выбрать что-то другое или отложить её.

  • Ещё не знакомы условия. Без if и elif не получится даже первая версия из 10 строк.
  • Ребёнку 6–7 лет. Набирать слова на клавиатуре утомительно; ту же логику можно разыграть на бумаге или в блочной среде.
  • Нужна графика с кнопками. Наша версия текстовая, 1 окно с вводом и выводом; картинки — это уже другой проект.
  • Хочется игру для 2 людей за одним компьютером. Второй игрок увидит ход первого на экране, так что честной игры не выйдет без хитростей.

«Против компьютера есть беспроигрышная тактика». Это миф: если выбор делает random.choice, у любого хода одинаковые шансы на победу, ничью и поражение, и выигрышной стратегии против случайности не существует.

«Если компьютер выиграл 4 раза подряд, программа жульничает». Это не подтверждается: серии подряд для случайных событий нормальны. Проверить честность можно по коду — выбор компьютера не зависит от хода игрока.

Чего делать нельзя

Нельзя проверять игру одной удачной партией: ошибки в правилах прячутся в редких сочетаниях. Вот 6 правил для этого проекта.

  • Нельзя сравнивать ввод без lower(): «Камень» с большой буквы не совпадёт с «камень».
  • Не стоит выбирать ход компьютера до цикла — тогда во всех раундах он будет одинаковым.
  • Не следует засчитывать раунд при опечатке: верните игрока к вводу через continue.
  • Не стоит писать 9 отдельных условий для всех сочетаний — хватит ничьей, одной проверки победы и else.
  • Нельзя менять счёт при ничьей, иначе матч закончится раньше, чем должен.
  • Не допускается называть переменную input: она перекроет встроенную функцию, и следующий ввод упадёт с ошибкой.

Отступы и имена в примерах следуют руководству PEP 8[3]. Оно советует давать переменным понятные имена: player_score читается лучше, чем ps. Если хочется ещё одну текстовую игру со случайностью, возьмитесь за игру «Угадай число».

Источники

[1] Brennan K., Resnick M. New frameworks for studying and assessing the development of computational thinking // Proceedings of the 2012 Annual Meeting of the American Educational Research Association. Vancouver, 2012.

[2] The Python Tutorial (официальная документация Python 3). Python Software Foundation.

[3] van Rossum G., Warsaw B., Coghlan N. PEP 8 — Style Guide for Python Code. Python Software Foundation, 2001.

Вопросы и ответы

Сколько строк кода нужно для игры «Камень, ножницы, бумага»?

Один раунд со словарём — около 10 строк. Матч со счётом и проверкой ввода, как в редакторе, занимает примерно 25.

Можно ли играть без русских букв?

Да, замените слова на «к», «н», «б» или на английские названия. Подойдёт любой вариант записи, главное — чтобы список и словарь использовали одни и те же строки.

Как показать, сколько было ничьих?

Заведите третий счётчик draws = 0 и увеличивайте его в ветке «Ничья». В конце матча выведите все три числа.

Почему программа говорит, что такого хода нет, хотя я написал правильно?

Скорее всего, в слове есть буква «ё» или латинская буква, похожая на русскую. Сравните строки посимвольно или выведите len(player).

Как сделать, чтобы компьютер «учился» на ходах игрока?

Храните, сколько раз игрок выбирал каждый предмет, и пусть компьютер чаще выбирает то, что бьёт любимый ход игрока. Это уже не случайная игра, а простая стратегия.

Что изучать после этого проекта?

Функции: вынесите раунд в def play_round(player, comp), которая возвращает результат. Код станет короче, и его будет проще проверять.