"""Game dices, coins, cards and other items
"""
import random
from typing import Optional, NoReturn, Any, cast
from pydantic import PositiveInt, NonNegativeInt, ConstrainedInt
from bgameb.base import BaseItem
from bgameb.errors import StuffDefineError
[docs]class Step(BaseItem):
"""Game steps or turns
..
Attr:
priority (NonNegativeInt): priority queue number. Default to 0.
"""
priority: NonNegativeInt = 0
def __eq__(self, other: 'Step') -> bool: # type: ignore[override]
return self.priority == other.priority
def __lt__(self, other: 'Step') -> bool:
return self.priority < other.priority
def __le__(self, other: 'Step') -> bool:
return self.priority <= other.priority
def __ne__(self, other: 'Step') -> bool: # type: ignore[override]
return self.priority != other.priority
def __gt__(self, other: 'Step') -> bool:
return self.priority > other.priority
def __ge__(self, other: 'Step') -> bool:
return self.priority >= other.priority
[docs]class Sides(ConstrainedInt):
"""Int subtipe to define sides of dices
..
Attr:
gt (int): greate than 1 constraint.
"""
gt = 1
[docs]class Dice(BaseItem):
"""Rolling or tossed objects, like dices or coins.
.. code-block::
:caption: Example:
dice = Dice(id='coin', sides=2, mapping={1: 'this', 2: 'that'})
..
Attr:
count (PositiveInt): count of dices. Default to 1.
sides (Sides): sides of dice or coin. Default to 2.
mapping (dict[PositiveInt, Any]):
optional mapping of roll result. Mapping must define
values for each side.
last roll (list[PositiveInt]), optional:
last roll values.
last roll mapped (list[Any]), optional:
last mapped roll values.
_range (list[PositiveInt]): range of roll, started from 1.
Raises:
StuffDefineError: mapping keys is not equal of roll range.
"""
count: PositiveInt = 1
sides: Sides = cast(Sides, 2)
mapping: dict[PositiveInt, Any] = {}
last_roll: list[PositiveInt] = []
last_roll_mapped: list[Any] = []
_range: list[PositiveInt] = []
def __init__(self, **data):
super().__init__(**data)
self._range = list(range(1, self.sides + 1))
if self.mapping and set(self.mapping.keys()) ^ set(self._range):
raise StuffDefineError(
message='Mapping must define values for each side.',
logger=self._logger
)
def __eq__(self, other: 'Dice') -> bool: # type: ignore[override]
return self.sides == other.sides
def __lt__(self, other: 'Dice') -> bool:
return self.sides < other.sides
def __le__(self, other: 'Dice') -> bool:
return self.sides <= other.sides
def __ne__(self, other: 'Dice') -> bool: # type: ignore[override]
return self.sides != other.sides
def __gt__(self, other: 'Dice') -> bool:
return self.sides > other.sides
def __ge__(self, other: 'Dice') -> bool:
return self.sides >= other.sides
[docs] def roll(self) -> list[PositiveInt]:
"""Roll and return result
Returns:
list[PositiveInt]: result of roll
"""
self.last_roll = [
random.choices(self._range, k=1)[0] for _
in list(range(self.count))
]
return self.last_roll
[docs] def roll_mapped(self) -> list[Any]:
"""Roll and return mapped result
Returns:
list[Any]: result of roll
"""
self.last_roll_mapped = [
self.mapping[roll] for roll in self.roll()
if self.mapping.get(roll)
]
return self.last_roll_mapped
[docs]class Card(BaseItem):
"""Card objects
..
Attr:
count (PositiveInt): count of cards. Default to 1.
is_revealed (bool): is card oppened. Default to False.
is_active (bool): is card tapped. Default to False.
side (str, optional): the side of tap. Default to None.
.. code-block::
:caption: Example:
card = Card(id='unique_card')
card.tap(side='left')
"""
count: PositiveInt = 1
is_revealed: bool = False
is_active: bool = True
side: Optional[str] = None
[docs] def flip(self) -> 'Card':
"""Face up or face down the card regardles of it condition
Returns:
Card
"""
if self.is_revealed:
self.is_revealed = False
self._logger.debug('Card face down.')
else:
self.is_revealed = True
self._logger.debug('Card face up.')
return self
[docs] def open(self) -> 'Card':
"""Face up the card
Returns:
Card
"""
self.is_revealed = True
self._logger.debug('Card face up.')
return self
[docs] def hide(self) -> 'Card':
"""Face down the card
Returns:
Card
"""
self.is_revealed = False
self._logger.debug('Card face down.')
return self
[docs] def tap(self, side='right') -> 'Card':
"""Tap the card to the given side
Args:
side (str, optional): side to tap. Defaults to 'right'.
Returns:
Card
"""
self.is_active = False
self.side = side
self._logger.debug(f'Card taped to side {side}.')
return self
[docs] def untap(self) -> 'Card':
"""Untap the card
Returns:
Card
"""
self.is_active = True
self.side = None
self._logger.debug('Card untaped. Side set to None.')
return self
[docs] def alter(self) -> NoReturn:
"""Many cards have alter views. For example
card can have main view, that apply most time of the game
and second view, that apply only if card played as
that alternative. For ease of understanding, consider that
different views of the same card are not related directly
to each other.
"""
raise NotImplementedError