Forráskód Böngészése

Updated: I score the wins in the table as well.

I should probably just have sqlite total up
the scores for the day (and sort descending
for me).

Broke out common functions into utils file.
Added get_logger() // global logging access.
Added config // global configuration access.
Steve Thielemann 3 éve
szülő
commit
6b27759eee
9 módosított fájl, 238 hozzáadás és 68 törlés
  1. 1 1
      CMakeLists.txt
  2. 84 15
      db.cpp
  3. 18 1
      db.h
  4. 10 12
      deck.cpp
  5. 0 2
      deck.h
  6. 33 28
      main.cpp
  7. 35 9
      play.cpp
  8. 33 0
      utils.cpp
  9. 24 0
      utils.h

+ 1 - 1
CMakeLists.txt

@@ -65,6 +65,6 @@ endif()
 
 add_subdirectory(yaml-cpp)
 
-add_executable(space-ace main.cpp deck.cpp db.h db.cpp play.h play.cpp images.h)
+add_executable(space-ace main.cpp deck.cpp db.h db.cpp play.h play.cpp utils.h utils.cpp images.h)
 target_link_libraries(space-ace door++ pthread SQLiteCpp sqlite3 dl yaml-cpp)
 

+ 84 - 15
db.cpp

@@ -1,4 +1,5 @@
 #include "db.h"
+#include "utils.h"
 
 #include <SQLiteCpp/VariadicBind.h>
 
@@ -6,14 +7,6 @@
 #include <iostream>
 #include <sstream>
 
-// configuration settings access
-#include "yaml-cpp/yaml.h"
-extern YAML::Node config;
-
-#include <fstream>
-#include <functional>
-extern std::function<std::ofstream &(void)> get_logger;
-
 /*
 The database access is slow.
 
@@ -47,7 +40,7 @@ settings(username TEXT, setting TEXT, value TEXT, \
 PRIMARY KEY(username, setting));");
     db.exec("CREATE TABLE IF NOT EXISTS \
 scores ( \"username\" TEXT, \"when\" INTEGER, \
-\"date\" INTEGER, \"hand\" INTEGER, \"score\" INTEGER, \
+\"date\" INTEGER, \"hand\" INTEGER, \"won\" INTEGER, \"score\" INTEGER, \
 PRIMARY KEY(\"username\", \"date\", \"hand\"));");
   } catch (std::exception &e) {
     if (get_logger) {
@@ -119,23 +112,25 @@ void DBData::setSetting(const std::string &setting, const std::string &value) {
  * @param when now()
  * @param date what day they played
  * @param hand which hand they played
+ * @param won did they win? 1/0
  * @param score
  */
-void DBData::saveScore(time_t when, time_t date, int hand, int score) {
+void DBData::saveScore(time_t when, time_t date, int hand, int won, int score) {
   try {
-    SQLite::Statement stmt(db,
-                           "INSERT INTO scores( \"username\", \"when\", "
-                           "\"date\", \"hand\", \"score\") VALUES(?,?,?,?,?);");
+    SQLite::Statement stmt(
+        db, "INSERT INTO scores( \"username\", \"when\", "
+            "\"date\", \"hand\", \"won\", \"score\") VALUES(?,?,?,?,?,?);");
     stmt.bind(1, user);
     stmt.bind(2, when);
     stmt.bind(3, date);
     stmt.bind(4, hand);
-    stmt.bind(5, score);
+    stmt.bind(5, won);
+    stmt.bind(6, score);
     stmt.exec();
   } catch (std::exception &e) {
     if (get_logger) {
       get_logger() << "saveScore( " << when << "," << date << "," << hand << ","
-                   << score << " ): " << user << std::endl;
+                   << won << "," << score << " ): " << user << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
   }
@@ -169,6 +164,80 @@ int DBData::handsPlayedOnDay(time_t day) {
   return 0;
 }
 
+std::vector<scores_data> DBData::getScoresOnDay(time_t date) {
+  std::vector<scores_data> scores;
+  try {
+    // \"when\",
+    SQLite::Statement stmt(db, "SELECT \"username\", \"date\", \"hand\", "
+                               "\"won\", \"score\" FROM SCORES WHERE "
+                               "\"date\"=? ORDER BY \"username\", \"hand\";");
+    stmt.bind(1, date);
+    while (stmt.executeStep()) {
+      scores_data sd;
+      sd.user = (const char *)stmt.getColumn(0);
+      sd.date = stmt.getColumn(1);
+      sd.hand = stmt.getColumn(2);
+      sd.won = stmt.getColumn(3);
+      sd.score = stmt.getColumn(4);
+      scores.push_back(sd);
+    }
+  } catch (std::exception &e) {
+    if (get_logger) {
+      get_logger() << "getScoresOnDay( " << date << " ): " << std::endl;
+      get_logger() << "SQLite exception: " << e.what() << std::endl;
+    }
+    scores.clear();
+  }
+  return scores;
+}
+
+std::map<time_t, std::vector<scores_data>> DBData::getScores(void) {
+  std::map<time_t, std::vector<scores_data>> scores;
+  try {
+    SQLite::Statement stmt(db, "SELECT \"username\", \"date\", \"hand\", "
+                               "\"won\", \"score\" FROM SCORES "
+                               "ORDER BY \"date\", \"username\", \"hand\";");
+    time_t current = 0;
+    std::vector<scores_data> vsd;
+
+    while (stmt.executeStep()) {
+      time_t the_date = stmt.getColumn(1);
+
+      if (current == 0) {
+        // ok, we've got the first one!
+        current = the_date;
+      } else {
+        // Ok, are we on another date now?
+        if (the_date != current) {
+          scores[current] = std::move(vsd);
+          vsd.clear();
+          current = the_date;
+        }
+      }
+      scores_data sd;
+      sd.user = (const char *)stmt.getColumn(0);
+      sd.date = the_date; // stmt.getColumn(1);
+      sd.hand = stmt.getColumn(2);
+      sd.won = stmt.getColumn(3);
+      sd.score = stmt.getColumn(4);
+      vsd.push_back(sd);
+    }
+    if (!vsd.empty()) {
+      scores[current] = std::move(vsd);
+    }
+    vsd.clear();
+  } catch (std::exception &e) {
+    if (get_logger) {
+      get_logger() << "getScores(): " << std::endl;
+      get_logger() << "SQLite exception: " << e.what() << std::endl;
+    }
+    scores.clear();
+  }
+  return scores;
+}
+
+void DBData::expireScores(void) {}
+
 /**
  * @brief Format date to string.
  *

+ 18 - 1
db.h

@@ -3,6 +3,18 @@
 
 #include <SQLiteCpp/SQLiteCpp.h>
 
+#include <map>
+#include <string>
+#include <vector>
+
+typedef struct {
+  time_t date;
+  std::string user;
+  int hand;
+  int score;
+  int won;
+} scores_data;
+
 class DBData {
   SQLite::Database db;
   void create_tables(void);
@@ -24,7 +36,12 @@ public:
   void clearUser(void) { user.clear(); };
   std::string getSetting(const std::string &setting, std::string ifMissing);
   void setSetting(const std::string &setting, const std::string &value);
-  void saveScore(time_t when, time_t date, int hand, int score);
+  void saveScore(time_t when, time_t date, int hand, int won, int score);
+
+  std::vector<scores_data> getScoresOnDay(time_t date);
+  std::map<time_t, std::vector<scores_data>> getScores(void);
+  void expireScores(void);
+
   int handsPlayedOnDay(time_t day);
 };
 

+ 10 - 12
deck.cpp

@@ -1,5 +1,6 @@
 #include "deck.h"
 
+#include "utils.h"
 #include <algorithm>
 #include <map>
 #include <sstream>
@@ -264,15 +265,15 @@ shared_panel Deck::card(int c) { return cards[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)
+ * - 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)
+ * - 5 = left (fills with left corner in place)
+ * - 6 = right (fills right corner)
+ * - 7 = both (fills both corners)
  *
  * @param level
  * @return door::Panel*
@@ -290,7 +291,7 @@ const std::array<std::pair<int, int>, 18> Deck::blocks = {
 };
 
 /**
- * @brief Which card (if any) is unblocked by this card
+ * @brief Which card(s) are unblocked by this card?
  *
  * @param card
  * @return * int
@@ -974,6 +975,3 @@ door::ANSIColor stringToANSIColor(std::string colorCode) {
 }
 
 std::string stringFromColorOptions(int opt) { return deck_colors[opt]; }
-void string_toupper(std::string &str) {
-  std::transform(str.begin(), str.end(), str.begin(), ::toupper);
-}

+ 0 - 2
deck.h

@@ -140,6 +140,4 @@ door::renderFunction makeColorRender(door::ANSIColor c1, door::ANSIColor c2,
 door::ANSIColor stringToANSIColor(std::string colorCode);
 std::string stringFromColorOptions(int opt);
 
-void string_toupper(std::string &str);
-
 #endif

+ 33 - 28
main.cpp

@@ -11,38 +11,13 @@
 #include "db.h"
 #include "deck.h"
 #include "play.h"
+#include "utils.h"
 #include "version.h"
 #include <algorithm> // transform
 
 // configuration here -- access via extern
 YAML::Node config;
 
-bool replace(std::string &str, const std::string &from, const std::string &to) {
-  size_t start_pos = str.find(from);
-  if (start_pos == std::string::npos)
-    return false;
-  str.replace(start_pos, from.length(), to);
-  return true;
-}
-
-bool replace(std::string &str, const char *from, const char *to) {
-  size_t start_pos = str.find(from);
-  if (start_pos == std::string::npos)
-    return false;
-  str.replace(start_pos, strlen(from), to);
-  return true;
-}
-
-bool file_exists(const std::string &name) {
-  ifstream f(name.c_str());
-  return f.good();
-}
-
-bool file_exists(const char *name) {
-  ifstream f(name);
-  return f.good();
-}
-
 door::ANSIColor stringToANSIColor(std::string colorCode);
 
 std::function<std::ofstream &(void)> get_logger;
@@ -706,7 +681,7 @@ int main(int argc, char *argv[]) {
   }
 
   if (!config["date_format"]) {
-    config["date_format"] = "%B %d";
+    config["date_format"] = "%B %d"; // Month day or "%b %d,%Y" Mon,d YYYY
     update_config = true;
   }
 
@@ -837,7 +812,37 @@ int main(int argc, char *argv[]) {
     }; break;
 
     case 2: // view scores
-      door << "Show scores goes here!" << door::nl;
+    {
+      door << door::cls;
+      auto all_scores = spacedb.getScores();
+      for (auto it : all_scores) {
+        time_t on_this_date = it.first;
+        std::string nice_date = convertDateToDateScoreFormat(on_this_date);
+        door << "  *** " << nice_date << " ***" << door::nl;
+        scores_data merge;
+
+        for (auto sd : it.second) {
+          if (merge.user.empty())
+            merge = sd;
+          else {
+            if (merge.user == sd.user) {
+              // merge in the information
+              merge.hand = sd.hand;
+              merge.won += sd.won;
+              merge.score += sd.score;
+            } else {
+              // Ok, output the merged data and reset
+              door << setw(15) << merge.user << " " << merge.hand << " "
+                   << merge.won << " " << sd.score << door::nl;
+              merge = sd;
+            }
+          }
+        }
+        door << setw(15) << merge.user << " " << merge.hand << " " << merge.won
+             << " " << merge.score << door::nl;
+      }
+      door << "====================" << door::nl;
+    }
       r = press_a_key(door);
       break;
 

+ 35 - 9
play.cpp

@@ -1,25 +1,24 @@
 #include "play.h"
 #include "db.h"
 #include "deck.h"
+#include "utils.h"
 #include "version.h"
 
 #include <iomanip> // put_time
 #include <sstream>
 
-// configuration settings access
-#include "yaml-cpp/yaml.h"
-extern YAML::Node config;
-
 #define CHEATER "CHEAT_YOUR_ASS_OFF"
 
-static std::function<std::ofstream &(void)> get_logger;
+// static std::function<std::ofstream &(void)> get_logger;
 
+/*
 static int press_a_key(door::Door &door) {
   door << door::reset << "Press a key to continue...";
   int r = door.sleep_key(door.inactivity);
   door << door::nl;
   return r;
 }
+*/
 
 /*
 In the future, this will probably check to see if they can play today or not, as
@@ -33,20 +32,32 @@ PlayCards::PlayCards(door::Door &d, DBData &dbd) : door{d}, db{dbd} {
   init_values();
 
   play_day = std::chrono::system_clock::now();
+
   // adjustment
   time_t time_play_day = std::chrono::system_clock::to_time_t(play_day);
+  /*
   if (get_logger) {
     get_logger() << "before: "
                  << std::put_time(std::localtime(&time_play_day), "%F %R")
                  << std::endl;
   };
+  */
   normalizeDate(time_play_day);
   play_day = std::chrono::system_clock::from_time_t(time_play_day);
+  /*
   if (get_logger) {
     get_logger() << "after: "
                  << std::put_time(std::localtime(&time_play_day), "%F %R")
                  << std::endl;
   };
+  */
+
+  /*
+   * TODO:  Check the date with the db.  Have they already played up today?  If
+   * so, display calendar and find a day they can play.  Or, play missed hands
+   * for today.
+   */
+
   spaceAceTriPeaks = make_tripeaks();
   score_panel = make_score_panel();
   streak_panel = make_streak_panel();
@@ -63,7 +74,9 @@ PlayCards::PlayCards(door::Door &d, DBData &dbd) : door{d}, db{dbd} {
 PlayCards::~PlayCards() { get_logger = nullptr; }
 
 void PlayCards::init_values(void) {
+  // beware of hand=1 !  We might not be playing the first hand here!
   hand = 1;
+
   if (config["hands_per_day"]) {
     total_hands = config["hands_per_day"].as<int>();
   } else
@@ -79,6 +92,10 @@ void PlayCards::init_values(void) {
   score = 0;
 }
 
+/**
+ * @brief Display the bonus text, when you remove a peak.
+ *
+ */
 void PlayCards::bonus(void) {
   door << door::ANSIColor(door::COLOR::YELLOW, door::ATTR::BOLD) << "BONUS";
 }
@@ -150,7 +167,7 @@ next_hand:
     streak_panel->set(right_panel_x + off_x, off_yp);
     cmd_panel->set(left_panel_x + off_x, off_yp + 5);
 
-    // next panel position
+    // next panel position (top = card 10, centered)
     cardPos(10, cx, cy);
     int next_off_x = (mx - next_quit_panel->getWidth()) / 2;
     next_quit_panel->set(next_off_x, cy + off_y);
@@ -240,7 +257,7 @@ next_hand:
                 std::chrono::system_clock::now());
             db.saveScore(right_now,
                          std::chrono::system_clock::to_time_t(play_day), hand,
-                         score);
+                         0, score);
           }
           in_game = false;
           r = 'Q';
@@ -251,7 +268,7 @@ next_hand:
           time_t right_now = std::chrono::system_clock::to_time_t(
               std::chrono::system_clock::now());
           db.saveScore(right_now,
-                       std::chrono::system_clock::to_time_t(play_day), hand,
+                       std::chrono::system_clock::to_time_t(play_day), hand, 0,
                        score);
           hand++;
           goto next_hand;
@@ -405,7 +422,7 @@ next_hand:
                     std::chrono::system_clock::now());
                 db.saveScore(right_now,
                              std::chrono::system_clock::to_time_t(play_day),
-                             hand, score);
+                             hand, 1, score);
                 //}
                 next_quit_panel->update();
                 door << *next_quit_panel;
@@ -512,6 +529,15 @@ next_hand:
   return r;
 }
 
+/**
+ * @brief Redraw the entire play cards screen.
+ *
+ * If dealing then show delays when displaying cards, or turning them over.
+ *
+ * Otherwise, the user has requested a redraw -- and don't delay.
+ *
+ * @param dealing
+ */
 void PlayCards::redraw(bool dealing) {
   shared_panel c;
 

+ 33 - 0
utils.cpp

@@ -0,0 +1,33 @@
+#include "utils.h"
+
+#include <algorithm>
+
+bool replace(std::string &str, const std::string &from, const std::string &to) {
+  size_t start_pos = str.find(from);
+  if (start_pos == std::string::npos)
+    return false;
+  str.replace(start_pos, from.length(), to);
+  return true;
+}
+
+bool replace(std::string &str, const char *from, const char *to) {
+  size_t start_pos = str.find(from);
+  if (start_pos == std::string::npos)
+    return false;
+  str.replace(start_pos, strlen(from), to);
+  return true;
+}
+
+bool file_exists(const std::string &name) {
+  std::ifstream f(name.c_str());
+  return f.good();
+}
+
+bool file_exists(const char *name) {
+  std::ifstream f(name);
+  return f.good();
+}
+
+void string_toupper(std::string &str) {
+  std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+}

+ 24 - 0
utils.h

@@ -0,0 +1,24 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <fstream>
+#include <functional>
+#include <string.h>
+#include <string>
+
+// utility functions go here
+
+bool replace(std::string &str, const std::string &from, const std::string &to);
+bool replace(std::string &str, const char *from, const char *to);
+bool file_exists(const std::string &name);
+bool file_exists(const char *name);
+void string_toupper(std::string &str);
+
+// logger access
+extern std::function<std::ofstream &(void)> get_logger;
+
+// configuration settings access
+#include "yaml-cpp/yaml.h"
+extern YAML::Node config;
+
+#endif