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

avm

Опубликован:  2018-02-11T07:28:52.199803Z
Отредактирован:  2018-02-11T07:26:40.190386Z
3200
Описание процесса создания на Python3 минимального эмулятора игры в кости в стандартном окружении Linux. Описание содержит листинги интерактивной отладки с необходимыми пояснениями, демонстрационный листинг игровой сессии и код полученной программы. Спойлеры ниже кликабельны.

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

Мне понадобится:

  • рабочее окружение Linux;
  • установленный и настроенный Python3;
  • любой текстовый редактор с подсветкой синтаксиса;
  • если работа в иксах, то любой эмулятор терминала.

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

Создаю в домашней папке рабочий каталог, в которому буду совершать все действия при проектировании приложения, для этого запускаю эмулятор терминала и даю команду:

sadmin@debian:~$ mkdir -p workspace/dice
sadmin@debian:~$ 

Захожу в созданный каталог:

sadmin@debian:~$ cd workspace/dice/
sadmin@debian:~/workspace/dice$ 

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

#!/usr/bin/env python3


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


if __name__ == '__main__':
    main()

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

Сохраняю файл. Для удобства запуска программы задаю этому файлу права на исполнение:

sadmin@debian:~/workspace/dice$ chmod u+x dice.py
sadmin@debian:~/workspace/dice$ 

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

sadmin@debian:~/workspace/dice$ sudo ln -s -T ~/workspace/dice/dice.py /usr/local/bin/dice
sadmin@debian:~/workspace/dice$

Теперь программу можно запускать:

sadmin@debian:~/workspace/dice$ dice
Hello, World!
sadmin@debian:~/workspace/dice$ 

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

kate-session

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

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

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

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

#!/usr/bin/env python3

import argparse


def enter():
    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 = enter()
    print('Количество бросков: {}'.format(args.loop))
    print('Игроки:')
    for i in args.play:
        print('\t', i)

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

sadmin@debian:~/workspace/dice$ dice --help
usage: dice [-h] [-n {1,2,3}] -p PLAY [PLAY ...]

optional arguments:
  -h, --help          show this help message and exit
  -n {1,2,3}          the loop length (default 3)
  -p PLAY [PLAY ...]  participants, e.g. Name1 Name2 ... NameN
sadmin@debian:~/workspace/dice$ 

Задаю участников игры:

sadmin@debian:~/workspace/dice$ dice -p First Second
Количество бросков: 3
Игроки:
     First
     Second
sadmin@debian:~/workspace/dice$ 

Задаю количество бросков кубика:

sadmin@debian:~/workspace/dice$ dice -n 2 -p First Second
Количество бросков: 2
Игроки:
     First
     Second
sadmin@debian:~/workspace/dice$ 

Ошибаюсь с аргументами:

sadmin@debian:~/workspace/dice$ dice -n 4 First Second
usage: dice [-h] [-n {1,2,3}] -p PLAY [PLAY ...]
dice: error: argument -n: invalid choice: 4 (choose from 1, 2, 3)
sadmin@debian:~/workspace/dice$ 

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

kate-session

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 enter():
     ...

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

Теперь лезу в интерпретатор и смотрю на результат трудов праведных:

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

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

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("{0}:{1:>{2}}"
                  .format(player.name, player.score, block-len(player.name)))
        return [player.name for player in self.players if player.score == win]

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

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

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

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

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

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

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

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

Congrats Machine!
>>>

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

Дописываю в функцию main соответствующие изменения:

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

И запускаю игру:

sadmin@debian:~/workspace/dice$ dice -n 3 -p Yours_truly
Yours_truly: 12
Machine:     12

No winner, one more throw:
Yours_truly: 15
Machine:     11

Congrats Yours_truly!
sadmin@debian:~/workspace/dice$ 

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

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("{0}:{1:>{2}}"
                  .format(player.name, player.score, block-len(player.name)))
        return [player.name for player in self.players if player.score == win]


def enter():
    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 = enter()
    game = Game(args.play, args.loop)
    winners = game.count_scores()
    while True:
        if len(winners) == 1:
            print('\nCongrats {}!'.format(winners[0]))
            break
        else:
            print('\nNo winner, one more throw:')
            game = Game(winners, args.loop)
            winners = game.count_scores()


if __name__ == '__main__':
    main()
 
Осталось: 108
Метки:  python3x, random, dice
Комментарии: