36,7 → 36,7 |
*/ |
|
/** @addtogroup tetris |
* @{ |
* @{ |
*/ |
/** @file |
*/ |
48,24 → 48,18 |
* |
* Major whacks since then. |
*/ |
|
#include <errno.h> |
/* #include <err.h> */ |
/* #include <fcntl.h> */ |
/* #include <pwd.h> */ |
#include <stdio.h> |
/* #include <stdlib.h> */ |
#include <string.h> |
#include <kbd/kbd.h> |
#include <kbd/keycode.h> |
#include <io/console.h> |
#include <io/keycode.h> |
#include <vfs/vfs.h> |
#include <stdlib.h> |
/* #include <time.h> */ |
/* #include <term.h> */ |
/* #include <unistd.h> */ |
/* #include <sys/param.h> */ |
/* #include <sys/stat.h> */ |
/* #include <sys/types.h> */ |
#include <fcntl.h> |
#include <err.h> |
#include <time.h> |
|
#include "pathnames.h" |
#include "screen.h" |
#include "tetris.h" |
#include "scores.h" |
78,20 → 72,22 |
* As long as the scores are kept sorted, this is simply the first one at |
* that level. |
*/ |
#define NUMSPOTS (MAXHISCORES + 1) |
#define NLEVELS (MAXLEVEL + 1) |
|
/* static time_t now; */ |
/* static int nscores; */ |
/* static int gotscores; */ |
/* static struct highscore scores[NUMSPOTS]; */ |
#define NUMSPOTS (MAXHISCORES + 1) |
#define NLEVELS (MAXLEVEL + 1) |
|
static struct highscore scores[NUMSPOTS]; |
|
/* static int checkscores(struct highscore *, int); */ |
/* static int cmpscores(const void *, const void *); */ |
/* static void getscores(FILE **); */ |
/* static void printem(int, int, struct highscore *, int, const char *); */ |
/* static char *thisuser(void); */ |
/** Copy from hiscore table score with index src to dest |
* |
*/ |
static void copyhiscore(int dest, int src) |
{ |
str_cpy(scores[dest].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, |
scores[src].hs_name); |
scores[dest].hs_score = scores[src].hs_score; |
scores[dest].hs_level = scores[src].hs_level; |
} |
|
void showscores(int firstgame) |
{ |
101,59 → 97,55 |
moveto(10, 0); |
printf("\tRank \tLevel \tName\t points\n"); |
printf("\t========================================================\n"); |
for (i = 0; i < NUMSPOTS - 1; i++) { |
printf("\t%6d %6d %-16s %20d\n", i+1, scores[i].hs_level, scores[i].hs_name, scores[i].hs_score); |
} |
|
for (i = 0; i < NUMSPOTS - 1; i++) |
printf("\t%6d %6d %-16s %20d\n", |
i + 1, scores[i].hs_level, scores[i].hs_name, scores[i].hs_score); |
|
if (!firstgame) { |
printf("\t========================================================\n"); |
printf("\t Last %6d %-16s %20d\n", scores[NUMSPOTS - 1].hs_level, scores[NUMSPOTS - 1].hs_name, scores[NUMSPOTS - 1].hs_score); |
printf("\t Last %6d %-16s %20d\n", |
scores[NUMSPOTS - 1].hs_level, scores[NUMSPOTS - 1].hs_name, scores[NUMSPOTS - 1].hs_score); |
} |
|
printf("\n\n\n\n\tPress any key to return to main menu."); |
getchar(); |
} |
|
/** Copy from hiscore table score with index src to dest |
* |
*/ |
static void copyhiscore(int dest, int src) |
{ |
str_cpy(scores[dest].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, |
scores[src].hs_name); |
scores[dest].hs_score = scores[src].hs_score; |
scores[dest].hs_level = scores[src].hs_level; |
} |
|
void insertscore(int score, int level) |
{ |
int i,j; |
int i; |
int j; |
size_t off; |
kbd_event_t ev; |
console_event_t ev; |
|
clear_screen(); |
moveto(10 , 10); |
moveto(10, 10); |
puts("Insert your name: "); |
str_cpy(scores[NUMSPOTS - 1].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, |
"Player"); |
i = 6; off = 6; |
|
i = 6; |
off = 6; |
|
moveto(10 , 28); |
printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................"); |
|
printf("%s%.*s", scores[NUMSPOTS - 1].hs_name, MAXLOGNAME-i, |
"........................................"); |
|
while (1) { |
fflush(stdout); |
if (kbd_get_event(&ev) != EOK) |
if (!console_get_event(fphone(stdin), &ev)) |
exit(1); |
|
if (ev.type == KE_RELEASE) |
|
if (ev.type == KEY_RELEASE) |
continue; |
|
|
if (ev.key == KC_ENTER || ev.key == KC_NENTER) |
break; |
|
|
if (ev.key == KC_BACKSPACE) { |
if (i > 0) { |
wchar_t uc; |
|
|
--i; |
while (off > 0) { |
--off; |
163,7 → 155,7 |
if (uc != U_SPECIAL) |
break; |
} |
|
|
scores[NUMSPOTS - 1].hs_name[off] = '\0'; |
} |
} else if (ev.c != '\0') { |
176,398 → 168,33 |
} |
} |
|
moveto(10 , 28); |
printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................"); |
moveto(10, 28); |
printf("%s%.*s", scores[NUMSPOTS - 1].hs_name, MAXLOGNAME - i, |
"........................................"); |
} |
|
scores[NUMSPOTS - 1].hs_score = score; |
scores[NUMSPOTS - 1].hs_score = score; |
scores[NUMSPOTS - 1].hs_level = level; |
|
i = NUMSPOTS-1; |
i = NUMSPOTS - 1; |
while ((i > 0) && (scores[i - 1].hs_score < score)) |
i--; |
|
for (j = NUMSPOTS - 2; j > i; j--) { |
copyhiscore(j,j-1); |
} |
copyhiscore(i, NUMSPOTS - 1); |
|
for (j = NUMSPOTS - 2; j > i; j--) |
copyhiscore(j, j-1); |
|
copyhiscore(i, NUMSPOTS - 1); |
} |
|
void initscores(void) |
{ |
int i; |
for(i = 0; i < NUMSPOTS; i++) { |
for (i = 0; i < NUMSPOTS; i++) { |
str_cpy(scores[i].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, "HelenOS Team"); |
scores[i].hs_score = (NUMSPOTS - i) * 200; |
scores[i].hs_level = (i + 1 > MAXLEVEL?MAXLEVEL:i + 1); |
scores[i].hs_score = (NUMSPOTS - i) * 200; |
scores[i].hs_level = (i + 1 > MAXLEVEL ? MAXLEVEL : i + 1); |
} |
} |
|
/* |
* Read the score file. Can be called from savescore (before showscores) |
* or showscores (if savescore will not be called). If the given pointer |
* is not NULL, sets *fpp to an open file pointer that corresponds to a |
* read/write score file that is locked with LOCK_EX. Otherwise, the |
* file is locked with LOCK_SH for the read and closed before return. |
* |
* Note, we assume closing the stdio file releases the lock. |
*/ |
/* static void */ |
/* getscores(FILE **fpp) */ |
/* { */ |
/* int sd, mint, lck, mask, i; */ |
/* char *mstr, *human; */ |
/* FILE *sf; */ |
|
/* if (fpp != NULL) { */ |
/* mint = O_RDWR | O_CREAT; */ |
/* mstr = "r+"; */ |
/* human = "read/write"; */ |
/* lck = LOCK_EX; */ |
/* } else { */ |
/* mint = O_RDONLY; */ |
/* mstr = "r"; */ |
/* human = "reading"; */ |
/* lck = LOCK_SH; */ |
/* } */ |
/* setegid(egid); */ |
/* mask = umask(S_IWOTH); */ |
/* sd = open(_PATH_SCOREFILE, mint, 0666); */ |
/* (void)umask(mask); */ |
/* setegid(gid); */ |
/* if (sd < 0) { */ |
/* if (fpp == NULL) { */ |
/* nscores = 0; */ |
/* return; */ |
/* } */ |
/* err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); */ |
/* } */ |
/* setegid(egid); */ |
/* if ((sf = fdopen(sd, mstr)) == NULL) */ |
/* err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); */ |
/* setegid(gid); */ |
|
/* /\* */ |
/* * Grab a lock. */ |
/* *\/ */ |
/* if (flock(sd, lck)) */ |
/* warn("warning: score file %s cannot be locked", */ |
/* _PATH_SCOREFILE); */ |
|
/* nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); */ |
/* if (ferror(sf)) */ |
/* err(1, "error reading %s", _PATH_SCOREFILE); */ |
/* for (i = 0; i < nscores; i++) */ |
/* if (scores[i].hs_level < MINLEVEL || */ |
/* scores[i].hs_level > MAXLEVEL) */ |
/* errx(1, "scorefile %s corrupt", _PATH_SCOREFILE); */ |
|
/* if (fpp) */ |
/* *fpp = sf; */ |
/* else */ |
/* (void)fclose(sf); */ |
/* } */ |
|
void |
savescore(int level) |
{ |
return; |
} |
/* struct highscore *sp; */ |
/* int i; */ |
/* int change; */ |
/* FILE *sf; */ |
/* const char *me; */ |
|
/* getscores(&sf); */ |
/* gotscores = 1; */ |
/* (void)time(&now); */ |
|
/* /\* */ |
/* * Allow at most one score per person per level -- see if we */ |
/* * can replace an existing score, or (easiest) do nothing. */ |
/* * Otherwise add new score at end (there is always room). */ |
/* *\/ */ |
/* change = 0; */ |
/* me = thisuser(); */ |
/* for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { */ |
/* if (sp->hs_level != level || str_cmp(sp->hs_name, me) != 0) */ |
/* continue; */ |
/* if (score > sp->hs_score) { */ |
/* (void)printf("%s bettered %s %d score of %d!\n", */ |
/* "\nYou", "your old level", level, */ |
/* sp->hs_score * sp->hs_level); */ |
/* sp->hs_score = score; /\* new score *\/ */ |
/* sp->hs_time = now; /\* and time *\/ */ |
/* change = 1; */ |
/* } else if (score == sp->hs_score) { */ |
/* (void)printf("%s tied %s %d high score.\n", */ |
/* "\nYou", "your old level", level); */ |
/* sp->hs_time = now; /\* renew it *\/ */ |
/* change = 1; /\* gotta rewrite, sigh *\/ */ |
/* } /\* else new score < old score: do nothing *\/ */ |
/* break; */ |
/* } */ |
/* if (i >= nscores) { */ |
/* strlcpy(sp->hs_name, me, sizeof sp->hs_name); */ |
/* sp->hs_level = level; */ |
/* sp->hs_score = score; */ |
/* sp->hs_time = now; */ |
/* nscores++; */ |
/* change = 1; */ |
/* } */ |
|
/* if (change) { */ |
/* /\* */ |
/* * Sort & clean the scores, then rewrite. */ |
/* *\/ */ |
/* nscores = checkscores(scores, nscores); */ |
/* rewind(sf); */ |
/* if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || */ |
/* fflush(sf) == EOF) */ |
/* warnx("error writing %s: %s\n\t-- %s", */ |
/* _PATH_SCOREFILE, strerror(errno), */ |
/* "high scores may be damaged"); */ |
/* } */ |
/* (void)fclose(sf); /\* releases lock *\/ */ |
/* } */ |
|
/* |
* Get login name, or if that fails, get something suitable. |
* The result is always trimmed to fit in a score. |
*/ |
/* static char * */ |
/* thisuser(void) */ |
/* { */ |
/* const char *p; */ |
/* struct passwd *pw; */ |
/* static char u[sizeof(scores[0].hs_name)]; */ |
|
/* if (u[0]) */ |
/* return (u); */ |
/* p = getlogin(); */ |
/* if (p == NULL || *p == '\0') { */ |
/* pw = getpwuid(getuid()); */ |
/* if (pw != NULL) */ |
/* p = pw->pw_name; */ |
/* else */ |
/* p = " ???"; */ |
/* } */ |
/* strlcpy(u, p, sizeof(u)); */ |
/* return (u); */ |
/* } */ |
|
/* |
* Score comparison function for qsort. |
* |
* If two scores are equal, the person who had the score first is |
* listed first in the highscore file. |
*/ |
/* static int */ |
/* cmpscores(const void *x, const void *y) */ |
/* { */ |
/* const struct highscore *a, *b; */ |
/* long l; */ |
|
/* a = x; */ |
/* b = y; */ |
/* l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; */ |
/* if (l < 0) */ |
/* return (-1); */ |
/* if (l > 0) */ |
/* return (1); */ |
/* if (a->hs_time < b->hs_time) */ |
/* return (-1); */ |
/* if (a->hs_time > b->hs_time) */ |
/* return (1); */ |
/* return (0); */ |
/* } */ |
|
/* |
* If we've added a score to the file, we need to check the file and ensure |
* that this player has only a few entries. The number of entries is |
* controlled by MAXSCORES, and is to ensure that the highscore file is not |
* monopolised by just a few people. People who no longer have accounts are |
* only allowed the highest score. Scores older than EXPIRATION seconds are |
* removed, unless they are someone's personal best. |
* Caveat: the highest score on each level is always kept. |
*/ |
/* static int */ |
/* checkscores(struct highscore *hs, int num) */ |
/* { */ |
/* struct highscore *sp; */ |
/* int i, j, k, numnames; */ |
/* int levelfound[NLEVELS]; */ |
/* struct peruser { */ |
/* char *name; */ |
/* int times; */ |
/* } count[NUMSPOTS]; */ |
/* struct peruser *pu; */ |
|
/* /\* */ |
/* * Sort so that highest totals come first. */ |
/* * */ |
/* * levelfound[i] becomes set when the first high score for that */ |
/* * level is encountered. By definition this is the highest score. */ |
/* *\/ */ |
/* qsort((void *)hs, nscores, sizeof(*hs), cmpscores); */ |
/* for (i = MINLEVEL; i < NLEVELS; i++) */ |
/* levelfound[i] = 0; */ |
/* numnames = 0; */ |
/* for (i = 0, sp = hs; i < num;) { */ |
/* /\* */ |
/* * This is O(n^2), but do you think we care? */ |
/* *\/ */ |
/* for (j = 0, pu = count; j < numnames; j++, pu++) */ |
/* if (str_cmp(sp->hs_name, pu->name) == 0) */ |
/* break; */ |
/* if (j == numnames) { */ |
/* /\* */ |
/* * Add new user, set per-user count to 1. */ |
/* *\/ */ |
/* pu->name = sp->hs_name; */ |
/* pu->times = 1; */ |
/* numnames++; */ |
/* } else { */ |
/* /\* */ |
/* * Two ways to keep this score: */ |
/* * - Not too many (per user), still has acct, & */ |
/* * score not dated; or */ |
/* * - High score on this level. */ |
/* *\/ */ |
/* if ((pu->times < MAXSCORES && */ |
/* getpwnam(sp->hs_name) != NULL && */ |
/* sp->hs_time + EXPIRATION >= now) || */ |
/* levelfound[sp->hs_level] == 0) */ |
/* pu->times++; */ |
/* else { */ |
/* /\* */ |
/* * Delete this score, do not count it, */ |
/* * do not pass go, do not collect $200. */ |
/* *\/ */ |
/* num--; */ |
/* for (k = i; k < num; k++) */ |
/* hs[k] = hs[k + 1]; */ |
/* continue; */ |
/* } */ |
/* } */ |
/* levelfound[sp->hs_level] = 1; */ |
/* i++, sp++; */ |
/* } */ |
/* return (num > MAXHISCORES ? MAXHISCORES : num); */ |
/* } */ |
|
/* |
* Show current scores. This must be called after savescore, if |
* savescore is called at all, for two reasons: |
* - Showscores munches the time field. |
* - Even if that were not the case, a new score must be recorded |
* before it can be shown anyway. |
*/ |
/* |
void |
showscores(int level) |
{ |
return; |
} |
*/ |
/* struct highscore *sp; */ |
/* int i, n, c; */ |
/* const char *me; */ |
/* int levelfound[NLEVELS]; */ |
|
/* if (!gotscores) */ |
/* getscores((FILE **)NULL); */ |
/* (void)printf("\n\t\t Tetris High Scores\n"); */ |
|
/* /\* */ |
/* * If level == 0, the person has not played a game but just asked for */ |
/* * the high scores; we do not need to check for printing in highlight */ |
/* * mode. If SOstr is null, we can't do highlighting anyway. */ |
/* *\/ */ |
/* me = level && SOstr ? thisuser() : NULL; */ |
|
/* /\* */ |
/* * Set times to 0 except for high score on each level. */ |
/* *\/ */ |
/* for (i = MINLEVEL; i < NLEVELS; i++) */ |
/* levelfound[i] = 0; */ |
/* for (i = 0, sp = scores; i < nscores; i++, sp++) { */ |
/* if (levelfound[sp->hs_level]) */ |
/* sp->hs_time = 0; */ |
/* else { */ |
/* sp->hs_time = 1; */ |
/* levelfound[sp->hs_level] = 1; */ |
/* } */ |
/* } */ |
|
/* /\* */ |
/* * Page each screenful of scores. */ |
/* *\/ */ |
/* for (i = 0, sp = scores; i < nscores; sp += n) { */ |
/* n = 20; */ |
/* if (i + n > nscores) */ |
/* n = nscores - i; */ |
/* printem(level, i + 1, sp, n, me); */ |
/* if ((i += n) < nscores) { */ |
/* (void)printf("\nHit RETURN to continue."); */ |
/* (void)fflush(stdout); */ |
/* while ((c = getchar()) != '\n') */ |
/* if (c == EOF) */ |
/* break; */ |
/* (void)printf("\n"); */ |
/* } */ |
/* } */ |
|
/* if (nscores == 0) */ |
/* printf("\t\t\t - none to date.\n"); */ |
/* } */ |
|
/* static void */ |
/* printem(int level, int offset, struct highscore *hs, int n, const char *me) */ |
/* { */ |
/* struct highscore *sp; */ |
/* int row, highlight, i; */ |
/* char buf[100]; */ |
/* #define TITLE "Rank Score Name (points/level)" */ |
/* #define TITL2 "==========================================================" */ |
|
/* printf("%s\n%s\n", TITLE, TITL2); */ |
|
/* highlight = 0; */ |
|
/* for (row = 0; row < n; row++) { */ |
/* sp = &hs[row]; */ |
/* (void)snprintf(buf, sizeof(buf), */ |
/* "%3d%c %6d %-31s (%6d on %d)\n", */ |
/* row + offset, sp->hs_time ? '*' : ' ', */ |
/* sp->hs_score * sp->hs_level, */ |
/* sp->hs_name, sp->hs_score, sp->hs_level); */ |
/* /\* Print leaders every three lines *\/ */ |
/* if ((row + 1) % 3 == 0) { */ |
/* for (i = 0; i < sizeof(buf); i++) */ |
/* if (buf[i] == ' ') */ |
/* buf[i] = '_'; */ |
/* } */ |
/* /\* */ |
/* * Highlight if appropriate. This works because */ |
/* * we only get one score per level. */ |
/* *\/ */ |
/* if (me != NULL && */ |
/* sp->hs_level == level && */ |
/* sp->hs_score == score && */ |
/* str_cmp(sp->hs_name, me) == 0) { */ |
/* putpad(SOstr); */ |
/* highlight = 1; */ |
/* } */ |
/* (void)printf("%s", buf); */ |
/* if (highlight) { */ |
/* putpad(SEstr); */ |
/* highlight = 0; */ |
/* } */ |
/* } */ |
/* } */ |
|
/** @} |
*/ |
|