Printing variables in a row horizontally

            import random
             import os
             import time

class Card:
    def __init__(self,value,suit):
        self.cost = value
        self.value = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'][value-1]
        self.suit = '♥♦♣♠'[suit]

        
    def show(self):
        print('┌───────┐')
        print(f'| {self.value:<2}    |')
        print('|       |')
        print(f'|   {self.suit}   |')
        print('|       |')
        print(f'|    {self.value:>2} |')
        print('└───────┘') 

    def price(self):
        if self.cost >= 10:
            return 10
        elif self.cost == 1:
            return 11
        return self.cost

class Deck:
    def __init__(self):
        self.cards = []

    def generate(self):
        for i in range(1, 14):
            for j in range(4):
                self.cards.append(Card(i, j))
    
    def draw(self, iteration):
        cards = []
        for i in range(iteration):
            card = random.choice(self.cards)
            self.cards.remove(card)
            cards.append(card)
        return cards

    def count(self):
        return len(self.cards)

class Player:
    def __init__(self, isDealer, deck):
        self.cards = []
        self.isDealer = isDealer
        self.deck = deck
        self.score = 0

    def hit(self):
        self.cards.extend(self.deck.draw(1))
        self.check_score()
        if self.score > 21:
            return 1
        return 0

    def deal(self):
        self.cards.extend(self.deck.draw(2))
        self.check_score()
        if self.score == 21:
            return 1
        return 0

    def check_score(self):
        a_counter = 0
        self.score = 0
        for card in self.cards:
            if card.price() == 11:
                a_counter += 1
            self.score += card.price()

        while a_counter != 0 and self.score > 21:
            a_counter -= 1
            self.score -= 10
        return self.score

    def show(self):
        if self.isDealer: 
            print("Dealer's Cards: ")
            time.sleep(1)
        else:
            print("Player's Cards: ")
            time.sleep(1)

        for i in self.cards:
            i.show()

        print("Score: " + str(self.score))
        time.sleep(1)

class Blackjack:
    def __init__(self):
        self.deck = Deck()
        self.deck.generate()
        self.player = Player(False, self.deck)
        self.dealer = Player(True, self.deck)

    def play(self):
        os.system('clear')
        p_status = self.player.deal()
        d_status = self.dealer.deal()

        self.player.show()

        if p_status == 1:
            print("You got Blackjack! Congratulations!")
            if d_status == 1:
                print("You Both got Blackjack! It's a push. (Tie)")
            return 1

        cmd = ""
        while cmd != "s":
            bust = 0
            cmd = input("(h)it or (s)tand? ")

            if cmd == "h":
                bust = self.player.hit()
                self.player.show()
            if bust == 1:
                print("You busted. Sorry! Good Game Though.")
                return 1
        print("\n")
        self.dealer.show()
        if d_status == 1:
            print("Dealer got Blackjack! Sorry! Better luck next time.")
            return 1

        while self.dealer.check_score() < 17:
            if self.dealer.hit() == 1:
                self.dealer.show()
                print("Dealer busted. You Win! Congratulations!")
                return 1
            self.dealer.show()

        if self.dealer.check_score() == self.player.check_score():
            print("It's a Push (Tie). Better luck next time!")
        elif self.dealer.check_score() > self.player.check_score():
            print("Dealer wins. Sorry! Good Game Though.")
        elif self.dealer.check_score() < self.player.check_score():
            print("You win! Congratulations!")

b = Blackjack()
b.play() #users 

my question is : when I run the script it displays the cards from top to bottom. How can I get it to display the cards in a single row left to right?
I have tried using end="" and sep="" and also """ """ when showing cards but to no avail.

Any help or guidance on this issue is greatly appreciated.
Thank you.

cogiz
python 3.7 user

Neat project, Steve. That’s a lot of code, though. Do you have a question about a specific part of it? Can you present a Minimal Working Example? (A minimal example will also help you experiment with options separate from the complexities of the rest of your code. This is sometimes called “toy code”; something to play with and work things out.)

Thank you for putting your code between backticks. Try adding python to the first set of backticks, like this:
```python
<your code here>
```

I think you’ll like the result.

EDIT: I see that your question got fenced by the backticks and should be set free:

An example always goes a VERY long way to showing the situation. You would like the cards side by side, right? That’s going to take some fancy string collation–probably in your Player.Show() method, but it’s not too bad since all rendered cards have the same height. At first blush, it looks like perhaps a two-dimensional List[] application. I don’t have time right now to put any detailed ideas together but this looks like a challenge that Steve D’Aprano would relish–and would probably come up with something brilliant. Let’s see who picks it up…

┌───────┐
| 5     |
|       |
|   ♥   |
|       |
|     5 |
└───────┘
┌───────┐
| 8     |
|       |
|   ♥   |
|       |
|     8 |
└───────┘

IDEA 1: You can probably use the previous line escape character to control the cursor and move the print() output “cursor” backward up to the top of the card. A StackEchange post about it is HERE.
You’ll still need to skip to the right past the cards that are already rendered, of course.
Example:

print("This line")
print("Next line")
print("\033[FMy text overwriting the previous line.")

This will be tedious and not at all Pythonic. The List[] collation is much more appealing.

You’ve got:

class Card:
    [...]
    def show(self):
        print('┌───────┐')
        print(f'| {self.value:<2}    |')
        print('|       |')
        print(f'|   {self.suit}   |')
        print('|       |')
        print(f'|    {self.value:>2} |')
        print('└───────┘')

and ask:

my question is : when I run the script it displays the cards from top
to bottom. How can I get it to display the cards in a single row left
to right?

I’m presuming you want the rectangular show() above, side by side?

As written, that is tricky because it directly prints several lines of
text.

Thinking about this, I suspect you might be best off doing:

  • write a method to just return a list of those strings you’re printing
    above
  • rewrite `show() above to get those strings and print them just as it
    does now
  • write a show_several(cards) class method to print cards side by
    side, by getting the lists of strings for each card, then printinging
    each string side by side i.e. the first of each list, then the second,
    etc.

Then you could just go:

print(*card_lines, sep="  ")

where card_lines is a list of the _n_th string from each of the cards
in a loop.

Cheers,
Cameron Simpson cs@cskk.id.au

Hi Steve,

Thanks for posting your code, but you dump about 150 lines of code that are not relevant to your question.

You are trying to print something like this:

┌───────┐ ┌───────┐

> 5     | | Q     |

>       > >       >

>   ♥   | |   ♥   |

>       > >       >

>     5 | |     Q |

└───────┘ └───────┘

but the two cards are printed below each other. Correct?

Let’s cut this down to a basic example.


def show_card(n):

    print("[ %d ]" % n)

    print("[ H ]")

You want to print two cards next to each other:

[ 5 ] [ 6 ]

[ H ] [ H ]

instead of one on top of each other. You can add the fancy stuff back later.

One way is to use an ANSI escape code to move the cursor position. You can see this as an example. Not that ANSI escape codes may not be supported on Windows.

Another way is with a library like Colorama.

A third way is to use the curses library, but that is Linux/Unix only.

But the most basic way that will work on any system is not to print your cards one at a time, but to assemble the entire hand before printing it.

So instead of calling print() for each line in each card separately, build up an entire hand in one go, then print the lot.

hand = []

# First row.

hand.append('[ 5 ]')

hand.append(' ')

hand.append('[ 6 ]')

hand.append('\n')  # Newline makes a new row

# Second row.

hand.append('[ H ] [ H ]')

print(''.join(hand))

Lots of different ways to handle this. Just remember that the secret is to assemble a string representing the whole hand first, then print it, rather than one card at a time.

It has been literally decades since I drove or flew a console’s cursor around (over 4) but it’s evidently easier than I thought. The \033[<L>;<C>H code sequence lets you specify a Line and Column. Here are some cursor control codes that will put you in good stead to create the output you’re after. As a further code improvement afterward, I strongly recommend the list approach to build a “hand” and then render it via a series of print() statements (preferably a single print() looped).

FROM this page: Cursor Movement

- Position the Cursor:
  \033[<L>;<C>H
     Or
  \033[<L>;<C>f
  puts the cursor at line L and column C.
- Move the cursor up N lines:
  \033[<N>A
- Move the cursor down N lines:
  \033[<N>B
- Move the cursor forward N columns:
  \033[<N>C
- Move the cursor backward N columns:
  \033[<N>D

- Clear the screen, move to (0,0):
  \033[2J
- Erase to end of line:
  \033[K

I only tested the ‘UP’ code for my first post, but it’s the same one in the list above.
CAVEAT: I ran the code snippet below in VS Code and it appears to work fine. HOWEVER, the Python 3.8 interpreter console does not recognize the cursor control codes. Running the ~.py program in a CMD console doesn’t work, either. As long as you only plan to run it in an environment that can interpret the codes, you’ll be fine. This scenario is obviously not for general release and depends on your use intentions. The control codes will be problematic in a cross-platform scenario, to say the least, but they’re fine–and fun–to play with in VS Code.

TIP: If you clear the screen before rendering each hand, you’ll always be able to always start rendering the hand at [0,0] (top left). This might look clean and professional but will, more importantly, save you some major headaches with dead-reckoning from an arbitrary cursor position (the cursor’s “current” position at any given point in the program.

Here’s an example snippet:

print("\033[0;0HLook Ma! I'm overwriting the top line starting from screen origin!!")

Here’s some toy code that shows the syntax of feeding variables to the cursor position command code. The only real trick is using the fString and embedding the variable names in curlies {}.

from random import randint
while True:
    L = randint(0,15)
    C = randint(0,55)
    print(f"\033[{L};{C}H{randint(0,9)}")
    print(f"\033[15;0H")
    input("Press[Enter]")
    print("\033[2J")

Here’s a solution that works for any number of cards in a hand. I changed the max score of ‘21’ to ‘999’ to allow a larger hand than anyone is likely to have and stay under 21. The entire edit is in the player.show() method.

Instead of iterating through the cards and rendering them one by one, we iterate through the cards and render the entire hand line by line.
EDIT: These cards are square on my laptop. Discourse mobile isn’t rendering with monospace for some reason.

#Player's Cards: 
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ 
| 7     | | 9     | | 4     | | 3     | | 2     | | 2     | | 7     | | 5     |
|       | |       | |       | |       | |       | |       | |       | |       |
|   ♠   | |   ♣   | |   ♥   | |   ♦   | |   ♣   | |   ♥   | |   ♥   | |   ♣   |
|       | |       | |       | |       | |       | |       | |       | |       |
|     7 | |     9 | |     4 | |     3 | |     2 | |     2 | |     7 | |     5 |
└───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘
Score: 39

        #for i in self.cards:
        #    i.show()
        handSize = self.cards.__len__()     #LJP<>
        print('┌───────┐ ' * handSize)      #LJP<> NOTE: Added a space at end of base string.
        for i in self.cards:
            print(f'| {i.value:<2}    | ', end='')
        print('\n'+'|       | ' * handSize)
        for i in self.cards:
            print(f'|   {i.suit}   | ', end='')
        print('\n'+'|       | ' * handSize)
        for i in self.cards:
            print(f'|    {i.value:>2} | ', end='')
        print('\n'+'└───────┘ ' * handSize)

This problem, like many, can and should be handled by preformatting the hand and printing in one go, as others have already explained well.

However, just in case you do actually need to do “cursor driving”, I suggest a library like Rich (which is now used by pip, Twine and other popular tools), which takes care of all the nitty gritty details and OS compatibility for you and avoids you having to re-invent the wheel; rather, you can just focus on building a nice-looking interface. More or less the equivalent or using an existing, proven GUI library rather than trying to draw every UI element to the screen using low-level draw calls.

Your solution is exactly what I was trying to achieve. It worked like a charm. Thank you very much.
I will mark this a s solved (as soon as I figure out how to do that).
in the mean time : SOLVED!

#for i in self.cards:
# i.show()
handSize = self.cards.len() #LJP<>
print(‘┌───────┐ ’ * handSize) #LJP<> NOTE: Added a space at end of base string.
for i in self.cards:
print(f’| {i.value:<2} | ‘, end=’’)
print(’\n’+’| | ’ * handSize)
for i in self.cards:
print(f’| {i.suit} | ‘, end=’’)
print(’\n’+’| | ’ * handSize)
for i in self.cards:
print(f’| {i.value:>2} | ‘, end=’’)
print(’\n’+'└───────┘ ’ * handSize)

cogiz
python 3.7 and Peppermint Linux user.

Don’t know if you checked my response in the forum but this is the solution I was looking for and it worked like a charm. Thank you very much.

Not sure yet how to mark this as solved in the forum but for now I consider it to be SOLVED.

Thanks again.
cogiz
Peppermint Linux and Python 3.7 user

Now that your proximate problem is resolved, here are some things to think about to simplify and improve your code—each is not only directly relevant to what you’ve written, but illustrates good techniques that will apply to a wide variety of programming you do.

Use helpful variable names

If you want other people (or you, in a few days, weeks or months time) to be able to understand your code, it really helps to have concise but descriptive variable names. i or idx is commonly used in Python and most programming languages for a loop index, a number that increments once each iteration of a loop, so using it to represent a card is both cryptic and potentially confusing. Instead, give it a more appropriate name, like, well, card!

for card in self.cards:
    print(card.value)

Also, in Python, standard convention is to use snake_case for variable (and attribute, method and function) names rather than camelCase, while UpperCamelCase is reserved for classes (as you have done).

Avoid lots of print() finagling

While you can use with print() with end="" as you do, it leads to more verbose and less efficient code, since you have to call print() a bunch and manually keep track of line breaks, which is easy to mess up. I suggest you make life easier on yourself by tweaking your design to add each line to a list of lines and then use '\n'.join(output_lines) and print them all at once.

output_lines = []
output_lines.append('┌───────┐ ' * hand_size)
# Add other card-specific lines to list
output_lines.append('└───────┘ ' * hand_size)
print('\n'.join(output_lines))

Take advantage of comprehensions

Now that we are building up a list before printing it, we can use list comprehensions to build up our individual lines in turn, which is both cleaner, shorter and more efficient than using for loops. A list comprehension works like this:

a_list = [do_something(item) for item in items]

So for example, to build up a line with the card value, instead of

value_line = ''
for card in self.cards:
    value_line += f'| {card.value:<2} | '
output_lines.append(value_line)

we can do

card_values = [f' | {card.value:<2} | ' for card in cards]
output_lines.append(''.join(card_values))

Use __len__ and len() properly

You should avoid calling dunder attributes and methods (those with Double UNDERscores around their names, like __len__()) directly, and instead use the generic functions they correspond to (in this case, the builtin len() function. So instead (with Pythonic variable naming), just do

hand_size = len(self.cards)

But wait, there’s more! For your own custom classes like Deck, you can implement appropriate dunder methods on them so the standard functions work as expected. So, instead of

class Deck:
    def count(self):
        return len(self.cards)

you can do

class Deck:
    def __len__(self):
        return len(self.cards)

Then, you and your users don’t need to remember what custom method is responsible for the deck length and call it, you can just call the normal len()

deck = Deck()
print(f"There are {len(deck)} cards in the deck")

One bonus of this approach is checking if the deck is empty is easier; instead of doing

if deck.count() != 0:
    print("The deck is not empty!")

you can just do

if deck:
    print("The deck is not empty!")
SIDENOTE: Expand to view

This particularly shines when dealing with multiple objects of different types that each have a length, so instead of doing (LBYL)

def print_length(obj):
    if isinstance(obj, list):
        length = len(obj)
    elif isinstance(obj, Deck):
        length = obj.count()
    elif isinstance(obj, Hand):
        length = len(Hand.cards)
    # Etc.
    print(f"The object has {length} items.")

or (EAFP)

def print_length(obj):
    try:
        length = len(obj)
    except TypeError:
        try:
            length = obj.count()
        except AttributeError:
            # Etc.
    print(f"The object has {length} items.")

you can simply do

def print_length(obj):
    print(f"The object has {len(obj)} items.")

Return bools for status and avoid magic numbers

In your code above, instead of returning one of two different arbitrary integer values to signal whether the score is over 21 when hitting or dealing and checking the for the exact value as a magic number,

       # In Player.deal()
       if self.score > 21:
            return 1
        return 0

p_status = self.player.deal()
if p_status == 1:  # Do something

Just return and check the boolean directly—its much simpler, clearer and more robust:

       # In Player.deal()
       return self.score > 21:

busted = self.player.deal()
if busted:  # Do something

Avoid duplicate bust checking every move

There’s still an issue, though—right now, every move that could change the player’s score (hit(), deal(), etc) checks for a bust, which leads to lot of duplicate code and the potential for mistakes. What if you also want to add more moves from real Blackjack, like Double Down or Split? You’ll need to duplicate that code too, and the calling code that checks it. Instead, move that check to its own method, or just do it directly in the caller.

Use a property to always keep your score up to date

But there’s still duplication here, and worse potential for bugs—these methods still all have to call check_score to update the current score. If you forget somewhere, or try to access the score directly while it is being updated, you’ve got a silent and often hard to track down bug. This also means that it duplicates the data in your cards list, which is a recipe for trouble since the two can get out of sync, which is another big bug magnet.

Instead, you want to have your score updated dynamically whenever it is accessed based on the data in cards, to ensure it is always up to date. But how to do that? Well, you could have a getter method, like your existing check_score(), which does that, and eliminate the score attribute to avoid having to constantly update it and the risk it gets out of date. But its awfully convenient to be able to access it by attribute, since its just data after all…

Well, in Python, you can have the best of both worlds using properties. To create one, you use the @property decorator, and then whenever you access the attribute, the value is computed dynamically. And best of all, any core that currently accesses score doesn’t need to change. We just remove check_score() from everywhere, and replace the score attriute with a score property:

class Player:
    def hit(self):
        self.cards.extend(self.deck.draw(1))

    @property
    def score(self):
        score = sum(min(10, card.cost) for card in self.cards)
        aces = len([card for card in self.cards if card.value == 'A'])
        return score + 10 if max(0, (21 - score) // 10) and aces else score

player.hit()
if player.score > 21: # Etc

Now, there’s no need to have or call check_score anymore; your code that uses Player can just check the score itself whenever it needs it.

System calls and basic courtesy

  • Use the modern subprocess.run() instead of the legacy os.system(); its more secure, much more powerful and avoids many “gotchas” with os.system()
  • Its considered very rude and disruptive to clear the user’s terminal scrollback, especially without asking and as soon as you run the program, because it may wantonly delete tens of thousands of lines of potentially scrollback not “owned” by your program, and there’s no, ahem, clear reason to do so. I’d be furious if a program did this, especailly one as trivial as this.
  • Furthermore, it won’t even work on non-POSIX shells and may produce unpredictable results in some environments.
1 Like

There’s no such thing here per-say, but you could edit the title if you want (or I can for you, if you prefer).

Glad you liked the solution, Steve. This looked like a hobby project (perhaps to focus on Class and Method knowledge), so I didn’t do the full Monty on best practices. One great thing about python is that you can put a working prototype together and then refactor it in various ways to suit your needs, whether that’s adding skills and knowledge incrementally, speed improvement, user interface design, making the code more ‘Pythonic’, or whatever.

Discourse has a Solved function [EXAMPLE HERE] but it has to be enabled. I personally don’t think it should be enabled, but each user’s area has a page for Solved topic tracking, so it’s probably another way to inceease one’s Trust Level. I’ve checked in with Discourse and the user page being visible while Solved is turned off seems to be an oversight/bug.

We should move any further discussion about best practices or the Solved function to a new thread.

1 Like