Search code examples
rubyoopasciiplaying-cards

How to draw card objects side by side vs stacked on top of each other in terminal


I am making an OO twenty one game. I have a Card class and a Deck class, the deck is made up of card objects. When a player shows their hand I call the to_s method on a card object which I represent in ascii characters. This all works fine except that the players hand prints out one card on top of the other. Wondering how I would go about printing the whole hand side by side. I have searched online and cannot find anything other than to use print instead of puts, but that does not resolve my issue. Thank you in advance for any advice you may have.

    class Card
  attr_reader :value
  def initialize(suit, value)
    @suit = suit
    @value = value
  end

  def to_s
    """
    +-----+
    |#{@value}    |
    |     |
    |  #{@suit}  |
    |     |
    |    #{@value}|
    +-----+
    """
  end
end

Sample output:

Your Hand:

    +-----+
    |Q    |
    |     |
    |  C  |
    |     |
    |    Q|
    +-----+

    +-----+
    |K    |
    |     |
    |  S  |
    |     |
    |    K|
    +-----+
    Your total is 20

Solution

  • Let me start by saying that there's no easy solution for what you're trying to do. Terminals are designed to print text row by row, not column by column. And once something is printed to the console, you generally can't go back and modify it. Given that, you have two options:

    1) Use ncurses (or a similar library): ncurses is a library for creating "gui" applications in the terminal. The library treats the terminal as a grid of cells, letting the developer specify each character in each cell at any given moment. This gives the developer tons of power, but it also forces you to manually add "traditional" terminal functionality (ie receiving user input and printing rows of output).

    2) Buffer your string and print it all at once: even though the terminal can only print strings of text row by row, there's nothing stopping you from taking a bunch of cards and rearranging their strings manually so they can be printed properly. It seemed like an interesting programming task, so I went ahead and gave it a shot:

    def display_hand cards
      strings = cards.map { |c| c.to_s.each_line.to_a }
      first, *rest = *strings
      side_by_side = first.zip *rest
      side_by_side.each do |row|
        row.each { |s| print s.chomp }
        print "\n"
      end
    end
    
    display_hand [Card.new("A", 1), Card.new("B", 2), Card.new("C", 3)]
    

    This code takes an array of cards, gathers their string representations into arrays and then uses zip to group them by row. So essentially instead of printing card 1, card 2, card 3, etc., it prints row 1 of all cards, row 2 of all cards, etc.

    Hope this helps!