HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

ASCII-fication of playing cards

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
playingcardsasciification

Problem

I am making a small Blackjack game in Python 3. The part of the code I am unsure about is that which allows me to print nice ASCII versions of the card. ascii_version_of_card() and ascii_version_of_hidden_card().

What the program does:

I am not seeking advice on the implementation of the Card() class. Only on ascii_version_of_card() and ascii_version_of_hidden_card().

The basic idea of the code is that there are 9 lines of output, and we iterate over all of the card (input) and create the appropriate version of that line for that card. Then we add that line to a master line. In the end we have 9 master lines which are our output.

Since some of the ASCII art is rendered strangely in the browser here is a dpaste.

```
class Card(object):

card_values = {
'Ace': 11, # value of the ace is high until it needs to be low
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'10': 10,
'Jack': 10,
'Queen': 10,
'King': 10
}

def __init__(self, suit, rank):
"""
:param suit: The face of the card, e.g. Spade or Diamond
:param rank: The value of the card, e.g 3 or King
"""
self.suit = suit.capitalize()
self.rank = rank
self.points = self.card_values[rank]

def ascii_version_of_card(*cards, return_string=True):
"""
Instead of a boring text version of the card we render an ASCII image of the card.
:param cards: One or more card objects
:param return_string: By default we return the string version of the card, but the dealer hide the 1st card and we
keep it as a list so that the dealer can add a hidden card in front of the list
"""
# we will use this to prints the appropriate icons for each card
suits_name = ['Spades', 'Diamonds', 'Hearts', 'Clubs']
suits_symbols = ['♠', '♦', '♥', '♣']

# create an empty list of list, each sublist is a line
lin

Solution

I know you're not seeking advice about Card, but I feel I should point out that you don't need to inherit from object in Python 3.

I would remove the return_string option; if someone wants a list they should just call splitlines.

Your

suits_name = ['Spades', 'Diamonds', 'Hearts', 'Clubs']
suits_symbols = ['♠', '♦', '♥', '♣']


should probably be tuples:

suits_name = 'Spades', 'Diamonds', 'Hearts', 'Clubs'
suits_symbols = '♠', '♦', '♥', '♣'


but they're only ever used as a mapping:

name_to_symbol = {
    'Spades':   '♠',
    'Diamonds': '♦',
    'Hearts':   '♥',
    'Clubs':    '♣',
}


Your

# add the individual card on a line by line basis
lines[0].append('┌─────────┐')
lines[1].append('│{}{}       │'.format(rank, space))  # use two {} one for char, one for space or char
lines[2].append('│         │')
lines[3].append('│         │')
lines[4].append('│    {}    │'.format(suit))
lines[5].append('│         │')
lines[6].append('│         │')
lines[7].append('│       {}{}│'.format(space, rank))
lines[8].append('└─────────┘')


looks a bit ugly, but I can see why you did that. What might be better is making an auxillary function to stack lines of strings first:

def join_lines(strings):
    string_lines = [string.splitlines() for string in strings]
    return '\n'.join(''.join(out_line) for out_line in zip(*string_lines))


And then do:

card = (
    '┌─────────┐\n'
    '│{r}{_}       │\n'
    '│         │\n'
    '│         │\n'
    '│    {s}    │\n'
    '│         │\n'
    '│         │\n'
    '│       {_}{r}│\n'
    '└─────────┘\n'
).format(r=rank, _=space, s=suit)

card_strings.append(card)


Since the formatting of the card matters for prettiness, I'd be tempted to do:

card = (
        '┌─────────┐\n'
        '│{}       │\n'
        '│         │\n'
        '│         │\n'
        '│    {}   │\n'
        '│         │\n'
        '│         │\n'
        '│       {}│\n'
        '└─────────┘\n'
    ).format(
        format(card.rank, ' 2')
    )


Of course, since card is from a static, you can move it out to get just

card = CARD.format(
        format(rank, ' 2')
    )


This removes the need for space. You can then make the main loop a comprehension if you make a function, and stick it inside the join_lines call:

def card_to_string(card):
    # 10 is the only card with a 2-char rank abbreviation
    rank = card.rank if card.rank == '10' else card.rank[0]
    suit = name_to_symbol[card.suit]

    # add the individual card on a line by line basis
    return CARD.format(
        format(rank, ' 2')
    )

return join_lines(map(card_to_string, cards))


The calls to format can be removed by making card like:

CARD = """\
┌─────────┐
│{}       │
│         │
│         │
│    {}   │
│         │
│         │
│       {}│
└─────────┘
""".format('{rank: 2}')


and doing:

def card_to_string(card):
    # 10 is the only card with a 2-char rank abbreviation
    rank = card.rank if card.rank == '10' else card.rank[0]

    # add the individual card on a line by line basis
    return CARD.format(rank=rank, suit=name_to_symbol[card.suit])


ascii_version_of_hidden_card can then be just:

def ascii_version_of_hidden_card(*cards):
    """
    Essentially the dealers method of print ascii cards. This method hides the first card, shows it flipped over
    :param cards: A list of card objects, the first will be hidden
    :return: A string, the nice ascii version of cards
    """

    return join_lines((HIDDEN_CARD, ascii_version_of_card(*cards[1:])))


Here's the full thing:

```
CARD = """\
┌─────────┐
│{} │
│ │
│ │
│ {} │
│ │
│ │
│ {}│
└─────────┘
""".format('{rank: 2}')

HIDDEN_CARD = """\
┌─────────┐
│░░░░░░░░░│
│░░░░░░░░░│
│░░░░░░░░░│
│░░░░░░░░░│
│░░░░░░░░░│
│░░░░░░░░░│
│░░░░░░░░░│
└─────────┘
"""

def join_lines(strings):
"""
Stack strings horizontally.
This doesn't keep lines aligned unless the preceding lines have the same length.
:param strings: Strings to stack
:return: String consisting of the horizontally stacked input
"""
liness = [string.splitlines() for string in strings]
return '\n'.join(''.join(lines) for lines in zip(*liness))

def ascii_version_of_card(*cards):
"""
Instead of a boring text version of the card we render an ASCII image of the card.
:param cards: One or more card objects
:return: A string, the nice ascii version of cards
"""

# we will use this to prints the appropriate icons for each card
name_to_symbol = {
'Spades': '♠',
'Diamonds': '♦',
'Hearts': '♥',
'Clubs': '♣',
}

def card_to_string(card):
# 10 is the only card with a 2-char rank abbreviation
rank = card.rank if card.rank == '10' else card.rank[0]

# add the individual card on a line by line basis
return CARD.format(rank=rank, suit=name_to_symbol[card.

Code Snippets

suits_name = ['Spades', 'Diamonds', 'Hearts', 'Clubs']
suits_symbols = ['♠', '♦', '♥', '♣']
suits_name = 'Spades', 'Diamonds', 'Hearts', 'Clubs'
suits_symbols = '♠', '♦', '♥', '♣'
name_to_symbol = {
    'Spades':   '♠',
    'Diamonds': '♦',
    'Hearts':   '♥',
    'Clubs':    '♣',
}
# add the individual card on a line by line basis
lines[0].append('┌─────────┐')
lines[1].append('│{}{}       │'.format(rank, space))  # use two {} one for char, one for space or char
lines[2].append('│         │')
lines[3].append('│         │')
lines[4].append('│    {}    │'.format(suit))
lines[5].append('│         │')
lines[6].append('│         │')
lines[7].append('│       {}{}│'.format(space, rank))
lines[8].append('└─────────┘')
def join_lines(strings):
    string_lines = [string.splitlines() for string in strings]
    return '\n'.join(''.join(out_line) for out_line in zip(*string_lines))

Context

StackExchange Code Review Q#82103, answer score: 10

Revisions (0)

No revisions yet.