Bit pattern to console output This function will draw a card in the console

vector&lt;string&gt; draw_card(const Carta&amp; carta){
//The class Carta has two members, carta.value and carta.seme
//both are integers representing the card value or suit
//the card is represented on 9 lines where the second will be the
//card name and suite, while the middle will show a pattern
/*
  |-----------|
  |Asso picche|
  |           |
  |     *     |
  |    * *    |
  |   *****   |
  |  *     *  |
  | *       * |
  |           |
*/

    //card opening and closing line
    string card_delimiter = "|-----------|";

    // vector representing the card
    vector&lt;string&gt; v;

    v.push_back(card_delimiter);

    // construct the second line, so that it will fit different cards names
    // and suites
    string nome_seme = create_semi(carta.seme);
    string nome_valore = create_nome_valore(carta.valore);
    string init_string = "|           |";

    //characters in the init_string will be subsittuted according to the string
    //nome_seme and then after a space nome_valore
    uint valore_offset = 1;
    uint seme_offset = nome_valore.size() + valore_offset + 1;

    for(uint i = 0; i &lt; init_string.size(); ++i){

        if(i &gt;= valore_offset &amp;&amp; i &lt; nome_valore.size() + valore_offset){
            init_string[i] = nome_valore[i - valore_offset];
        }

        if(i &gt;= seme_offset &amp;&amp; i &lt;= nome_seme.size() + seme_offset - 1){
            init_string[i] = nome_seme[i - seme_offset];
        }
    }

    // update the main vector holding the string
    v.push_back(init_string);

    // initialize the pattern for the center drawing
    vector&lt;uint16_t&gt; middle_matrix;
    middle_matrix.push_back(0x0000); //0      b0000000000000000
    middle_matrix.push_back(0x0400); //1024   b0000010000000000
    middle_matrix.push_back(0x0A00); //2560   b0000101000000000
    middle_matrix.push_back(0x1F00); //7936   b0001111100000000
    middle_matrix.push_back(0x2080); //8320   b0010000010000000
    middle_matrix.push_back(0x4040); //16448  b0100000001000000
    middle_matrix.push_back(0x0000); //0      b0000000000000000

    // scroll through the vector
    for(uint i = 0; i &lt; middle_matrix.size(); ++i){
        string line = "|";
        // bitshift the mask to find if the character will be a space or a *
        for(uint16_t mask = 0x8000; mask != 0x0010; mask &gt;&gt;= 1){
            if(middle_matrix[i] &amp; mask){ line += "*"; }
            else{ line += " "; }
        }
        line += "|";
        v.push_back(line);
    }

    // close the card
    v.push_back(card_delimiter);

    //print the card
    for(unsigned int i = 0; i &lt; v.size(); ++i){
        cout &lt;&lt; v[i] &lt;&lt; endl;
    }

    return v;
}


I'm posting this for a couple of questions, first of all I'm pretty new to C++ and I would like to know if you would write the whole procedure differently, maybe using replace functions or functions to map the bit pattern.

Second this version will print only one pattern, what would you suggest to do for the other 13?
What I did now was to take a text file, draw the pattern, realize it could be expressed in binary and now I'm here with this integers.

How would you go to maintain clarity? Shall I read in the pattern from a file?
Declare a static array that contains the numbered patterns?
Where shall I place the function that reads the values? or maybe create a class that reads from a "pattern" file and stores them in some member variable, and call it afterwards?
Would you write the whole procedure differently?


Absolutely. In it's current form, it does far too much stuff and (at least in my opinion) is not really flexible enough. Every time you try to add something (e.g. other card patterns, loading patterns from a file, change the card border, ...) the function will just get more and more bloated.

Actually, I'd implement it in a class CardPrinter. This way, you can transfer state (e.g. card patterns) between calls in a nicely, encapsulated way. Also, I'd split the functionality into multiple member functions, so it's easier to reason about them.


  (paraphrasing:) How would you handle multiple patterns?


For patterns as simple as these, I'd probably not bother to encode them in any special format. Just an array of lines is fine (unless you're programming under heavy memory constraints).

These patterns can be set statically, loaded from somewhere or even generated dynamically. Choose what fits your circumstances best! E.g. loading from a file could allow for different patterns, allowing for customization.


  Example:


struct CardPattern {
    std::vector&lt;std::string&gt; lines;
};

class CardPrinter {
    std::vector&lt;CardPattern&gt; patterns;
    std::string delimiter_left;
    std::string delimiter_right;
    std::string delimiter_top;
    std::string delimiter_bottom;
    size_t internal_width;

public:
    CardPrinter(const std::vector&lt;CardPattern&gt;&amp; pats, size_t w = 13, std::string del_left = "|", std::string del_right = "|", std::string del_top = "-", std::string del_bottom = "-")
            : patterns(pats), delimiter_left(del_left), delimiter_right(del_right), delimiter_top(del_top), delimiter_bottom(del_bottom), width(w - del_left.length() - del_right.length()) {
    }

    void print(const Carta&amp; card, std::ostream&amp; stream = std::cout) {
        stream &lt;&lt; get_card_visual(card);
    }
private:
    std::string get_card_title(const Carta&amp; card) {
        auto nome_semi = create_semi(card.seme);
        auto nome_valore = create_nome_valore(card.valore);

        return nome_valore + " " + nome_semi;
    }

    const CardPattern&amp; get_card_pattern(const Carta&amp; card) {
        // assuming pattern exists for that value
        return patterns[card.valore];
    }

    std::string format_line(const std::string&amp; line) {
        // just cut off parts that are too long
        // if too short, fill rest with spaces (could be changed, e.g. center text in that case)
        auto visible_part = line.substr(0, internal_width);
        auto filler = std::string(internal_width - visible_part.length(), ' ');

        return delimiter_left + visible_part + filler + delimiter_right + "\n";
    }

    std::string format_card_pattern(const CardPattern&amp; pattern) {
        auto formatted = std::string("");

        for(auto&amp; line : pattern.lines) {
            formatted += format_line(line);
        }

        return formatted;
    }

    std::string format_top() {
        auto top = std::string();
        while(top.length() &lt; internal_width) {
            top += delimiter_top;
        }

        return format_line(top);
    }

    std::string format_bottom() {
        auto bottom = std::string();
        while(bottom.length() &lt; internal_width) {
            bottom += delimiter_bottom;
        }

        return format_line(bottom);
    }

    std::string get_card_visual(const Carta&amp; card) {
        return format_top() + format_line(get_card_title(card)) + format_card_pattern(get_card_pattern(card)) + format_bottom();
    }
};

// Usage example
auto patterns = /* up to you, e.g. */ load_patterns();
auto printer1 = CardPrinter(patterns);
auto printer2 = CardPrinter(patterns, 15, "::", "::", "=", "=");

auto cards = /* get some cards */;
for(auto&amp; card : cards) {
    printer1.print(card);
    printer2.print(card);
}