Explorar el Código

We fill the holes when we remove cards.

We correctly locate the "next" card to the
left or the right.
We display something when the column is cleared.

(That needs to be something sensible.)

We detect when the game is won.

(I need to do something more then log it.)
Steve Thielemann hace 3 años
padre
commit
51373696f1
Se han modificado 3 ficheros con 397 adiciones y 28 borrados
  1. 170 5
      deck.cpp
  2. 64 5
      deck.h
  3. 163 18
      main.cpp

+ 170 - 5
deck.cpp

@@ -249,17 +249,59 @@ const std::array<std::pair<int, int>, 18> Deck::blocks = {
  * @param c
  * @return * int
  */
-int Deck::unblocks(int c) {
+std::vector<int> Deck::unblocks(int c) {
+  std::vector<int> result;
   for (size_t i = 0; i < blocks.size(); ++i) {
     if ((blocks.at(i).first == c) || (blocks.at(i).second == c)) {
-      return i;
+      result.push_back(i);
     }
   }
-  return -1;
+  return result;
+}
+
+bool Deck::can_play(int c1, int c2) {
+  int s1, s2;
+  s1 = is_rank(c1);
+  s2 = is_rank(c2);
+
+  // this works %13 handles wrap-around for us.
+  if ((s1 + 1) % 13 == s2)
+    return true;
+
+  if (s1 == 0) {
+    s1 += 13;
+  }
+  if (s1 - 1 == s2)
+    return true;
+  return false;
 }
 
 door::Panel *Deck::marker(int c) { return mark[c]; }
 
+void Deck::remove_card(door::Door &door, int c, int off_x, int off_y, bool left,
+                       bool right) {
+  int cx, cy, level;
+  cardgo(c, cx, cy, level);
+  if (level > 1)
+    --level;
+  std::string cstr = back_char(level);
+  door::Goto g(cx + off_x, cy + off_y);
+  door << g << cardback;
+  if (left)
+    door << cstr;
+  else
+    door << " ";
+  door << "   ";
+  if (right)
+    door << cstr;
+  else
+    door << " ";
+  g.set(cx + off_x, cy + off_y + 1);
+  door << g << "     ";
+  g.set(cx + off_x, cy + off_y + 2);
+  door << g << "     ";
+}
+
 /*
 Layout spacing 1:
 
@@ -291,13 +333,15 @@ width = 5 * 10 + (2 * 9) = 50+18 = 68   !  I could do that!
 void cardgo(int pos, int space, int h, int &x, int &y, int &level) {
   // special cases here
   if (pos == 28) {
-    cardgo(23, space, h, x, y, level);
+    // cardgo(23, space, h, x, y, level);
+    cardgo(23, x, y, level);
     y += h + 1;
     --level;
     return;
   } else {
     if (pos == 29) {
-      cardgo(22, space, h, x, y, level);
+      // cardgo(22, space, h, x, y, level);
+      cardgo(22, x, y, level);
       y += h + 1;
       --level;
       return;
@@ -365,6 +409,80 @@ int levels[4] = {3, 6, 9, 10};
   }
 }
 
+/**
+ * @brief Given card pos, calculate x, y, and level values.
+ *
+ * level is used to determine the card background gradient.
+ *
+ * @param pos
+ * @param x
+ * @param y
+ * @param level
+ */
+void cardgo(int pos, int &x, int &y, int &level) {
+  const int space = 3;
+  const int h = 3;
+
+  // special cases here
+  if (pos == 28) {
+    cardgo(23, x, y, level);
+    y += h + 1;
+    --level;
+    return;
+  } else {
+    if (pos == 29) {
+      cardgo(22, x, y, level);
+      y += h + 1;
+      --level;
+      return;
+    }
+  }
+
+  const int CARD_WIDTH = 5;
+  int HALF_WIDTH = 3;
+  HALF_WIDTH += space / 2;
+
+  int between = CARD_WIDTH + space;
+
+  if (pos < 3) {
+    // top
+    level = 1;
+    y = (level - 1) * (h - 1) + 1;
+    x = pos * (between * 3) + between + HALF_WIDTH + space; // 10
+    return;
+  } else {
+    pos -= 3;
+  }
+  if (pos < 6) {
+    level = 2;
+    y = (level - 1) * (h - 1) + 1;
+    int group = (pos) / 2;
+    x = pos * between + (group * between) + CARD_WIDTH + space * 2;
+    return;
+  } else {
+    pos -= 6;
+  }
+  if (pos < 9) {
+    level = 3;
+    y = (level - 1) * (h - 1) + 1;
+    x = pos * between + HALF_WIDTH + space;
+    return;
+  } else {
+    pos -= 9;
+  }
+  if (pos < 10) {
+    level = 4;
+    y = (level - 1) * (h - 1) + 1;
+    x = (pos)*between + space;
+    return;
+  } else {
+    // something is wrong.
+    y = -1;
+    x = -1;
+    level = -1;
+  }
+}
+
 cards card_shuffle(std::seed_seq &seed, int decks) {
   std::mt19937 gen;
 
@@ -388,3 +506,50 @@ cards card_states(int decks) {
   states.assign(decks * 52, 0);
   return states;
 }
+
+/**
+ * @brief Find the next card we can move the marker to.
+ *
+ * if left, look in the left - direction, otherwise the right + direction.
+ * current is the current active card.
+ * states is the card states (0 = down, 1 = in play, 2 = removed)
+ *
+ * @param left
+ * @param states
+ * @param current
+ * @return int
+ */
+int find_next(bool left, const cards &states, int current) {
+  int cx, cy, level;
+  int current_x;
+  cardgo(current, cx, cy, level);
+  current_x = cx;
+  int x;
+  int pos = -1;
+  int pos_x;
+  if (left)
+    pos_x = 0;
+  else
+    pos_x = 100;
+
+  for (x = 0; x < 28; x++) {
+    if (states.at(x) == 1) {
+      // possible location
+      if (x == current)
+        continue;
+      cardgo(x, cx, cy, level);
+      if (left) {
+        if ((cx < current_x) and (cx > pos_x)) {
+          pos_x = cx;
+          pos = x;
+        }
+      } else {
+        if ((cx > current_x) and (cx < pos_x)) {
+          pos_x = cx;
+          pos = x;
+        }
+      }
+    }
+  }
+  return pos;
+}

+ 64 - 5
deck.h

@@ -90,9 +90,6 @@ private:
   std::string back_char(int level);
   door::Panel *back_of(int level);
   door::Panel *mark_of(int c);
-  int is_rank(int c);
-  int is_suit(int c);
-  int is_deck(int c);
   void init(void);
   char rank_symbol(int c);
   std::string suit_symbol(int c);
@@ -105,17 +102,52 @@ public:
   Deck(door::ANSIColor backcolor, int size = 3);
   ~Deck();
 
+  int is_rank(int c);
+  int is_suit(int c);
+  int is_deck(int c);
+  /**
+   * @brief Can this rank play on this other rank?
+   *
+   * @param c1
+   * @param c2
+   * @return true
+   * @return false
+   */
+  bool can_play(int c1, int c2);
+
   door::Panel *card(int c);
+  /**
+   * @brief Return panel for back of card.
+   *
+   * 0 = Blank
+   * 1 = level 1 (furthest/darkest)
+   * 2 = level 2
+   * 3 = level 3
+   * 4 = level 4 (closest/lightest)
+   *
+   * 5 = left (fills with left corner in place)
+   * 6 = right (fills right corner)
+   * 7 = both (fills both corners)
+   *
+   * @param level
+   * @return door::Panel*
+   */
   door::Panel *back(int level);
   door::Panel *marker(int c);
   void part(int x, int y, door::Door &d, int level, bool left);
-  int unblocks(int c);
+  std::vector<int> unblocks(int c);
   const static std::array<std::pair<int, int>, 18> blocks;
+
+  void remove_card(door::Door &door, int c, int off_x, int off_y, bool left,
+                   bool right);
 };
 
 /**
  * @brief Given a position, space=3, height=3, return x,y and level.
  *
+ * This is the older version that allows for space and h "height"
+ * to be variable.  I'd rather have one that has them as constants.
+ *
  * @param pos
  * @param space
  * @param h
@@ -123,7 +155,20 @@ public:
  * @param y
  * @param level
  */
-void cardgo(int pos, int space, int h, int &x, int &y, int &level);
+[[deprecated("Use cardgo(int pos, int &x, int &y, int &level")]] void
+cardgo(int pos, int space, int h, int &x, int &y, int &level);
+
+/**
+ * @brief Where does this card go?
+ *
+ * This finds x, y, and the level (for the card background)
+ *
+ * @param pos
+ * @param x
+ * @param y
+ * @param level
+ */
+void cardgo(int pos, int &x, int &y, int &level);
 
 /**
  * @brief shuffle deck of cards
@@ -137,3 +182,17 @@ void cardgo(int pos, int space, int h, int &x, int &y, int &level);
  */
 cards card_shuffle(std::seed_seq &seed, int decks = 1);
 cards card_states(int decks = 1);
+
+/**
+ * @brief Find the next card to move to.
+ *
+ * if (left) .. to the left, otherwise right
+ * current is the current position we're on.
+ *
+ * return -1 failed to find anything.
+ * @param left
+ * @param states
+ * @param current
+ * @return int
+ */
+int find_next(bool left, const cards &states, int current);

+ 163 - 18
main.cpp

@@ -533,9 +533,7 @@ door::ANSIColor from_string(std::string colorCode) {
 }
 
 // This does not seem to be working.  I keep getting zero.
-
 int opt_from_string(std::string colorCode) {
-
   for (std::size_t pos = 0; pos != deck_colors.size(); ++pos) {
     // if (caseInsensitiveStringCompare(colorCode, deck_colors[pos]) == 0) {
     if (iequals(colorCode, deck_colors[pos])) {
@@ -667,6 +665,9 @@ door::Panel make_streak_panel(void) {
     std::stringstream ss;
     ss << std::put_time(std::localtime(&in_time_t), "%B %d");
     text.append(ss.str());
+    door::Line current(text, W);
+    current.setRender(svRender);
+    p.addLine(std::make_unique<door::Line>(current));
   }
   {
     door::updateFunction currentUpdate = [](void) -> std::string {
@@ -841,6 +842,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
   card_number = 28;
   active_card = 23;
   score = 0;
+  play_day = std::chrono::system_clock::now();
 
   // cards color --
   // configured by the player.
@@ -866,7 +868,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
   int game_height = 20; // 13; // 9;
   {
     int cx, cy, level;
-    cardgo(27, space, height, cx, cy, level);
+    cardgo(27, cx, cy, level);
     game_width = cx + 5; // card width
   }
   int off_x = (mx - game_width) / 2;
@@ -897,9 +899,9 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
     int cxp, cyp, levelp;
     int left_panel_x, right_panel_x;
     // find position of card, to position the panels
-    cardgo(18, space, height, cxp, cyp, levelp);
+    cardgo(18, cxp, cyp, levelp);
     left_panel_x = cxp;
-    cardgo(15, space, height, cxp, cyp, levelp);
+    cardgo(15, cxp, cyp, levelp);
     right_panel_x = cxp;
     score_panel.set(left_panel_x + off_x, off_yp);
     streak_panel.set(right_panel_x + off_x, off_yp);
@@ -919,7 +921,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
       // step 1:
       // draw the deck "source"
       int cx, cy, level;
-      cardgo(29, space, height, cx, cy, level);
+      cardgo(29, cx, cy, level);
 
       if (card_number == 51)
         level = 0; // out of cards!
@@ -939,7 +941,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
     for (int x = 0; x < (dealing ? 28 : 29); x++) {
       int cx, cy, level;
 
-      cardgo(x, space, height, cx, cy, level);
+      cardgo(x, cx, cy, level);
       // This is hardly visible.
       // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
       if (dealing)
@@ -980,7 +982,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
         int cx, cy, level;
 
         state.at(x) = 1;
-        cardgo(x, space, height, cx, cy, level);
+        cardgo(x, cx, cy, level);
         // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
         std::this_thread::sleep_for(std::chrono::milliseconds(200));
 
@@ -991,7 +993,7 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
 
     {
       int cx, cy, level;
-      cardgo(active_card, space, height, cx, cy, level);
+      cardgo(active_card, cx, cy, level);
       c = d.marker(1);
       c->set(cx + off_x + 2, cy + off_y + 2);
       door << *c;
@@ -1013,21 +1015,23 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
         if (r < 0x1000)
           r = std::toupper(r);
         switch (r) {
-        case ' ':
+        case '\x0d':
           if (card_number < 51) {
             card_number++;
+            current_streak = 0;
+            streak_panel.update(door);
 
             // Ok, deal next card from the pile.
             int cx, cy, level;
 
             if (card_number == 51) {
-              cardgo(29, space, height, cx, cy, level);
+              cardgo(29, cx, cy, level);
               level = 0; // out of cards
               c = d.back(level);
               c->set(cx + off_x, cy + off_y);
               door << *c;
             }
-            cardgo(28, space, height, cx, cy, level);
+            cardgo(28, cx, cy, level);
             c = d.card(deck1.at(card_number));
             c->set(cx + off_x, cy + off_y);
             door << *c;
@@ -1041,23 +1045,161 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
         case 'Q':
           now_what = false;
           break;
+        case ' ':
+          // can we play this card?
+          /*
+          get_logger() << "can_play( " << active_card << ":"
+                       << deck1.at(active_card) << "/"
+                       << d.is_rank(deck1.at(active_card)) << " , "
+                       << card_number << "/" << d.is_rank(deck1.at(card_number))
+                       << ") = "
+                       << d.can_play(deck1.at(active_card),
+                                     deck1.at(card_number))
+                       << std::endl;
+                       */
+
+          if (d.can_play(deck1.at(active_card), deck1.at(card_number))) {
+            // if (true) {
+            // yes we can.
+            ++current_streak;
+            if (current_streak > best_streak)
+              best_streak = current_streak;
+            streak_panel.update(door);
+
+            // play card!
+            state.at(active_card) = 2;
+            {
+              // swap the active card with card_number (play card)
+              int temp = deck1.at(active_card);
+              deck1.at(active_card) = deck1.at(card_number);
+              deck1.at(card_number) = temp;
+              // active_card is -- invalidated here!  find "new" card.
+              int cx, cy, level;
+
+              // erase/clear active_card
+              std::vector<int> check = d.unblocks(active_card);
+              bool left = false, right = false;
+              for (const int c : check) {
+                std::pair<int, int> blockers = d.blocks[c];
+                if (blockers.first == active_card)
+                  right = true;
+                if (blockers.second == active_card)
+                  left = true;
+              }
+
+              d.remove_card(door, active_card, off_x, off_y, left, right);
+
+              /*   // old way of doing this that leaves holes.
+              cardgo(active_card, cx, cy, level);
+              c = d.back(0);
+              c->set(cx + off_x, cy + off_y);
+              door << *c;
+              */
+
+              // redraw play card #28. (Which is the "old" active_card)
+              cardgo(28, cx, cy, level);
+              c = d.card(deck1.at(card_number));
+              c->set(cx + off_x, cy + off_y);
+              door << *c;
+
+              // Did we unhide a card here?
+
+              // std::vector<int> check = d.unblocks(active_card);
+
+              /*
+              get_logger() << "active_card = " << active_card
+                           << " unblocks: " << check.size() << std::endl;
+              */
+              int new_card_shown = -1;
+              if (!check.empty()) {
+                for (const int to_check : check) {
+                  std::pair<int, int> blockers = d.blocks[to_check];
+                  /*
+                  get_logger()
+                      << "Check: " << to_check << " " << blockers.first << ","
+                      << blockers.second << " " << state.at(blockers.first)
+                      << "," << state.at(blockers.second) << std::endl;
+                      */
+                  if ((state.at(blockers.first) == 2) and
+                      (state.at(blockers.second) == 2)) {
+                    // WOOT!  Card uncovered.
+                    /*
+                    get_logger() << "showing: " << to_check << std::endl;
+                    */
+                    state.at(to_check) = 1;
+                    cardgo(to_check, cx, cy, level);
+                    c = d.card(deck1.at(to_check));
+                    c->set(cx + off_x, cy + off_y);
+                    door << *c;
+                    new_card_shown = to_check;
+                  }
+                }
+              } else {
+                // this would be a "top" card.  Should set status = 4 and
+                // display something here?
+                get_logger() << "top card cleared?" << std::endl;
+                // display something at active_card position
+                int cx, cy, level;
+                cardgo(active_card, cx, cy, level);
+                door << door::Goto(cx + off_x, cy + off_y) << door::reset
+                     << "clear";
+              }
+
+              // Find new "number" for active_card to be.
+              if (new_card_shown != -1) {
+                active_card = new_card_shown;
+              } else {
+                // active_card++;
+                int new_active = active_card - 1;
+                while (new_active >= 0) {
+                  if (state.at(new_active) == 1)
+                    break;
+                  --new_active;
+                }
+                if (new_active >= 0) {
+                  active_card = new_active;
+                } else {
+                  // ok, we failed to find a card that way, look the other way.
+                  new_active = active_card + 1;
+                  while (new_active <= 28) {
+                    if (state.at(new_active) == 1)
+                      break;
+                    ++new_active;
+                  }
+                  if (new_active < 28) {
+                    active_card = new_active;
+                  } else {
+                    get_logger() << "This looks like END OF GAME." << std::endl;
+                  }
+                }
+              }
+              // update the active_card marker!
+              cardgo(active_card, cx, cy, level);
+              c = d.marker(1);
+              c->set(cx + off_x + 2, cy + off_y + 2);
+              door << *c;
+            }
+          }
+          break;
         case XKEY_LEFT_ARROW:
         case '4': {
+          int new_active = find_next(true, state, active_card);
+          /*
           int new_active = active_card - 1;
           while (new_active >= 0) {
             if (state.at(new_active) == 1)
               break;
             --new_active;
-          }
+          }*/
           if (new_active >= 0) {
 
             int cx, cy, level;
-            cardgo(active_card, space, height, cx, cy, level);
+            cardgo(active_card, cx, cy, level);
             c = d.marker(0);
             c->set(cx + off_x + 2, cy + off_y + 2);
             door << *c;
             active_card = new_active;
-            cardgo(active_card, space, height, cx, cy, level);
+            cardgo(active_card, cx, cy, level);
             c = d.marker(1);
             c->set(cx + off_x + 2, cy + off_y + 2);
             door << *c;
@@ -1065,20 +1207,23 @@ int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
         } break;
         case XKEY_RIGHT_ARROW:
         case '6': {
+          int new_active = find_next(false, state, active_card);
+          /*
           int new_active = active_card + 1;
           while (new_active < 28) {
             if (state.at(new_active) == 1)
               break;
             ++new_active;
           }
-          if (new_active < 28) {
+          */
+          if (new_active >= 0) { //(new_active < 28) {
             int cx, cy, level;
-            cardgo(active_card, space, height, cx, cy, level);
+            cardgo(active_card, cx, cy, level);
             c = d.marker(0);
             c->set(cx + off_x + 2, cy + off_y + 2);
             door << *c;
             active_card = new_active;
-            cardgo(active_card, space, height, cx, cy, level);
+            cardgo(active_card, cx, cy, level);
             c = d.marker(1);
             c->set(cx + off_x + 2, cy + off_y + 2);
             door << *c;