~ajpaon/squell

888d58e495b889071a97a6ac10854dfc90ae8038 — Andrew Paon 10 months ago 3753351
Add hiscores powered by sqlite
2 files changed, 209 insertions(+), 13 deletions(-)

M main.c
M makefile
M main.c => main.c +207 -12
@@ 1,4 1,5 @@
#include <errno.h>
#include <sqlite3.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>


@@ 243,11 244,14 @@ struct Piece PIECES[NPIECES];
#define NCLEARS 27
GridField CLEARS[NCLEARS];

sqlite3 *sqlite;

enum GameState {
  CHOOSING_PIECE,
  CHOOSING_COLUMN,
  CHOOSING_ROW,
  RESOLUTION,
  QUIT,
};

struct Game {


@@ 380,20 384,188 @@ void render(struct Game g) {
  }
}

// like strcpy, but doesn't insert null
void insert(char *dest, char *src, size_t max) {
  for (; *src && max > 0; --max) {
    *dest++ = *src++;
  }
}

void insert_score(char *dest, int score) {
  char digits[6] = "     ";
  int rounded = score >= 10000;
  score = rounded ? 9999 : score;
  digits[4] = rounded ? '+' : ' ';

  for (int i = 3; score && i >= 0; --i) {
    digits[i] = score % 10 + '0';
    score /= 10;
  }

  insert(dest, digits, 5);
}

void print_hiscores() {
  char hiscores[] = "               HISCORES                 \n"
                    "+------------+------------+------------+\n"
                    "| ALL TIME   | WEEKLY     | TODAY      |\n"
                    "+------------+------------+------------+\n"
                    "|            |            |            |\n"
                    "|            |            |            |\n"
                    "|            |            |            |\n"
                    "+------------+------------+------------+\n"
                    "\n\n";
  const int width = 41;
  const int r1 = width * 4;
  const int c1 = 2;
  const int c2 = 15;
  const int c3 = 28;

  {
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2(
        sqlite, "SELECT name, score FROM scores ORDER BY score DESC LIMIT 3;",
        -1, &stmt, NULL);

    int offset = r1 + c1;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
      const unsigned char *name = sqlite3_column_text(stmt, 0);
      const int score = sqlite3_column_int(stmt, 1);

      insert(hiscores + offset, (char *)name, 5);
      insert_score(hiscores + offset + 6, score);

      offset += width;
    }

    sqlite3_finalize(stmt);
  }

  {
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2(
        sqlite,
        "SELECT name, score FROM scores "
        "WHERE julianday(CURRENT_TIMESTAMP) - julianday(timestamp) < 7 "
        "ORDER BY score DESC "
        "LIMIT 3;",
        -1, &stmt, NULL);

    int offset = r1 + c2;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
      const unsigned char *name = sqlite3_column_text(stmt, 0);
      const int score = sqlite3_column_int(stmt, 1);

      insert(hiscores + offset, (char *)name, 5);
      insert_score(hiscores + offset + 6, score);

      offset += width;
    }

    sqlite3_finalize(stmt);
  }

  {
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2(
        sqlite,
        "SELECT name, score FROM scores "
        "WHERE julianday(CURRENT_TIMESTAMP) - julianday(timestamp) < 1 "
        "ORDER BY score DESC "
        "LIMIT 3;",
        -1, &stmt, NULL);

    int offset = r1 + c3;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
      const unsigned char *name = sqlite3_column_text(stmt, 0);
      const int score = sqlite3_column_int(stmt, 1);

      insert(hiscores + offset, (char *)name, 5);
      insert_score(hiscores + offset + 6, score);

      offset += width;
    }

    sqlite3_finalize(stmt);
  }
  fputs(hiscores, stdout);
}

void handle_quit(int score) {
  char name[6] = {0};
  int i = 0;

#define splash()                                                               \
  clear_screen();                                                              \
  printf("Thanks for playing! Your score is %d\n", score);                     \
  puts("Enter your name for the hiscores: ");                                  \
  fputs("    ", stdout);                                                       \
  puts(name)

  while (1) {
    splash();

    char c = getc(stdin);

    if (c == '\n') {
      break;
    }

    if (c == 127) {
      if (i > 0) {
        --i;
      }

      name[i] = 0;

      continue;
    }

    if (c >= 'a' && c <= 'z') {
      c += ('A' - 'a');
    }

    if (c >= 'A' && c <= 'Z') {
      if (i < 5) {
        name[i++] = c;
      }

      continue;
    }
  }

  splash();

#undef splash

  sqlite3_stmt *stmt;
  sqlite3_prepare_v2(sqlite, "INSERT INTO scores(name, score) VALUES (?, ?);",
                     -1, &stmt, NULL);

  sqlite3_bind_text(stmt, 1, name, 6, SQLITE_STATIC);
  sqlite3_bind_int(stmt, 2, score);
  sqlite3_step(stmt);

  sqlite3_finalize(stmt);

  clear_screen();
  print_hiscores();
}

void game_main() {
  long seed;
  time(&seed);
  srand(seed);

  char splash[] =
      "                      _ _ \n"
      " ___  __ _ _   _  ___| | |\n"
      "/ __|/ _` | | | |/ _ \\ | |\n"
      "\\__ \\ (_| | |_| |  __/ | |\n"
      "|___/\\__, |\\__,_|\\___|_|_|\n"
      "        |_|               \n"
      "\n\n"
  char header[] = "                        _ _ \n"
                  "   ___  __ _ _   _  ___| | |\n"
                  "  / __|/ _` | | | |/ _ \\ | |\n"
                  "  \\__ \\ (_| | |_| |  __/ | |\n"
                  "  |___/\\__, |\\__,_|\\___|_|_|\n"
                  "          |_|               \n"
                  "  \n\n";

  char intro[] =
      "squell is a game that combines elements of tetris and sudoku.\n"
      "Place blocks to fill and clear rows, columns, or squares.\n"
      "\n"


@@ 406,7 578,9 @@ void game_main() {
      "Press any key to begin.";

  clear_screen();
  fputs(splash, stdout);
  fputs(header, stdout);
  print_hiscores();
  fputs(intro, stdout);
  getc(stdin);

  struct Game game = {0};


@@ 444,7 618,8 @@ void game_main() {
      }

      case 'Q':
        return;
        game.state = QUIT;
        continue;
      }

      if (c < 'a' || c > 'c')


@@ 469,7 644,8 @@ void game_main() {
        continue;

      case 'Q':
        return;
        game.state = QUIT;
        continue;
      }

      if (c < 'a' || c > 'i') {


@@ 491,7 667,8 @@ void game_main() {
        continue;

      case 'Q':
        return;
        game.state = QUIT;
        continue;
      }

      if (c < '1' || c > '9') {


@@ 538,6 715,10 @@ void game_main() {

      break;
    }

    case QUIT:
      handle_quit(game.score);
      return;
    }

    printf("\n%d\n", game.active_piece);


@@ 698,6 879,15 @@ fatal:
  return -1;
}

int init_db() {
  static const char dbname[] = ".local/share/squell.db";
  if (sqlite3_open(dbname, &sqlite) != SQLITE_OK) {
    fputs(sqlite3_errmsg(sqlite), stderr);
  }

  return 0;
}

int main(int argc, char *argv[]) {
  if (init_screen() != 0) {
    return 1;


@@ 705,6 895,11 @@ int main(int argc, char *argv[]) {

  init_pieces();
  init_clears();

  if (init_db() != 0) {
    return 1;
  }

  game_main();

  return 0;

M makefile => makefile +2 -1
@@ 1,4 1,5 @@
CC=clang
LDLIBS=-lsqlite3

PREFIX?=/usr/local
BINDIR=$(PREFIX)/bin


@@ 12,7 13,7 @@ release: CFLAGS=-Wall -O2
release: squell

squell:	main.o
	$(CC) $(CFLAGS) --debug $< -o $@
	$(CC) $(CFLAGS) $(LDLIBS) --debug $< -o $@

.PHONY: clean
clean: