От ливадите до Лас Вегас (и назад)

Предадени решения

Краен срок:
15.11.2022 18:00
Точки:
10

Срокът за предаване на решения е отминал

Небезизвестен факт е, че меката на хазарта е Лас Вегас.
Фактът, че името "Лас Вегас" значи "ливади", обаче, не е чак толкова известен.
Това, което ги свързва, е, че стандартното тесте карти за игра е еднакво полулярно, както в Лас Вегас, така и по селските ливади. Само игрите (в повечето случаи) са различни.

С днешната задача ще направим един мост между двата свята и ще напишем код, който е еднакво полезен и на двата фронта.

Целта на задачата е да подготвим гръбнака на онлайн игра, която позволява да се играят Белот и Покер.
Защо точно онлайн? Едва ли има значение за решението на задачата ви, но както е казал един велик български шоумен:
Най-хубавите сайтове са в Интернет!

Напишете следните класове.

  1. Клас Card, който представлява една единствена карта от тестето. Класът има следната спецификация.

    Аргументи, които класът очаква при инициализиране:

    1. suit - боя на картата от тип str - 'clubs', 'diamonds', 'hearts', 'spades';
    2. face - стойност на картата от тип str - '2',...'9', '10', 'J', 'Q', 'K' или 'A'.

    Методи, които класът имплементира:

    1. get_suit() - връща стойността на боята на картата в тип str;
    2. get_face() - връща стойността на лицето на картата в тип str.
  2. Клас Deck, който представлява стандартно тесте от 52 карти (т.е. без жокерите), използвайки класа Card. Класът има следната спецификация.

    Аргументи, които класът очаква при инициализиране:

    1. face_filter - optional аргумент, който представлява списък от дефиниция на карти, подадени като list от str. Ако при инициализация класът получи този аргумент, инстанцираното тесте трябва да съдържа само картите, които са подадени в списъка. Списъкът съдържа само вида карта, без нейната боя - всички карти от този тип, независимо от боята, остават в игра.
      Например face_filter = ['K', 'A'] значи, че искам да играя игра, в която участват само Поп и Асо от всички 4 бои.

    Методи, които класът имплементира:

    1. cut() - цепи тестето, т.е. взима една или повече карти отгоре и ги слага отдолу;
    2. shuffle() - разбърква картите в тестето;
    3. get_cards() - връща списък от карти като list от Card - картите, които в момента са налични за игра, в реда, в който са подредени в момента.
  3. Клас Player - представлява играч на произволна игра. Класът има следната спецификация.

    Аргументи, които класът очаква при инициализиране:

    • Няма

    Методи, които класът имплементира:

    1. get_cards() - връща списък от карти като list от Card, които играчът притежава в момента.
  4. Клас Game - представлява игра с карти. Класът има следната спецификация.

    Аргументи, които класът очаква при инициализиране:

    1. number_of_players - брой играчи, нужни за играта в тип int;
    2. dealing_direction - посока на раздаване на картите в тип str ('ltr' или 'rtl', съответно left-to-right /от ляво надясно/ и right-to-left /от дясно наляво/);
    3. dealing_instructions - инструкции за раздаване като tuple от int. Всеки елемент от tuple-а дефинира брой карти при едно завъртане около играчите. Може да има произволен брой завъртания, стига картите да стигат (приемете, че винаги ще стигат и няма да тестваме за невъзможни инструкции).
      Например dealing_instructions((1, 2)) значи, че първо ще раздам по една карта на всеки играч (в реда, дефиниран от dealing_direction), след което ще раздам по още две карти в същия ред.

    Методи, които класът имплементира:

    1. get_players() - връща списък с играчи като list от Player, които играят играта, в ред от ляво надясно, започвайки от произволен играч;
    2. prepare_deck() - събира раздадените карти от играчите, разбърква тестето и го цепи;
    3. deal(<Player>) - раздава тесте карти на играчите, в нужната посока, започвайки от този, подаден като аргумент;
    4. get_deck() - връща тестето от карти в тип Deck, което се използва за играта. При вече раздадени карти се очаква обектът, който връщате, да съдържа само тези карти, които не са били раздадени на играчите.
  5. Клас Belot - представлява игра с карти на Белот. Класът се възползва от дефинираните по-горе класове и инициализира игра със следните спецификации:

    1. брой играчи - 4;
    2. посока на раздаване - от ляво надясно;
    3. инструкции за раздаване - първо по 2 на всеки играч, после по още 3 и накрая по още 3;
    4. нужни карти - 7, 8, 9, 10, J, Q, K, A.

    Аргументи, които класът очаква при инициализиране:

    • Няма
  6. Клас Poker - представлява игра с карти на Покер. Класът се възползва от дефинираните по-горе класове и инициализира игра със следните спецификации:

    1. брой играчи - 9;
    2. посока на раздаване - от дясно наляво;
    3. инструкции за раздаване - 1 по 1 на всеки играч, докато всички имат по 5;
    4. нужни карти - цялото тесте.

    Аргументи, които класът очаква при инициализиране:

    • Няма

FAQ

Q: Защо има толкова много get функции? Нужни ли са те за този дизайн?
A: По-скоро не, не са нужни. Нужни са, обаче, за да можем да изтестваме кода ви без да ви казваме конкретно какви имена на атрибути да използвате.

Q: Как да разбъркам тестето?
А: Все още не сме говорили за модули, така че получавате това наготово:

import random

random.shuffle(<колекция>)

Разбира се, може да използвате и други опции от random модула - разгледайте документацията и си изберете.

Q: Какво значи, че раздавам или дефинирам ред на играчите от ляво надясно или от дясно наляво?
A: За външния свят няма значение как записваш играчите си в данните и как определяш посоката. Просто се подсигури, че всички методи, които връщат информация за играчите, са консистентни. "От ляво надясно" трябва да е обратната посока на "от дясно наляво".

Q: Имам ли право да дефинирам методи и атрибути, които не са споменати в условието?
A: Да, но класът ти трябва да очаква точно тези аргументи, които сме дефинирали, за да можем да изтестваме кода. Ако например твоят клас Player очаква аргумент при инициализиране, а нашият тест се опита да инстанцира Player без подаден аргумент, ние ще получим грешка, а ти фейлващ тест, т.е. по-малко точки.

Q: Може ли да дадете пример за това как клиентът би използвал това?
A: Може:

belot = Belot()
players = belot.get_players()
belot.prepare_deck()
belot.deal(players[0])
# Play the game, argue about the rules, curse, fight, argue about who should deal the cards during second round...
belot.prepare_deck()
belot.deal(players[1])

Примерен тест

>>>ТОЧНО ЕТО ТУК<<<