#!/usr/bin/env python3
"""Contains redscience constants plus functions for internationalization
and versioning.
Most constants in this module are encoded as a :doc:`category.Category <category>`
or as a NamedTuple_. Each displays in the locale set via `setlang()`_
and excludes members not in the version set via `setvers()`_.
Examples::
import matplotlib.pyplot as plt
ttt=Game()
ipywidgets.Dropdown(options=sorted(Command), value=Command.QUIT)
ipywidgets.Button(
description=Command.QUIT,
tooltip=Command.QUIT.TOOLTIP,
icon=Command.QUIT.ICON,
)
fig = plt.figure(1,(
Layout.FIGURE_HEIGHT,
Layout.FIGURE_WIDTH,
))
ax = ttt.AXES(fig)
ax.scatter(
x = 1,
y = 1,
c = Color.WHITE.HEX,
marker = Marker.CIRCLE.CODE,
edgecolors = Color.BLACK.HEX,
)
setlang(")
setvers("")
print(ttt.RULES)
print(Command.QUIT)
plt.show()
.. _babel.core.Locale: http://babel.pocoo.org/en/latest/api/core.html#babel.core.Locale
.. _enum.IntEnum: https://docs.python.org/3/library/enum.html#enum.IntEnum
.. _matplotlib.axes.Axes: https://matplotlib.org/stable/api/axes_api.html#matplotlib.axes.Axes
.. _matplotlib.figure.Figure: https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure
.. _matplotlib.marker: https://matplotlib.org/stable/api/markers_api.html marker
.. _NamedTuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple
.. _numpy.array: https://numpy.org/doc/stable/reference/generated/numpy.array.html#numpy.array
.. _portion.interval.Interval: https://pypi.org/project/portion/#documentation--usage
"""
import collections
import enum
import functools
import itertools
from typing import (
Any,
Callable,
Dict,
Iterable,
List,
NamedTuple,
Optional,
Tuple,
Union,
)
import category
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
setlang = category.babelwrap.setlang
setvers = category.setvers
format_list = category.babelwrap.format_list
format_decimal = category.babelwrap.format_decimal
format_percent = category.babelwrap.format_percent
format_unit = category.babelwrap.format_unit
format_datetime = category.babelwrap.format_datetime
from_version = category.from_version
ALL = category.ALL
def _(message: str) -> str:
"""Defined for type hint, then replaced at bottom."""
return message
class _Color(NamedTuple):
STR: str
HEX: str
VERSIONS: Iterable = ALL
[docs]class Color(category.Categorized):
"""Color used in a game. E.g.::
Color.BLACK
**Attributes:**
:STR (str): A localized name. How the color prints.
:HEX (str): A hex code to communicate the color to computers.
:VERSIONS (Iterable): The versions which offer this color.
"""
# TRANSLATOR: Color of game piece as in "Move: Black circle to (2,1)"
BLACK = _Color(STR=_("black"), HEX="#000000")
# TRANSLATOR: Color of game piece as in "Move: White circle to (2,1)"
WHITE = _Color(STR=_("white"), HEX="#ffffff")
# TRANSLATOR: Color of game piece as in "Move: Pink circle to (2,1)"
PINK = _Color(STR=_("pink"), HEX="#ff81c0")
# TRANSLATOR: Color of game piece as in "Move: Yellow circle to (2,1)"
YELLOW = _Color(STR=_("yellow"), HEX="#ffff14")
# TRANSLATOR: Color of game piece as in "Move: Orange circle to (2,1)"
ORANGE = _Color(STR=_("orange"), HEX="#fdaa48")
# TRANSLATOR: Color of game piece as in "Move: Blue circle to (2,1)"
BLUE = _Color(STR=_("blue"), HEX="#95d0fc")
# TRANSLATOR: Color of game piece as in "Move: Purple circle to (2,1)"
PURPLE = _Color(STR=_("purple"), HEX="#bf77f6")
# TRANSLATOR: Color of game piece as in "Move: Green circle to (2,1)"
GREEN = _Color(STR=_("green"), HEX="#96f97b")
# TRANSLATOR: Color of game piece as in "Move: Gray circle to (2,1)"
GRAY = _Color(STR=_("gray"), HEX="#929591")
PlayerColor = category.ctg(*Color[0:4], name="PlayerColor") # type: ignore[misc]
[docs]class Layout(enum.IntEnum):
"""Layout constants. E.g.::
Layout.POINTS_PER_INCH
See enum.IntEnum_
"""
FIGURE_WIDTH = 5
FIGURE_HEIGHT = 5
POINTS_PER_INCH = 54
MARKER_MARGIN = 8
class _Command(NamedTuple):
STR: str
KEY: str
VERSIONS: Iterable = ALL
[docs]class Command(category.Categorized):
"""Command from user to the application. E.g.::
Command.NEW
**Attributes:**
:STR (str): A localized name. How the command prints.
:KEY (str): A localized shortcut key.
:VERSIONS (Iterable): The versions which offer this command.
Each command tests ``==`` to its ``KEY`` as well as to itself. For
example, if the language is English:
>>> Command.NEW == "n"
True
"""
# TRANSLATOR: This is the command to start a new game (e.g. button text).
# TRANSLATOR: This is the shortcut key to start a new game.
# It should match the key listed in the prompt.
NEW = _Command(STR=_("Play New"), KEY=_("n"))
# TRANSLATOR: This is the command to end the application (e.g. button text).
# TRANSLATOR: This is the shortcut key to end the application.
# It should match the key listed in the prompt.
QUIT = _Command(STR=_("Quit"), KEY=_("q"))
# TRANSLATOR: This is the command to reverse last user input (e.g. button text).
# TRANSLATOR: This is the shortcut key to back-up by one user input.
# It should match the key listed in the prompt.
UNDO = _Command(STR=_("Back"), KEY=_("z"))
def __eq__(self: "Command", other) -> bool:
return (
other.lower() == self.KEY
if type(other) is str
else category.Categorized.__eq__(self, other)
)
def __ne__(self: "Command", other) -> bool:
return not self.__eq__(other)
class _PlayersOption(NamedTuple):
STR: str
NUM: int
VERSIONS: Iterable = ALL
[docs]class PlayersOption(category.Categorized):
"""Category of game by number/relationship of players. E.g.::
PlayersOption.TWO
**Attributes:**
:STR (str): A localized name. How the option prints.
:NUM (int): The number of regular players.
:VERSIONS (Iterable): The versions which offer this option.
"""
# TRANSLATOR: Category to describe games with two regular players
TWO = _PlayersOption(STR=_("2-Player"), NUM=2)
# TRANSLATOR: Category to describe games with three regular players
THREE = _PlayersOption(STR=_("3-Player"), NUM=3)
class _Marker(NamedTuple):
STR: str
CODE: str
VERSIONS: Iterable = ALL
[docs]class Marker(category.Categorized):
"""The shape of a game piece. E.g.::
Marker.CIRCLE
**Attributes:**
:STR (str): A localized name. How the narker prints.
:CODE (str): A matplotlib.marker_.
:VERSIONS (Iterable): The versions which offer this marker.
"""
# TRANSLATOR: Description of the pyplot marker
CIRCLE = _Marker(STR=_("circle"), CODE="o")
class _StalemateOption(NamedTuple):
STR: str
VERSIONS: Iterable = ALL
[docs]class StalemateOption(category.Categorized):
"""How statemate ends a game. E.g.::
StalemateOption.DRAW
**Attributes:**
:STR (str): A localized name. How the option prints.
:VERSIONS (Iterable): The versions which offer this option.
"""
# TRANSLATOR: A rule that the game ends in a draw if there is a stalemate
DRAW = _StalemateOption(STR=_("stalemate draws"))
class _ColorOption(NamedTuple):
STR: str
VERSIONS: Iterable = ALL
[docs]class ColorOption(category.Categorized):
"""Category of game by how it treats colors. E.g.::
ColorOption.ASSIGNED
**Attributes:**
:STR (str): A localized name. How the option prints.
:VERSIONS (Iterable): The versions which offer this marker.
"""
# TRANSLATOR: A rule that each player is assigned their own unique color
ASSIGNED = _ColorOption(
STR=_("Assigned Colors"),
)
class _BoardOption(NamedTuple):
STR: str
AX: Callable[[matplotlib.figure.Figure, tuple], matplotlib.axes.Axes]
VERSIONS: Iterable = ALL
[docs]class BoardOption(category.Categorized):
"""Category of game board. E.g.::
BoardOption.HASH
**BoardValue Attributes**:
:STR (str): A localized name. How the option prints.
:AX (Callable): Function to return matplotlib.axes.Axes_, given
a matplotlib.figure.Figure_ and tuple of dimensions.
:VERSIONS (Iterable): The versions which offer this option.
"""
def _hash_board(fig: matplotlib.figure.Figure, dims: tuple) -> matplotlib.axes.Axes:
# Tic-Tac-Toe board
rows, cols = dims[0], dims[1]
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0, 0])
ax.patch.set_alpha(0.0)
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.xaxis.set_ticks_position("none")
ax.xaxis.set_ticklabels([""])
ax.yaxis.set_ticks_position("none")
ax.yaxis.set_ticklabels([""])
ax.grid(True)
ax.xaxis.set_ticks(np.arange(0.5, cols + 0.5, 1))
ax.yaxis.set_ticks(np.arange(0.5, rows + 0.5, 1))
plt.xlim(cols + 0.5, 0.5)
plt.ylim(0.5, rows + 0.5)
return ax
# TRANSLATOR: The type of board used for Tic-Tac-Toe, as in "Played on a hash (3,3)"
HASH = _BoardOption(STR=_("a hash"), AX=_hash_board)
class _DirectionsValue(NamedTuple):
STR: str
CALL: Callable[[int], tuple]
VERSIONS: Iterable = ALL
[docs]class Directions(category.Categorized):
"""Categories of ways in which to move or build in square-tiled space. E.g::
>>> Directions.DIAGONAL(2)
((1,1), (1,-1), (-1,1), (-1,-1))
Args:
dimensions (int): The number of dimensions in the space
Returns:
tuple: Of relative coordinates (each a numpy.array_ of int)
**Attributes**:
:STR (str): A localized name. How the Directions prints.
:CALL (Callable): The bound method that yields the tuples.
:VERSIONS (Iterable): The versions which offer this option.
Tip:
To get cache info::
Directions.DIAGONAL.call.cache_info()
"""
@functools.lru_cache(maxsize=8)
def _any_direction(self: "Directions", dimensions: int):
zero = tuple([0] * dimensions)
unfiltered = itertools.product([1, 0, -1], repeat=dimensions)
return tuple([np.array(x) for x in unfiltered if x != zero])
@functools.lru_cache(maxsize=8)
def _orthogonal(self: "Directions", dimensions: int):
return tuple(np.identity(dimensions, dtype=int))
@functools.lru_cache(maxsize=8)
def _diagonal(self: "Directions", dimensions: int):
orthogonals = list(map(tuple, self.orthogonal(dimensions)))
return tuple(
[x for x in self.any_direction(dimensions) if tuple(x) not in orthogonals]
)
@functools.lru_cache(maxsize=8)
def _knight(self: "Directions", dimensions: int):
spots = []
for spot in itertools.product([2, 1, 0, -1, -2], repeat=dimensions):
inring = (spot.count(2) + spot.count(-2)) == 1
orthogonal = spot.count(0) >= (dimensions - 1)
if inring and not orthogonal:
spots.append(np.array(spot))
return tuple(spots)
# TRANSLATOR: Category of directions in which chess queen can move
ANY = _DirectionsValue(STR=_("any direction"), CALL=_any_direction)
# TRANSLATOR: Category of directions in which chess bishop can move
DIAGONAL = _DirectionsValue(STR=_("diagonal"), CALL=_diagonal)
# TRANSLATOR: Category of directions in which chess rook can move
ORTHOGONAL = _DirectionsValue(STR=_("orthogonal"), CALL=_orthogonal)
# TRANSLATOR: Category of directions in which chess knight can move
KNIGHT = _DirectionsValue(STR=_("knight move"), CALL=_knight)
class _OutcomeValue(NamedTuple):
STR: str
FORMAT: str
CALL: Callable[["Outcome", List[str]], str]
VERSIONS: Iterable = ALL
[docs]class Outcome(category.Categorized):
"""Function to apply localized formatting to strings. E.g:
>>> winners = (DefaultName.PLAYER_ONE, DefaultName.PLAYER_THREE)
>>> Outcome.VICTORY(players=winners)
'Victory: Player 1 and Player 3'
Args:
**kwargs: a str for each bookmark in the STR
Returns:
str: The localized string.
**OutcomeValue Attributes**:
:STR (str): A localized name. How the Directions prints.
:CALL (Callable): The bound method that yields the str.
:FORMAT (str): The formatted string for the ``CALL``.
:VERSIONS (Iterable): The versions which offer this option.
"""
def _formatter(self: "Outcome", players: List[str]) -> str:
return self.FORMAT.format(players=format_list(players))
# TRANSLATOR: Labels {winners} as the winner(s) of a game
# e.g. "Victory: Player 1 and Player 3"
VICTORY = _OutcomeValue(
STR=_("Victory"),
FORMAT=_("Victory: {players}"),
CALL=_formatter,
)
class _CheckOption(NamedTuple):
STR: str
VERSIONS: Iterable = ALL
PATTERN: Optional[str] = None
DIRECTIONS: Optional[_DirectionsValue] = None
OUTCOME: Optional[_OutcomeValue] = None
[docs]class CheckOption(category.Categorized):
"""Game rules checked at the end of each move. E.g.::
CheckOption.THREE_SAME_COLOR_IN_ROW_WINS
**Attributes**:
:STR (str): A localized name. How the option prints.
:VERSIONS (Iterable): The versions which offer this option.
:PATTERN (str): If specified, a type of pattern to be checked. Default
to None.
:DIRECTIONS (Directions_): If specified, directions in which to check
the pattern. Default to None.
:OUTCOME (Outcome_): If specified, the outcome if the check triggers.
Default to None.
"""
# TRANSLATOR: Game rule to award the win to any player that aranges three
# pieces of the same color in a row
THREE_SAME_COLOR_IN_ROW_WINS = _CheckOption(
STR=_("first 3-same-color-in-a-row wins"),
PATTERN="CCC",
DIRECTIONS=Directions.ANY,
OUTCOME=Outcome.VICTORY,
)
[docs]def ntversions(self: Iterable) -> Iterable:
"""The versions which offer a NamedTuple. E.g.::
@property
def VERSIONS(self) -> Iterable:
return ntversions(self)
Note:
This function assumes that each attribute that can contain values
specific to a version has a VERSIONS attribute listing valid versions
as a portion.interval.Interval_
"""
versions = ALL
for attr in self:
if hasattr(attr, "VERSIONS"):
versions = versions & attr.VERSIONS # type: ignore[attr-defined]
return versions
[docs]class PieceRules(NamedTuple):
"""Rules for a type of piece in a game. E.g.::
PieceRules(INITIAL_RESERVES=(5,4))
**Attributes:**
:INITIAL_RESERVES (Tuple[int, ...]): Specifies the number of each
color in initial reserves, e.g. (5, 4) means start with 5 of the
first color and 4 of the second color in reserve.
"""
INITIAL_RESERVES: Tuple[int, ...]
@property
def RESERVES_STR(self: "PieceRules") -> str:
"""A constant localized str describing initial reserves for the
piece. E.g.:
>>> piece.RESERVES_STR
'5 black and 4 white start in reserve'
"""
by_color = []
for index in range(len(self.INITIAL_RESERVES)):
# TRANSLATOR: Part of a list of amounts of game pieces.
# e.g. "5 black" in "5 black and 4 white start in reserve"
by_color.append(
_("{number} {color}").format(
number=self.INITIAL_RESERVES[index],
color=str(Color[index]).lower(), # type: ignore[misc]
),
)
# TRANSLATOR: The rule for how many to have in reserve when a game
# begins.. E.g. "5 black and 2 white start in reserve"
return _("{list} start in reserve").format(list=format_list(by_color))
@property
def STRS(self: "PieceRules") -> Tuple[str, ...]:
"""Get tuple of strings describing the rules for the piece. E.g.::
>>> piece.STRS
('No movement', 'No power', '5 black and 4 white start in reserve')
"""
lines = [self.RESERVES_STR]
return tuple(lines)
def __str__(self: "PieceRules") -> str:
return "/n".join(self.STRS)
@property
def VERSIONS(self) -> Iterable:
"""The versions which offer this piece. E.g.::
>>> piece.VERSIONS
(-inf, +inf)
"""
return ntversions(self)
[docs]class Game(NamedTuple):
"""A game definition. E.g.::
Game() # To use all defaults (i.e. Tic-Tac-Toe)
**Attributes:**
:PLAYERS (PlayersOption_): If specified, determines the number/
relationship of players. Default is 2-Player.
:COLOR (ColorOption_): If specified, determines the significance
of colors. Default is Assigned Colors.
:BOARD (BoardOption_): If specified, determines the type of board.
Default is hash.
:DIMENSIONS (Tuple[int, ...]): If specified, determines the
dimensions of the board. Default is (3,3).
:PIECES (Tuple[PieceRules_, ...]): If specified, determines
piece-specific rules. Default is to have only one type of piece
(circle) with 5 black and 4 white circles starting in reserve.
:MOVE_CHECKS (Tuple[CheckOption_, ...]): If specified, determines
which rules are checked at the end of each move. Can be None.
Default is to award the win to any player that gets three of the
same color in a row.
:STALEMATE (StalemateOption_): If specified, determines the result
of stalemate. Default is that stalemate results in a draw.
"""
PLAYERS: _PlayersOption = PlayersOption.TWO
COLOR: _ColorOption = ColorOption.ASSIGNED
BOARD: _BoardOption = BoardOption.HASH
DIMENSIONS: Tuple[int, ...] = (3, 3)
PIECES: Tuple[PieceRules, ...] = (PieceRules(INITIAL_RESERVES=(5, 4)),)
MOVE_CHECKS: Union[Tuple[()], Tuple[_CheckOption, ...]] = (
CheckOption.THREE_SAME_COLOR_IN_ROW_WINS,
)
STALEMATE: _StalemateOption = StalemateOption.DRAW
@property
def STRS(self) -> Tuple[str, ...]:
"""A Tuple of localized strings describing the game. E.g:
>>> Game().STRS
('Played on a hash (3, 3)', '2-Player', 'Assigned Colors',
'Circle: 5 black and 4 white start in reserve', 'First
3-same-color-in-a-row wins', 'Stalemate draws')
"""
lines = []
# TRANSLATOR: Line defining a game board e.g. "Played on hash (3,3)" for
# Tic-Tac-Toe where {board} is "hash" and {dimensions} is "(3,3)"
lines.append(
_("Played on {board} {dimensions}").format(
dimensions=str(self.DIMENSIONS),
board=str(self.BOARD),
)
)
lines.append(str(self.PLAYERS))
lines.append(str(self.COLOR))
for index in range(len(self.PIECES)):
# TRANSLATOR: Line defining rules for a type of game piece/card
# e.g. "Circle: Immobile, 5 black and 4 white start in reserve"
lines.append(
_("{shape}: {rules}")
.format(
shape=str(Marker[index]), # type: ignore[misc]
rules=format_list(list(self.PIECES[index].STRS)),
)
.capitalize()
)
for rule in self.MOVE_CHECKS:
lines.append(str(rule).capitalize())
lines.append(str(self.STALEMATE).capitalize())
return tuple(lines)
@property
def VERSIONS(self) -> Iterable:
"""The (Tuple) versions which offer this Game. E.g.:
>>> Game().VERSIONS
(-inf,+inf)
"""
return ntversions(self)
@property
def RULES(self) -> str:
"""A localized str of check and stalemate rules. E.g:
>>>game().RULES
'Rules: First 3-same-color-in-a-row wins and stalemate draws'
"""
rule_list: List[Union[_CheckOption, _StalemateOption]] = list(self.MOVE_CHECKS)
rule_list.append(self.STALEMATE)
# TRANSLATOR: Labels {rules} as rules of a game
# e.g. "Rules: First 3-same-color-in-a-row wins and stalemate draws"
return _("Rules: {rules}").format(
rules=str(format_list(rule_list)).capitalize(),
)
def __str__(self: "Game") -> str:
return "\n".join(self.STRS)
[docs] def AXES(self, fig: matplotlib.figure.Figure) -> matplotlib.axes.Axes:
"""The board in terms of matplotlib E.g.::
import matplotlib.pyplot as plt
figure = plt.figure(1,(
Layout.FIGURE_HEIGHT,
Layout.FIGURE_WIDTH,
))
game.AXES(fig=figure)
plt.show()
Args:
fig (matplotlib.figure.Figure_): The Figure in which the Axes
will appear
Returns:
matplotlib.axes.Axes_: A framework in which to place pieces
"""
return self.BOARD.AX(fig, self.DIMENSIONS)
@property
def MARKER_SIZE(self) -> float:
"""The (int) size for markers in this game. E.g.:
>>> Game().MARKER_SIZE
6724
"""
figure_max = max(Layout.FIGURE_HEIGHT, Layout.FIGURE_WIDTH)
spot_size = figure_max / max(self.DIMENSIONS)
return (spot_size * Layout.POINTS_PER_INCH - Layout.MARKER_MARGIN) ** 2
class _DefaultName(NamedTuple):
STR: str
VERSIONS: Iterable = ALL
[docs]class DefaultName(category.Categorized):
"""Default names for players. E.g.::
DefaultName.PLAYER_ONE
**Attributes:**
:STR (str): A localized name. How the name prints.
:VERSIONS (Iterable): The versions which offer this name.
"""
# TRANSLATOR: Default name for a player in a game (independent of order)
PLAYER_ONE = _DefaultName(STR=_("Player 1"))
# TRANSLATOR: Default name for a player in a game (independent of order)
PLAYER_TWO = _DefaultName(STR=_("Player 2"))
# TRANSLATOR: Default name for a player in a game (independent of order)
PLAYER_THREE = _DefaultName(STR=_("Player 3"), VERSIONS=from_version("1.5.0"))
# TRANSLATOR: Default name for a player in a game (independent of order)
PLAYER_FOUR = _DefaultName(STR=_("Player 4"), VERSIONS=from_version("1.5.0"))
class _PlayerType(NamedTuple):
STR: str
VERSIONS: Iterable = ALL
[docs]class PlayerType(category.Categorized):
"""Type of player. E.g.::
PlayerType.HUMAN
**Attributes:**
:STR (str): A localized name. How the type prints.
:VERSIONS (Iterable): The versions which offer this PlayerType.
"""
# TRANSLATOR: An unnamed human player in a game
ANONYMOUS = _PlayerType(STR=_("Anonymous"))
# TRANSLATOR: A named human player in a game
HUMAN = _PlayerType(STR=_("Human"), VERSIONS=from_version("1.2.0"))
[docs]class Player(NamedTuple):
"""The constant parts of a player. E.g.::
Player() # To use all defaults (i.e. human)
**Attributes:**
:TYPE (PlayerType_): If specified, determines the type. Default is Human.
"""
TYPE: _PlayerType = PlayerType.HUMAN
@property
def VERSIONS(self) -> Iterable:
"""The versions in which this player can be selected."""
return ntversions(self)
class _Placement(NamedTuple):
TO: Tuple[int, ...]
COLOR: _Color = Color.BLACK
MARKER: _Marker = Marker.CIRCLE
def __str__(self) -> str:
# TRANSLATOR: Names a placement in a game e.g. "Black circle to (1,2)"
return (
_("{color} {shape} to {destination}")
.format(color=self.COLOR, shape=self.MARKER, destination=self.TO)
.capitalize()
)
@property
def VERSIONS(self) -> Iterable:
"""The versions which offer this placement. E.g.::
placement.VERSIONS
"""
return ntversions(self)
class _Jump(NamedTuple):
FROM: Tuple[int, ...]
TO: Tuple[int, ...]
def __str__(self) -> str:
# TRANSLATOR: Names a move in a game e.g. "(2,3) to (1,2)
return _("{origin} to {destination}").format(
origin=self.FROM, destination=self.TO
)
@property
def VERSIONS(self) -> Iterable:
"""The versions which offer this jump"""
return ntversions(self)
class _Move(NamedTuple):
STR: str
CALL: Any = None
VERSIONS: Iterable = ALL
[docs]class Move(category.Categorized):
"""A type of move in a game. Prints localized str. Examples::
Move.PASS
Move.PLACE(COLOR=Color.WHITE, MARKER=Marker.CIRCLE, TO=(2,3))
Move.JUMP(FROM=(1,1), TO=(2,3))
**Attributes:**
:STR (str): A localized name. How the move prints.
:VERSIONS (Iterable): The versions which offer this Move.
:TO (Tuple[int,...]): *Only for PLACE and JUMP.* destination
coordinates.
:COLOR (PlayerColor_): *Only for PLACE.* Color to be placed.
Default is black
:MARKER (Marker_): *Only for PLACE.* Shape to be placed.
Default is circle.
:FROM (Tuple[int, ...]): *Only for JUMP.* Origin coordinates.
"""
# TRANSLATOR: Move in a game when the player forfeits their turn
PASS = _Move(STR=_("Pass"))
# TRANSLATOR: Move in a game when the player adds a piece or card
PLACE = _Move(STR=_("Place from reserves"), CALL=_Placement)
# TRANSLATOR: Move in a game from one spot to another
JUMP = _Move(
STR=_("Reposition"),
CALL=_Jump,
VERSIONS=from_version("1.5.0"),
)
# TRANSLATOR: Move in a game when the player offers a voluntary draw
OFFER = _Move(
STR=_("Offer to draw"),
VERSIONS=from_version("1.5.0"),
)
# TRANSLATOR: Move in a game when the player accepts an offer to draw
AGREE = _Move(
STR=_("Agree to draw"),
VERSIONS=from_version("1.5.0"),
)
# TRANSLATOR: Move in a game when the player rejects an offer to draw
REFUSE = _Move(
STR=_("Refuse to draw"),
VERSIONS=from_version("1.5.0"),
)
# Delay this until after all constants are declared; otherwise the strings will
# get translated upon declaration, and that will prevent us from changing
# language later (since we will have lost the original strings)
_ = category.babelwrap._
# defaults['misc']['prompt'] = _('What\'s your move? (\'#,#\' or q/z/n for quit/undo/new game)')
# defaults['misc']['illegal'] = _('That is not a legal option.')
# defaults['misc']['error'] = _('Error in program')
# defaults['misc']['draw'] = _('Draw')
# defaults['shape'] = { '__type__' : 'shape',
# 'mobility' : _('no movement'),
# 'power' : _('no power'),
# 'initial_reserves' : [5,4] }
# defaults['phase'] = { '__type__': 'phase',
# 'pass' : _('no voluntary pass') }
# defaults['rules'] = {
# '__type__': 'rules',
# 'name': _('a game'),
# 'players_type': _('2 players'),
# 'colors_type': _('assigned colors'),
# 'dimensions': [3, 3],
# 'shapes': [ defaults['shape'] ],
# 'board_type': _('hash'),
# 'initial_state': [ ],
# 'phases': [ defaults['phase'] ],
# 'turn_density': _('1 piece/turn'),
# 'draw_option': _('no voluntary draw'),
# 'stalemate_result': _('stalemate draws'),
# 'move_checks': [ _('first 3-same-color-in-a-row wins') ]
# }
# class FormatAs(Categorized):
# """Function to apply localized formatting to strings. E.g:
# FormatAs.TURN(player="Player 1")
# Args:
# **kwargs: a string for each bookmark in the str
# Returns:
# The localized formated string.
# """
# def formatter(self, *args, **kwargs):
# return self.str.format(*args, **kwargs)
# TRANSLATOR: Labels {player} as having the turn in a game
# e.g. "Turn: Player 1"
# TURN = collections.namedtuple("FormatValue", "STR CALL")(_("Turn: {player}"), formatter)
# # TRANSLATOR: Labels {winners} as the winner(s) of a game
# # e.g. "Victory: Player 1 and Player 3"
# VICTORY = collections.namedtuple("FormatValue", "STR CALL")(_("Victory: {winners}"), formatter)