Пишем игру Ним на Python
easy

Пишем игру Ним на Python

Простой проект с продолжением

Вчера мы рассказали, как теория игр работает на практике и помогает победить. Вот кратко основное: 

  • теория игр — это не только про игры, а про любые ситуации, в которых может что-то происходить, меняться и зависеть от разных факторов;
  • игрок — это тот, кто находится в этой ситуации и принимает решения;
  • смысл теории игр в том, чтобы понять, как нужно действовать игрокам в разных ситуациях, чтобы получить нужный результат. Но не интуитивно, а с точки зрения законов математики. 

В прошлой статье мы разобрали игру Ним с точки зрения логики и математики. Сегодня мы реализуем её на Python.

Классические правила игры Ним звучат так:

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

Что делаем

Пишем игру Ним с двумя кучками — количество камней в кучках будет больше одного, за раз можно брать сколько угодно камней.

Играть будем против компьютера — он случайным образом будет выбирать кучку и количество камней, которые будет забирать. Кто заберёт последние камни, тот и победил.

Для этого нам понадобится Python — с ним получится быстро написать код и сразу проверить его в деле.

Логика проекта

Сегодня сделаем простую версию игры, которая будет работать так:

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

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

Подготавливаем переменные

Для игры нам нужен модуль случайных чисел и переменные для кучек. Ещё добавим название текущего игрока — это поможет при проверках в дальнейшем. Начинать будет игрок, поэтому сразу пропишем его в переменной:

# подключаем модуль для работы со случайными числами
import random
# формируем кучки с камнями
stack_1 = random.randint(2,10)
stack_2 = random.randint(2,10)
# выбранная кучка
select = 0
# сколько камней взяли из кучки
taken = 0
# кто делает текущий ход
current_player = 'Игрок'

Забираем камни из кучки

Чтобы взять камни из кучки, нам нужно знать две вещи: из какой кучки забирать и сколько камней брать. Сделаем отдельную функцию, в которой на вход подаётся кучка и её номер; а внутри будет выбор, что делать для каждого игрока.

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

После этого нам остаётся вывести состояние текущего хода и уменьшить кучку на выбранное число камней. Результат работы функции — новое количество камней в выбранной кучке:

# забираем камни из кучки
def take(stack, num):
        # будем менять глобальную переменную внутри функции
	global taken
        # если ход делает компьютер
	if current_player == 'Компьютер':
                # случайным образом выбираем количество камней, которые заберём из кучки
		taken = random.randint(1,stack)
        # выводим состояние текущего хода
	print(current_player + ' взял ' + str(taken) + ' камней из ' + str(num) + ' кучки')
        # уменьшаем кучку на выбранное количество камней
	stack = stack - taken
        # возвращаем количество камней в кучке
	return(stack)

Выводим текущее количество камней

На старте игры и после каждого хода нам важно знать, сколько камней лежит в каждой кучке. Для этого напишем простую функцию — она сообщит нам о состоянии кучек и заодно проверит, не выиграл ли кто-то после своего хода. Если выиграл — выводится сообщение и игра останавливается.

# выводим текущее состояние кучек
def result():
        # сообщаем, сколько камней в каждой кучке
        print('Камней в 1 кучке: ' + str(stack_1))
        print('Камней в 2 кучке: ' + str(stack_2))
        # если в кучках не осталось камней — текущий игрок победил
        if stack_1 == 0 and stack_2 == 0:
                print('Игра окончена, победил ' + current_player)
                # выходим из программы
                exit()

Программируем ход компьютера

Для начала компьютеру нужно проверить, в каких кучках есть камни: если в одной кучке нет камней, значит, берём из другой. Если камни есть в обеих кучках — случайным способом выбираем номер кучки и количество камней, которые надо забрать. Так как мы не проверяем выигрышность, ходы компьютера могут быть совсем не оптимальными:

# ход компьютера
def take_computer():
        # будем работать с глобальными переменными
        global stack_1, stack_2, current_player
        # если в первой кучке нет камней
        if stack_1 == 0:
                # берём камни из второй
                stack_2 = take(stack_2, 2)
        # то же самое для второй кучки
        elif stack_2 == 0:
                stack_1 = take(stack_1, 1)
        # если камни в кучках есть
        else:
                # выбираем случайным образом номер кучки
                choice = random.randint(1,2)
                # если выбрана первая кучка — берём камни оттуда
                if choice == 1:
                        stack_1 = take(stack_1, 1)
                # то же самое для второй кучки
                else:
                        stack_2 = take(stack_2, 2)

Запускаем игру

Последнее, что нам осталось сделать, — это запустить саму игру. Для этого на старте выводим начальное состояние кучек и запускаем бесконечный цикл. Бесконечный — для того, чтобы он работал всё время, пока в кучках есть камни. Как только они закончатся, функция result() выведет сообщение о победе и остановит игру.

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

# выводим на старте состояние кучек
result()
# запускаем бесконечный цикл
while True:
        # если ходит компьютер
	if current_player == 'Компьютер':
                # он делает свой ход
		take_computer()
                # выводим состояние кучек
		result()
                # меняем текущего игрока
		current_player = 'Игрок'
        # если ходит игрок
	else:
                # спрашиваем у него номер кучки и количество камней
                select = int(input('Выберите кучку: '))
                taken = int(input('Сколько камней забрать: '))
                # если игрок выбрал первую кучку — берём оттуда
                if select == 1:
                        stack_1 = take(stack_1, 1)
                # а если вторую — то оттуда
                else:
                        stack_2 = take(stack_2, 2)
                # выводим состояние кучек
                result()
                # меняем текущего игрока
                current_player = 'Компьютер'
Пишем игру Ним на Python
Игра работает: кучки уменьшаются, игроки ходят по очереди, всё выглядит красиво

# подключаем модуль для работы со случайными числами
import random
# формируем кучки с камнями
stack_1 = random.randint(2,10)
stack_2 = random.randint(2,10)
# выбранная кучка
select = 0
# сколько камней взяли из кучки
taken = 0
# кто делает текущий ход
current_player = 'Игрок'

# забираем камни из кучки
def take(stack, num):
        # будем менять глобальную переменную внутри функции
	global taken
        # если ход делает компьютер
	if current_player == 'Компьютер':
                # случайным образом выбираем количество камней, которые заберём из кучки
		taken = random.randint(1,stack)
        # выводим состояние текущего хода
	print(current_player + ' взял ' + str(taken) + ' камней из ' + str(num) + ' кучки')
        # уменьшаем кучку на выбранное количество камней
	stack = stack - taken
        # возвращаем количество камней в кучке
	return(stack)

# выводим текущее состояние кучек
def result():
        # сообщаем, сколько камней в каждой кучке
        print('Камней в 1 кучке: ' + str(stack_1))
        print('Камней в 2 кучке: ' + str(stack_2))
        # если в кучках не осталось камней — текущий игрок победил
        if stack_1 == 0 and stack_2 == 0:
                print('Игра окончена, победил ' + current_player)
                # выходим из программы
                exit()
	
# ход компьютера
def take_computer():
        # будем работать с глобальными переменными
        global stack_1, stack_2, current_player
        # если в первой кучке нет камней
        if stack_1 == 0:
                # берём камни из второй
                stack_2 = take(stack_2, 2)
        # то же самое для второй кучки
        elif stack_2 == 0:
                stack_1 = take(stack_1, 1)
        # если камни в кучках есть
        else:
                # выбираем случайным образом номер кучки
                choice = random.randint(1,2)
                # если выбрана первая кучка — берём камни оттуда
                if choice == 1:
                        stack_1 = take(stack_1, 1)
                # то же самое для второй кучки
                else:
                        stack_2 = take(stack_2, 2)

# выводим на старте состояние кучек
result()
# запускаем бесконечный цикл
while True:
        # если ходит компьютер
	if current_player == 'Компьютер':
                # он делает свой ход
		take_computer()
                # выводим состояние кучек
		result()
                # меняем текущего игрока
		current_player = 'Игрок'
        # если ходит игрок
	else:
                # спрашиваем у него номер кучки и количество камней
                select = int(input('Выберите кучку: '))
                taken = int(input('Сколько камней забрать: '))
                # если игрок выбрал первую кучку — берём оттуда
                if select == 1:
                        stack_1 = take(stack_1, 1)
                # а если вторую — то оттуда
                else:
                        stack_2 = take(stack_2, 2)
                # выводим состояние кучек
                result()
                # меняем текущего игрока
                current_player = 'Компьютер'

Что дальше

Кажется, что программа получилась большой, но на самом деле в ней хватает недочётов:

  • Нет проверки на номер кучки: игрок может ввести число 3, и программа остановится с ошибкой.
  • То же самое — с количеством камней, которые берёт игрок. При этом у компьютера с этим всё в порядке, он не может взять больше, чем там есть.
  • Нет проверки на выигрышность хода — и это самое главное.

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

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Виталий Вебер

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Вам может быть интересно
easy