Skip to content

Latest commit

 

History

History
259 lines (186 loc) · 13.6 KB

File metadata and controls

259 lines (186 loc) · 13.6 KB

Модульное задание по OOP

Создаём консольную игру «Камень-ножницы-бумага»

Общее описание

Нам нужно создать консольную игру, в которой игрок будет выбирать свой ход из 3 вариантов (камень, ножницы, бумага) путём ввода в консоль одной из 3 цифр: 1, 2 или 3. Вымышленный соперник будет выбирать свой вариант случайно из таких же трёх вариантов.

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

Необходимые файлы и папки

  • main.py — основной скрипт, отвечает за меню и за запуск игры
  • tests.py — файл с тестами для проверки работоспособности классов и методов
  • Папка game/ — со всеми основными файлами игры. Все остальные файлы должны быть в этой папке.
  • game/__init__.py — пустой файл, необходим для корректного импорта модулей из папки
  • game/models.py — файл с описанием моделей, которые есть в игре (игрок и соперник)
  • game/settings.py — файл со всеми константами, которые могут нам понадобиться
  • game/exceptions.py — файл с необходимыми нам исключениями
  • game/game.py — файл с основной логикой игры
  • game/score.py — файл с логикой сохранения очков игры

Разберём каждый файл

models.py

Должен содержать два класса:

  • Player
  • Enemy

Player

Атрибуты:

  • name — имя игрока, задаётся пользователем через консоль
  • lives — количество жизней, берётся из константы из settings.py
  • score — очки игрока, изначально 0

Методы:

  • __init__ — для инициализации игрока, принимает только имя, назначает имя, количество жизней и очков
  • select_attack — метод для ввода атаки игроком. Вводим до тех пор, пока пользователь не введёт валидное значение (1, 2, 3), использует константы из файла settings.py
  • decrease_lives — метод, который будет вызываться, если игрок проиграл «бой», уменьшает жизни на 1. Если жизни закончились, вызывает исключение GameOver из файла exceptions.py
  • add_score — метод для начисления очков игроку

Enemy

Атрибуты:

  • lives — количество жизней, изначально зависит от уровня соперника и уровня сложности, уменьшается на 1, когда соперник проигрывает «бой»
  • level — уровень соперника, будет увеличиваться с каждым новым соперником. Изначально 1

Методы:

  • __init__ — для инициализации соперника, принимает только уровень и сложность, чтобы вычислить количество жизней, назначает количество жизней и уровень
  • select_attack — метод для случайного выбора атаки (1, 2, 3), использует константы из файла settings.py
  • decrease_lives — уменьшает жизни при проигрыше «боя», вызывает исключение EnemyDown из файла exceptions.py, если у соперника закончились жизни

game.py

Содержит класс игры Game

Game

Атрибуты:

  • player — объект игрока
  • enemy — объект соперника, при убийстве будет создан новый, с более высоким уровнем
  • mode — уровень сложности, normal или hard, содержит либо 1, либо 2, которые определены константами

Методы:

  • __init__ — принимает объект игрока и уровень сложности, создаёт первого соперника
  • create_enemy — метод для создания нового соперника
  • play — метод запуска игры. Запускает бесконечный цикл, в одной итерации которого происходит «бой». Для этого вызывает два метода: fight и handle_fight_result. Отслеживает, не произошло ли одно из исключений при вызове второго метода — GameOver или EnemyDown. При первом завершает игру и вызывает метод для записи очков, при втором создаёт нового, более сильного соперника
  • fight — метод запрашивает у пользователя и соперника атаки, из констант получает результат боя (-1, 0, 1)
  • handle_fight_result — принимает результат боя и в зависимости от результата отнимает жизни либо у игрока, либо у соперника
  • save_score — вызывает сохранение очков при помощи класса из файла score.py

exceptions.py

Классы:

  • GameOver — когда у игрока заканчиваются жизни
  • EnemyDown — когда у соперника заканчиваются жизни

settings.py

Содержит все константы.

Примерное содержание:

MODE_NORMAL = 'Normal'
MODE_HARD = 'Hard'
MODES = {'1': MODE_NORMAL,
         '2': MODE_HARD}

PLAYER_LIVES = 2
POINTS_FOR_FIGHT = 1
POINTS_FOR_KILLING = 5
MAX_RECORDS_NUMBER = 5
HARD_MODE_MULTIPLIER = 2

SCORE_FILE = 'scores.txt'

PAPER = 'Paper'
STONE = 'Stone'
SCISSORS = 'Scissors'

WIN = 1
DRAW = 0
LOSE = -1

ALLOWED_ATTACKS = {
    '1': PAPER,
    '2': STONE,
    '3': SCISSORS
}

ATTACK_PAIRS_OUTCOME = {
    (PAPER, PAPER): DRAW,
    (PAPER, STONE): WIN,
    (PAPER, SCISSORS): LOSE,
    (STONE, PAPER): LOSE,
    (STONE, STONE): DRAW,
    (STONE, SCISSORS): WIN,
    (SCISSORS, PAPER): WIN,
    (SCISSORS, STONE): LOSE,
    (SCISSORS, SCISSORS): DRAW
}

score.py

Классы:

  • ScoreHandler — класс для обработки очков
  • GameRecord — класс, содержащий записи об игроках
  • PlayerRecord — класс для хранения записи об одном игроке

ScoreHandler

Атрибуты:

  • game_record — объект класса GameRecord, туда мы будем считывать сохранённые очки и записывать таблицу с новыми
  • file_name — имя файла, откуда и куда мы записываем очки

Методы:

  • __init__ — принимает только имя файла и сохраняет его. Вызывает метод для чтения файла
  • read — метод, который будет читать файл и каждую его строку сохранять в PlayerRecord, которые будут сохраняться в GameRecord
  • save — метод, который нужен, чтобы записать новые результаты в файл (предварительно отсортировать и обрезать, если нужно)
  • display — метод для отображения очков

GameRecord

Атрибуты:

  • records — список объектов типа PlayerRecord

Методы:

  • __init__ — создаёт объект с пустым списком записей
  • add_record — метод для добавления записи об одном игроке. Должен проверять, нет ли у нас уже такого игрока, и если есть, то перезаписывать его результат. Тот же самый игрок проверяется по имени и уровню сложности (игрок может быть представлен в таблице два раза на разном уровне сложности). Можно использовать magic-метод __eq__ для поиска через in
  • prepare_records — метод для сортировки существующих результатов и обрезки до максимального количества, указанного в настройках

PlayerRecord

Атрибуты:

  • name — имя игрока
  • mode — уровень сложности
  • score — количество очков

Методы:

  • __init__ — для создания объекта принимает все три параметра
  • __gt__ — чтобы можно было отсортировать записи по очкам
  • __str__ — для удобного вывода данных

main.py

Содержит функции:

  • main — для запуска всего кода. Внутри этой функции должен быть запущен процесс выбора из трёх пунктов: запуск игры, посмотреть очки и выйти из игры (1, 2, 3)
  • play_game — вызывается, если игрок выбрал начать игру. В этой функции будет запущен процесс создания игрока, создание объекта игры и запуск самой игры
  • create_player — спросить у игрока имя и сложность, создать объект игрока с указанным именем и передать объект игрока и сложность в класс игры
  • show_scores — показать очки, используя класс ScoreHandler
  • exit_game — выйти из игры (не exit, чтобы не конфликтовать со встроенной функцией)

tests.py

Файл с модульными тестами. Используйте unittest для тестирования классов и методов.

Что нужно протестировать:

Player:

  • Создание игрока с корректным именем и начальными значениями (lives, score)
  • Метод add_score корректно увеличивает очки
  • Метод decrease_lives уменьшает жизни на 1
  • Метод decrease_lives вызывает GameOver, когда жизни заканчиваются

Enemy:

  • Создание соперника с корректным количеством жизней в зависимости от уровня и сложности
  • Метод select_attack возвращает одно из допустимых значений
  • Метод decrease_lives вызывает EnemyDown, когда жизни заканчиваются

Game:

  • Метод fight возвращает корректный результат (-1, 0, 1) для разных комбинаций атак
  • Метод create_enemy создаёт соперника с правильным уровнем

PlayerRecord:

  • Метод __gt__ корректно сравнивает записи по очкам
  • Метод __eq__ корректно сравнивает записи по имени и режиму

GameRecord:

  • Метод add_record добавляет новую запись
  • Метод add_record перезаписывает существующую запись при совпадении имени и режима
  • Метод prepare_records сортирует записи по убыванию очков и обрезает до MAX_RECORDS_NUMBER

ScoreHandler:

  • Метод read корректно читает файл и создаёт объекты PlayerRecord
  • Метод save корректно записывает данные в файл

Подсказка: для тестирования методов, которые используют input(), можно использовать unittest.mock.patch.

Пример того, как выглядит содержимое файла с очками

Name Mode Score
Vlad Normal 14
Test Hard 12
Jack Normal 12
Vlad Hard 10
Jack Hard 9