129 lines
3.8 KiB
C

#include "highscore.h"
#include "bintree.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LEN 100
#define MAX_PLAYER_NAME_LEN 20
typedef struct {
char name[MAX_PLAYER_NAME_LEN];
int score;
} HighscoreEntry;
static TreeNode *highscoreTree = NULL;
// Compare two highscore entries by score (descending), then by name
// (ascending).
static int compareHighscoreEntries(const void *arg1, const void *arg2) {
const HighscoreEntry *entry1 = (const HighscoreEntry *)arg1;
const HighscoreEntry *entry2 = (const HighscoreEntry *)arg2;
int result = entry2->score - entry1->score;
if (result == 0)
result = strcmp(entry1->name, entry2->name);
return result;
}
// Create a new highscore entry from name and score.
static HighscoreEntry createHighscoreEntry(const char *name, int score) {
HighscoreEntry entry = {"", score};
if (name != NULL) {
strncpy(entry.name, name, MAX_PLAYER_NAME_LEN);
entry.name[MAX_PLAYER_NAME_LEN - 1] = '\0';
}
return entry;
}
// Calculate score based on time used and number of shown numbers.
static int calculateScore(double timeInSeconds, unsigned int len) {
return (1000.0 - timeInSeconds) * len;
}
// Load highscores from file into memory.
void loadHighscores(const char *path) {
FILE *file = fopen(path, "r");
if (file != NULL) {
char buffer[MAX_LINE_LEN + 1];
while (fgets(buffer, MAX_LINE_LEN + 1, file) != NULL) {
char *name = strtok(buffer, ";\n");
char *scoreStr = strtok(NULL, ";\n");
if (name != NULL && scoreStr != NULL) {
HighscoreEntry entry =
createHighscoreEntry(name, strtol(scoreStr, NULL, 10));
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry),
compareHighscoreEntries, NULL);
}
}
fclose(file);
}
}
// Add a new highscore entry and return the calculated score.
int addHighscore(const char *name, double timeInSeconds, unsigned int len) {
HighscoreEntry entry =
createHighscoreEntry(name, calculateScore(timeInSeconds, len));
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry),
compareHighscoreEntries, NULL);
return entry.score;
}
// Print highscores (up to NUMBER_OF_SHOWN_HIGHSCORES) in a formatted table.
void showHighscores() {
const char *blanks =
" "
" ";
const char *stripes =
"------------------------------------------------------------------------"
"------------------------------------------------";
const char *header = "H I G H S C O R E S";
const int lineWidth = MAX_PLAYER_NAME_LEN + MAX_PLAYER_NAME_LEN + 5;
int blankSpace = (int)(lineWidth - strlen(header)) / 2;
HighscoreEntry *entry = nextTreeData(highscoreTree);
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
printf("|%*.*s%s%*.*s|\n", blankSpace, blankSpace, blanks, header, blankSpace,
blankSpace, blanks);
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
for (int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++) {
printf("| %-*s | %*d |\n", MAX_PLAYER_NAME_LEN, entry->name,
MAX_PLAYER_NAME_LEN, entry->score);
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
entry = nextTreeData(NULL);
}
}
// Save highscores to file (up to NUMBER_OF_SHOWN_HIGHSCORES).
void saveHighscores(const char *path) {
FILE *file = fopen(path, "w");
if (file != NULL) {
HighscoreEntry *entry = nextTreeData(highscoreTree);
for (int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++) {
fprintf(file, "%s;%d\n", entry->name, entry->score);
entry = nextTreeData(NULL);
}
fclose(file);
}
}
// Free all memory used for highscores.
void clearHighscores() {
clearTree(&highscoreTree);
highscoreTree = NULL;
}