# -*- coding: utf-8 -*-
"""
Copyright François Durand
fradurand@gmail.com
This file is part of Hanabython.
Hanabython is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Hanabython is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Hanabython. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from hanabython.Modules.Colored import Colored
from hanabython.Modules.StringUtils import uncolor
from hanabython.Modules.Configuration import Configuration
from hanabython.Modules.Color import Color
from hanabython.Modules.Card import Card
[docs]class Board(Colored):
"""
The board (cards successfully played) in a game of Hanabi.
:param cfg: the configuration of the game.
:var np.array altitude: indicates the highest card played in each color.
E.g. with color ``c`` of index ``i``, ``altitude[i]`` is the value
of the highest card played in color ``c``. The correspondence between
colors and indexes is the one provided by :attr:`cfg`.
>>> from hanabython import Configuration
>>> board = Board(Configuration.STANDARD)
>>> print(board.altitude)
[0 0 0 0 0]
"""
def __init__(self, cfg: Configuration):
self.cfg = cfg
self.altitude = np.zeros(self.cfg.n_colors, dtype=int) # type: np.array
def __repr__(self) -> str:
return '<Board: %s>' % self.str_compact()
[docs] def colored(self) -> str:
return self.colored_fixed_space()
[docs] def str_compact(self) -> str:
"""
Convert to string in "compact" layout.
:return: a representation of the board.
>>> from hanabython import Configuration
>>> board = Board(Configuration.STANDARD)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board.str_compact())
G1 G2 Y1 Y2 Y3 Y4 Y5
"""
return uncolor(self.colored_compact())
[docs] def colored_compact(self) -> str:
"""
Colored version of :meth:`str_compact`.
"""
if self.score == 0:
return 'No card on the board yet'
return ' '.join([
c.color_str(self._str_one_color(i, c))
for i, c in enumerate(self.cfg.colors)
if self.altitude[i] > 0
])
[docs] def str_fixed_space(self) -> str:
"""
Convert to string in "fixed-space" layout.
:return: a representation of the board.
>>> from hanabython import Configuration
>>> board = Board(Configuration.STANDARD)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board.str_fixed_space())
B - G 1 2 R - W - Y 1 2 3 4 5
"""
return uncolor(self.colored_fixed_space())
[docs] def colored_fixed_space(self) -> str:
"""
Colored version of :meth:`str_fixed_space`.
"""
length = 1 + 2 * self.cfg.n_values
return ' '.join([
c.color_str(self._str_one_color_factorized(i, c).ljust(length))
for i, c in enumerate(self.cfg.colors)
])
[docs] def str_multi_line(self) -> str:
"""
Convert to string in "multi-line" layout.
:return: a representation of the board.
>>> from hanabython import Configuration
>>> board = Board(Configuration.STANDARD)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board.str_multi_line())
-
G1 G2
-
-
Y1 Y2 Y3 Y4 Y5
"""
return uncolor(self.colored_multi_line())
[docs] def colored_multi_line(self) -> str:
"""
Colored version of :meth:`str_multi_line`.
"""
return '\n'.join([
c.color_str(self._str_one_color(i, c))
for i, c in enumerate(self.cfg.colors)
])
[docs] def str_multi_line_compact(self) -> str:
"""
Convert to string in "compact multi-line" layout.
:return: a representation of the board.
>>> from hanabython import Configuration
>>> board = Board(Configuration.STANDARD)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board.str_multi_line_compact())
G1 G2
Y1 Y2 Y3 Y4 Y5
"""
return uncolor(self.colored_multi_line_compact())
[docs] def colored_multi_line_compact(self) -> str:
"""
Colored version of :meth:`str_multi_line_compact`.
"""
if self.score == 0:
return 'No card on the board yet'
return '\n'.join([
c.color_str(self._str_one_color(i, c))
for i, c in enumerate(self.cfg.colors)
if self.altitude[i] > 0
])
# noinspection PyProtectedMember
def _str_one_color(self, i: int, c: Color) -> str:
"""
Convert one color to string.
:param i: index of the color.
:param c: the color.
:return: a representation of the cards played in this color.
>>> from hanabython import Configuration
>>> cfg = Configuration.STANDARD
>>> board = Board(cfg)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board._str_one_color(i=4, c=cfg.colors[4]))
Y1 Y2 Y3 Y4 Y5
"""
if self.altitude[i] == 0:
return '-'
return ' '.join([
str(Card(c, j)) for j in range(1, self.altitude[i] + 1)
])
# noinspection PyProtectedMember
def _str_one_color_factorized(self, i: int, c: Color) -> str:
"""
Same as :meth:`_str_one_color`, but with the color symbol only once.
:param i: index of the color.
:param c: the color.
:return: a representation of the cards played in this color.
>>> from hanabython import Configuration
>>> cfg = Configuration.STANDARD
>>> board = Board(cfg)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board._str_one_color_factorized(i=4, c=cfg.colors[4]))
Y 1 2 3 4 5
"""
if self.altitude[i] == 0:
return c.symbol + ' -'
return c.symbol + ' ' + ' '.join([
str(j) for j in range(1, self.altitude[i] + 1)
])
[docs] def try_to_play(self, card: Card) -> bool:
"""
Try to play a card on the board.
:param card: the card.
:return: True if the card is successfully played on the board, False
otherwise (i.e. if it leads to a misfire).
>>> from hanabython import Configuration, Card
>>> board = Board(Configuration.STANDARD)
>>> for s in ['B1', 'B2', 'Y1', 'Y3', 'B1']:
... board.try_to_play(Card(s))
True
True
True
False
False
>>> print(board.str_compact())
B1 B2 Y1
"""
i_c = self.cfg.i_from_c(card.c)
if card.v == self.altitude[i_c] + 1:
self.altitude[i_c] += 1
return True
else:
return False
@property
def score(self) -> int:
"""
The current score.
:return: the sum of the altitudes reached in all colors.
>>> from hanabython import Configuration
>>> cfg = Configuration.STANDARD
>>> board = Board(cfg)
>>> for s in ['G1', 'G2', 'Y1', 'Y2', 'Y3', 'Y4', 'Y5']:
... _ = board.try_to_play(Card(s))
>>> print(board.score)
7
"""
return int(np.sum(self.altitude))
if __name__ == '__main__':
my_board = Board(Configuration.W_MULTICOLOR_SHORT)
for s in ['B1', 'B2', 'M1', 'M3', 'B1']:
print('Try to play %s: ' % s, my_board.try_to_play(Card(s)))
print()
my_board.test_str()
print('\nAll layout styles (colored version):')
print('Compact: ')
print(my_board.colored_compact())
print('\nFixed space: ')
print(my_board.colored_fixed_space())
print('\nMulti-line compact: ')
print(my_board.colored_multi_line_compact())
print('\nMulti-line: ')
print(my_board.colored_multi_line())
import doctest
doctest.testmod()