Пишем на Питоне игру в кости

webmaster

Опубликован:  2021-05-18T08:41:41.688199Z
Отредактирован:  2021-05-18T08:34:05.115352Z
7
0
0
Вы неавторизованы, рекомендую зарегистрироваться и авторизоваться.

Данная презентация описывает процесс разработки на Python3 в стандартном окружении Linux минимального эмулятора игры в кости. Под катом представлены листинги интерактивной отладки с необходимыми пояснениями, демонстрационный листинг игровой сессии и код полученной программы. Спойлеры ниже кликабельны, комментарии приветствуются.

Замечание: эта статья впервые была опубликована в блоге newbie в списках auriz.ru 5 сентября 2018 года и набрала с тех пор 1101 просмотров, поскольку тема будет актуальна всегда, а по url-адресу страницы на сервер до сих пор приходят запросы от читателей сайта, принято решение восстановить статью из архива с некоторыми эстетическими изменениями.

1. Необходимый набор инструментов

Для реализации задуманного мне понадобится:

Всё это можно найти практически в любом дистрибутиве Linux.

2. Подготовка рабочего пространства

Запускаю терминал и создаю новый каталог.

mkdir -p workspace/dice

t4zuFFI9SM.png

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

cd workspace/dice/

xhdD3ItcpK.png

Теперь, находясь в этом каталоге, создаю файл dice.py, для этого вбиваю в терминале команду:

nvim dice.py

COfAaMbHhq.png

Жму enter и оказываюсь в псевдо-окне текстового редактора, в строке состояния обращаю внимание, что открыт файл с именем dice.py.

09I5j233mn.png

NeoVim имеет два режима работы: командный режим и режим ввода текста. Чтобы ввести текст, мне необходимо перейти в режим ввода, для этого нажимаю клавишу a - команда позволяет вводить текст после курсора. Пишу в файл dice.py следующий код:

#!/usr/bin/env python3


def main():
    print('Hello, World!')


if __name__ == '__main__':
    main()

Здесь всё просто. Первой строчкой уточняю, что программа должна исполняться интерпретатором python3. Далее создаю функцию main, которая выводит на экран типичное приветствие Hello, World!, а затем посредством стандартной идиомы Питона вызываю эту функцию только в случаях, когда программа исполнена, при импорте модуля это действие осуществляться не будет.

Чтобы сохранить изменения в файл, мне необходимо перейти в командный режим NeoVim, для этого нажимаю клавишу Esc, затем клавишу : и клавишу w, в строке состояния должна отобразиться введённая команда - она сохраняет изменения в файл.

W7Fa9DhQxc.png

Ещё раз нажимаю enter - файл будет сохранён. Чтобы каждый раз не выходить из текстового редактора, запускаю ещё один терминал, в нём захожу в этот же каталог - workspace/dice. Задаю только что созданному файлу права на исполнение:

chmod u+x dice.py

E5LYceXE1z.png

Создаю ссылку на этот файл в одном из каталогов $PATH:

sudo ln -s -T ~/workspace/dice/dice.py /usr/local/bin/dice

UNChQVSvLd.png

С этого момента программу можно запускать в терминале как любую другую команду.

dice

HNAmAmAxhD.png

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

3. Определяем набор аргументов командной строки

Игра в кости в самом общем случае требует:

  1. определить игроков, хотя бы двоих;
  2. определить количество брошенных каждым игроком кубиков или количество бросков одного кубика, совершаемых каждым игроком.

Оба условия могут быть заданы посредством аргументов командной строки. Для обработки аргументов командной строки использую стандартный модуль Питона argparse. Пишу в файл программы (dice.py) следующий код:

#!/usr/bin/env python3

import argparse


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-n',
        help='the loop length (default 3)',
        dest='loop',
        action='store',
        default=3,
        type=int,
        choices=(1, 2, 3))
    parser.add_argument(
        '-p',
        nargs='+',
        help='participants, e.g. Name1 Name2 ... NameN',
        dest='play',
        action='store',
        required=True)
    return parser.parse_args()

Здесь задаю два аргумента для командной строки:

  • -n - определяет количество брошенных кубиков, либо количество бросков одного кубика совершаемых каждым игроком, аргумент числовой, может принимать значения 1, 2 или 3, имеет значение по умолчанию равное 3 и может быть опущен при вызове программы, сохраняется как int;
  • -p - определяет участников игры, которые в свою очередь определяются по именам, аргумент обязательный, имена вводятся через пробел, количество имён участников не ограничивается, сохраняется список содержащий имена - строки.

Для предварительной отладки аргументов меняю функцию main соответствующим образом:

def main():
    args = parse_args()
    print(f'Количество бросков: {args.loop}')
    print('Игроки:')
    for i in args.play:
        print('\t', i)

Сохраняю изменения в файл.

VJ0ImP22zA.png

Перемещаюсь в соседний терминал и пробую различные варианты запуска игры. Минимальная справка:

dice --help

2uSabgqFHP.png

Пробую задать участников игры:

dice -p First Second

m1EHZFBfCo.png

Умышленно ошибаюсь с аргументами:

dice -n 4 First Second

TK0xN3JiBm.png

Отлично, аргументы заработали. Далее займёмся игроками...

4. Описываем игрока

Игрок, бросающий кубики, будет описан классом. Этому классу задаю два свойства:

  • имя игрока;
  • полученную игроком сумму в результате броска кубиков.

Бросок кубиков эмулирую стандартным генератором случайных чисел. Количество кубиков задаю при инициализации класса. Полученную игроком сумму считаю посредством стандартной функции sum из набора встроенных функций Питона и записываю в соответствующее свойство класса при инициализации.

Дописываю в dice.py следующий код:

#!/usr/bin/env python3

import argparse

from random import randint


class Player:
    def __init__(self, name, loop):
        self.name = name
        self.score = sum(randint(1, 6) for _ in range(loop))


def parse_args():
     ...

Здесь я импортировал функцию randint из модуля random стандартной библиотеки, посредством которой эмулирую бросок кубиков, затем создал класс Player и определил ему метод инициализации (в народе "дандэринит"), в котором свойствам создаваемых экземпляров этого класса будут присваиваться определённые этим методом значения. Сохраняю изменения в файл.

xA3Xqld2cQ.png

Возвращаюсь в соседний терминал, запускаю интерпретатор Python3.

python3 -q

EnUMbJDN4m.png

Попробуем воспользоваться созданным только что классом.

>>> from dice import Player
>>> player = Player('Игрок', 3)
>>> player.name
'Игрок'
>>> player.score
9
>>> second_player = Player('Второй', 3)
>>> second_player.name
'Второй'
>>> second_player.score
5
>>> exit()

gp6qF6whHC.png

Отлично, игрок готов.

5. Описываем игру

Игра будет описана своим классом. При инициализации передаю этому классу список игроков, в котором каждый игрок идентифицирован именем (строкой). Если в списке только один игрок, в игру будет добавлен виртуальный игрок Machine. При инициализации формирую из полученных имён список, состоящий из экземпляров класса Player.

Дописываю в dice.py следующий код:

class Game:
    def __init__(self, names, loop):
        if len(names) == 1:
            names.append('Machine')
        self.players = [Player(name, loop) for name in names]

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

...

    def count_scores(self):
        win = max(player.score for player in self.players)
        block = max(len(i.name) for i in self.players) + len(str(win)) + 1
        for player in self.players:
            print(f'{player.name}:{player.score:>{block-len(player.name)}}')
        return [player.name for player in self.players
                    if player.score == win]

Здесь win - максимальное значение набранных игроками баллов, block - ширина блока для выравнивания результатов при выводе на экран.

Сохраняю изменения в файл.

95LwAwlzFF.png

Возвращаюсь в соседний терминал и снова запускаю интерпретатор, чтобы протестировать полученный класс и его методы:

>>> from dice import Game
>>> loop = 1
>>> game = Game(['Первый'], loop)
>>> winners = game.count_scores()
Первый:  3
Machine: 5
>>> winners
['Machine']
>>> exit()

3YBxgYoEI2.png

Отлично, в игру автоматически включен виртуальный игрок, так как при инициализации было задано имя только одного игрока.

6. Заключительный аккорд

Вся необходимая инфраструктура в программе создана, но при запуске программы игры не получается. Необходимо изменить функцию main должным образом. Ставлю эксперимент в интерпретаторе.

>>> from dice import Game
>>> loop = 1
>>> game = Game(['Первый'], loop)
>>> winners = game.count_scores()
Первый:  3
Machine: 3
>>> while True:
...    if len(winners) == 1:
...        print(f'\nCongrats {winners[0]}!')
...        break
...    else:
...        print('\nNo winner, one more throw:')
...        game = Game(winners, loop)
...        winners = game.count_scores()
...

No winner, one more throw:
Первый:  6
Machine: 1

Congrats Первый! 
>>> 

9empvEAV5p.png

Первый выиграл. Эксперимент успешный. Дописываю в функцию main соответствующие изменения:

def main():
    args = parse_args()
    game = Game(args.play, args.loop)
    winners = game.count_scores()
    while True:
        if len(winners) == 1:
            print(f'\nCongrats {winners[0]}!')
            break
        else:
            print('\nNo winner, one more throw:')
            game = Game(winners, args.loop)
            winners = game.count_scores()

Сохраняю изменения в файл.

VhK83aA8Kx.png

Попробуем запустить игру.

dice -n 3 -p Yours_truly

1fPrHYLkg5.png

Машина проиграла. Элементарный эмулятор игры в кости готов.

7. Код программы целиком

dice.py

#!/usr/bin/env python3

import argparse

from random import randint


class Player:
    def __init__(self, name, loop):
        self.name = name
        self.score = sum(randint(1, 6) for _ in range(loop))


class Game:
    def __init__(self, names, loop):
        if len(names) == 1:
            names.append('Machine')
        self.players = [Player(name, loop) for name in names]

    def count_scores(self):
        win = max(player.score for player in self.players)
        block = max(len(i.name) for i in self.players) + len(str(win)) + 1
        for player in self.players:
            print(f'{player.name}:{player.score:>{block-len(player.name)}}')
        return [player.name for player in self.players
                if player.score == win]


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-n',
        help='the loop length (default 3)',
        dest='loop',
        action='store',
        default=3,
        type=int,
        choices=(1, 2, 3))
    parser.add_argument(
        '-p',
        nargs='+',
        help='participants, e.g. Name1 Name2 ... NameN',
        dest='play',
        action='store',
        required=True)
    return parser.parse_args()


def main():
    args = parse_args()
    game = Game(args.play, args.loop)
    winners = game.count_scores()
    while True:
        if len(winners) == 1:
            print(f'\nCongrats {winners[0]}!')
            break
        else:
            print('\nNo winner, one more throw:')
            game = Game(winners, args.loop)
            winners = game.count_scores()


if __name__ == '__main__':
    main()

Всё просто... Расскажите в комментариях, какой была ваша первая программа на Питоне?

8. Важная информация для постоянных читателей

Уважаемый читатель, списки auriz.ru не имеет спонсоров и финансирования, и чтобы выжить мы можем рассчитывать только на поддержку своих постоянных читателей и их пожертвования. Если вам оказалась полезна эта или другая статья этого сайта, и вы хотите больше интересных материалов на auriz.ru, поддержите нас рублём, ваша поддержка даст нам шанс выжить, оплатить продление домена и VDS, написать и опубликовать для вас другие интересные материалы.

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

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

Метки:  newbie, python3x, linux, dice, geany, random
Комментарии: