Subversion Repositories HelenOS

Rev

Rev 4268 | Rev 4565 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $OpenBSD: scores.c,v 1.11 2006/04/20 03:25:36 ray Exp $ */
  2. /*  $NetBSD: scores.c,v 1.2 1995/04/22 07:42:38 cgd Exp $   */
  3.  
  4. /*-
  5.  * Copyright (c) 1992, 1993
  6.  *  The Regents of the University of California.  All rights reserved.
  7.  *
  8.  * This code is derived from software contributed to Berkeley by
  9.  * Chris Torek and Darren F. Provine.
  10.  *
  11.  * Redistribution and use in source and binary forms, with or without
  12.  * modification, are permitted provided that the following conditions
  13.  * are met:
  14.  * 1. Redistributions of source code must retain the above copyright
  15.  *    notice, this list of conditions and the following disclaimer.
  16.  * 2. Redistributions in binary form must reproduce the above copyright
  17.  *    notice, this list of conditions and the following disclaimer in the
  18.  *    documentation and/or other materials provided with the distribution.
  19.  * 3. Neither the name of the University nor the names of its contributors
  20.  *    may be used to endorse or promote products derived from this software
  21.  *    without specific prior written permission.
  22.  *
  23.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  24.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  27.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  29.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  30.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  32.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  33.  * SUCH DAMAGE.
  34.  *
  35.  *  @(#)scores.c    8.1 (Berkeley) 5/31/93
  36.  */
  37.  
  38. /** @addtogroup tetris
  39.  * @{
  40.  */
  41. /** @file
  42.  */
  43.  
  44. /*
  45.  * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu)
  46.  * modified 22 January 1992, to limit the number of entries any one
  47.  * person has.
  48.  *
  49.  * Major whacks since then.
  50.  */
  51. #include <errno.h>
  52. /* #include <err.h> */
  53. /* #include <fcntl.h> */
  54. /* #include <pwd.h> */
  55. #include <stdio.h>
  56. /* #include <stdlib.h> */
  57. #include <string.h>
  58. #include <io/console.h>
  59. #include <io/keycode.h>
  60. #include <vfs/vfs.h>
  61. #include <stdlib.h>
  62. /* #include <time.h> */
  63. /* #include <term.h> */
  64. /* #include <unistd.h> */
  65. /* #include <sys/param.h> */
  66. /* #include <sys/stat.h> */
  67. /* #include <sys/types.h> */
  68.  
  69. #include "pathnames.h"
  70. #include "screen.h"
  71. #include "tetris.h"
  72. #include "scores.h"
  73.  
  74. /*
  75.  * Within this code, we can hang onto one extra "high score", leaving
  76.  * room for our current score (whether or not it is high).
  77.  *
  78.  * We also sometimes keep tabs on the "highest" score on each level.
  79.  * As long as the scores are kept sorted, this is simply the first one at
  80.  * that level.
  81.  */
  82. #define NUMSPOTS (MAXHISCORES + 1)
  83. #define NLEVELS (MAXLEVEL + 1)
  84.  
  85. /* static time_t now; */
  86. /* static int nscores; */
  87. /* static int gotscores; */
  88. /* static struct highscore scores[NUMSPOTS]; */
  89. static struct highscore scores[NUMSPOTS];
  90.  
  91. /* static int checkscores(struct highscore *, int); */
  92. /* static int cmpscores(const void *, const void *); */
  93. /* static void getscores(FILE **); */
  94. /* static void printem(int, int, struct highscore *, int, const char *); */
  95. /* static char *thisuser(void); */
  96.  
  97. void showscores(int firstgame)
  98. {
  99.     int i;
  100.    
  101.     clear_screen();
  102.     moveto(10, 0);
  103.     printf("\tRank \tLevel \tName\t                     points\n");
  104.     printf("\t========================================================\n");
  105.     for (i = 0; i < NUMSPOTS - 1; i++) {
  106.         printf("\t%6d %6d %-16s %20d\n", i+1, scores[i].hs_level, scores[i].hs_name, scores[i].hs_score);
  107.     }
  108.     if (!firstgame) {
  109.         printf("\t========================================================\n");
  110.         printf("\t  Last %6d %-16s %20d\n", scores[NUMSPOTS - 1].hs_level, scores[NUMSPOTS - 1].hs_name, scores[NUMSPOTS - 1].hs_score);
  111.     }
  112.     printf("\n\n\n\n\tPress any key to return to main menu.");
  113.     getchar();
  114. }
  115.  
  116. /** Copy from hiscore table score with index src to dest
  117.  *
  118.  */
  119. static void copyhiscore(int dest, int src)
  120. {
  121.     str_cpy(scores[dest].hs_name, STR_BOUNDS(MAXLOGNAME) + 1,
  122.         scores[src].hs_name);
  123.     scores[dest].hs_score = scores[src].hs_score;  
  124.     scores[dest].hs_level = scores[src].hs_level;
  125. }
  126.  
  127. void insertscore(int score, int level)
  128. {
  129.     int i,j;
  130.     size_t off;
  131.     console_event_t ev;
  132.    
  133.     clear_screen();
  134.     moveto(10 , 10);
  135.     puts("Insert your name: ");
  136.     str_cpy(scores[NUMSPOTS - 1].hs_name, STR_BOUNDS(MAXLOGNAME) + 1,
  137.         "Player");
  138.     i = 6; off = 6;
  139.  
  140.     moveto(10 , 28);
  141.     printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
  142.  
  143.     while (1) {
  144.         fflush(stdout);
  145.         if (!console_get_event(fphone(stdin), &ev))
  146.             exit(1);
  147.  
  148.         if (ev.type == KEY_RELEASE)
  149.             continue;
  150.  
  151.         if (ev.key == KC_ENTER || ev.key == KC_NENTER)
  152.             break;
  153.  
  154.         if (ev.key == KC_BACKSPACE) {
  155.             if (i > 0) {
  156.                 wchar_t uc;
  157.  
  158.                 --i;
  159.                 while (off > 0) {
  160.                     --off;
  161.                     size_t otmp = off;
  162.                     uc = str_decode(scores[NUMSPOTS - 1].hs_name,
  163.                         &otmp, STR_BOUNDS(MAXLOGNAME) + 1);
  164.                     if (uc != U_SPECIAL)
  165.                         break;
  166.                 }
  167.  
  168.                 scores[NUMSPOTS - 1].hs_name[off] = '\0';
  169.             }
  170.         } else if (ev.c != '\0') {
  171.             if (i < (MAXLOGNAME - 1)) {
  172.                 if (chr_encode(ev.c, scores[NUMSPOTS - 1].hs_name,
  173.                     &off, STR_BOUNDS(MAXLOGNAME) + 1) == EOK) {
  174.                     ++i;
  175.                 }
  176.                 scores[NUMSPOTS - 1].hs_name[off] = '\0';
  177.             }
  178.         }
  179.        
  180.         moveto(10 , 28);
  181.         printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
  182.     }
  183.    
  184.     scores[NUMSPOTS - 1].hs_score = score; 
  185.     scores[NUMSPOTS - 1].hs_level = level;
  186.    
  187.     i = NUMSPOTS-1;
  188.     while ((i > 0) && (scores[i - 1].hs_score < score))
  189.         i--;
  190.  
  191.     for (j = NUMSPOTS - 2; j > i; j--) {
  192.         copyhiscore(j,j-1);
  193.     }
  194.     copyhiscore(i, NUMSPOTS - 1);  
  195. }
  196.  
  197. void initscores(void)
  198. {
  199.     int i;
  200.     for(i = 0; i < NUMSPOTS; i++) {
  201.         str_cpy(scores[i].hs_name, STR_BOUNDS(MAXLOGNAME) + 1, "HelenOS Team");
  202.         scores[i].hs_score = (NUMSPOTS - i) * 200; 
  203.         scores[i].hs_level = (i + 1 > MAXLEVEL?MAXLEVEL:i + 1);
  204.     }
  205. }
  206.  
  207. /*
  208.  * Read the score file.  Can be called from savescore (before showscores)
  209.  * or showscores (if savescore will not be called).  If the given pointer
  210.  * is not NULL, sets *fpp to an open file pointer that corresponds to a
  211.  * read/write score file that is locked with LOCK_EX.  Otherwise, the
  212.  * file is locked with LOCK_SH for the read and closed before return.
  213.  *
  214.  * Note, we assume closing the stdio file releases the lock.
  215.  */
  216. /* static void */
  217. /* getscores(FILE **fpp) */
  218. /* { */
  219. /*  int sd, mint, lck, mask, i; */
  220. /*  char *mstr, *human; */
  221. /*  FILE *sf; */
  222.  
  223. /*  if (fpp != NULL) { */
  224. /*      mint = O_RDWR | O_CREAT; */
  225. /*      mstr = "r+"; */
  226. /*      human = "read/write"; */
  227. /*      lck = LOCK_EX; */
  228. /*  } else { */
  229. /*      mint = O_RDONLY; */
  230. /*      mstr = "r"; */
  231. /*      human = "reading"; */
  232. /*      lck = LOCK_SH; */
  233. /*  } */
  234. /*  setegid(egid); */
  235. /*  mask = umask(S_IWOTH); */
  236. /*  sd = open(_PATH_SCOREFILE, mint, 0666); */
  237. /*  (void)umask(mask); */
  238. /*  setegid(gid); */
  239. /*  if (sd < 0) { */
  240. /*      if (fpp == NULL) { */
  241. /*          nscores = 0; */
  242. /*          return; */
  243. /*      } */
  244. /*      err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); */
  245. /*  } */
  246. /*  setegid(egid); */
  247. /*  if ((sf = fdopen(sd, mstr)) == NULL) */
  248. /*      err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); */
  249. /*  setegid(gid); */
  250.  
  251. /*  /\* */
  252. /*   * Grab a lock. */
  253. /*   *\/ */
  254. /*  if (flock(sd, lck)) */
  255. /*      warn("warning: score file %s cannot be locked", */
  256. /*          _PATH_SCOREFILE); */
  257.  
  258. /*  nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); */
  259. /*  if (ferror(sf)) */
  260. /*      err(1, "error reading %s", _PATH_SCOREFILE); */
  261. /*  for (i = 0; i < nscores; i++) */
  262. /*      if (scores[i].hs_level < MINLEVEL || */
  263. /*          scores[i].hs_level > MAXLEVEL) */
  264. /*          errx(1, "scorefile %s corrupt", _PATH_SCOREFILE); */
  265.  
  266. /*  if (fpp) */
  267. /*      *fpp = sf; */
  268. /*  else */
  269. /*      (void)fclose(sf); */
  270. /* } */
  271.  
  272. void
  273. savescore(int level)
  274. {
  275.     return;
  276. }
  277. /*  struct highscore *sp; */
  278. /*  int i; */
  279. /*  int change; */
  280. /*  FILE *sf; */
  281. /*  const char *me; */
  282.  
  283. /*  getscores(&sf); */
  284. /*  gotscores = 1; */
  285. /*  (void)time(&now); */
  286.  
  287. /*  /\* */
  288. /*   * Allow at most one score per person per level -- see if we */
  289. /*   * can replace an existing score, or (easiest) do nothing. */
  290. /*   * Otherwise add new score at end (there is always room). */
  291. /*   *\/ */
  292. /*  change = 0; */
  293. /*  me = thisuser(); */
  294. /*  for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { */
  295. /*      if (sp->hs_level != level || str_cmp(sp->hs_name, me) != 0) */
  296. /*          continue; */
  297. /*      if (score > sp->hs_score) { */
  298. /*          (void)printf("%s bettered %s %d score of %d!\n", */
  299. /*              "\nYou", "your old level", level, */
  300. /*              sp->hs_score * sp->hs_level); */
  301. /*          sp->hs_score = score;   /\* new score *\/ */
  302. /*          sp->hs_time = now;  /\* and time *\/ */
  303. /*          change = 1; */
  304. /*      } else if (score == sp->hs_score) { */
  305. /*          (void)printf("%s tied %s %d high score.\n", */
  306. /*              "\nYou", "your old level", level); */
  307. /*          sp->hs_time = now;  /\* renew it *\/ */
  308. /*          change = 1;     /\* gotta rewrite, sigh *\/ */
  309. /*      } /\* else new score < old score: do nothing *\/ */
  310. /*      break; */
  311. /*  } */
  312. /*  if (i >= nscores) { */
  313. /*      strlcpy(sp->hs_name, me, sizeof sp->hs_name); */
  314. /*      sp->hs_level = level; */
  315. /*      sp->hs_score = score; */
  316. /*      sp->hs_time = now; */
  317. /*      nscores++; */
  318. /*      change = 1; */
  319. /*  } */
  320.  
  321. /*  if (change) { */
  322. /*      /\* */
  323. /*       * Sort & clean the scores, then rewrite. */
  324. /*       *\/ */
  325. /*      nscores = checkscores(scores, nscores); */
  326. /*      rewind(sf); */
  327. /*      if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || */
  328. /*          fflush(sf) == EOF) */
  329. /*          warnx("error writing %s: %s\n\t-- %s", */
  330. /*              _PATH_SCOREFILE, strerror(errno), */
  331. /*              "high scores may be damaged"); */
  332. /*  } */
  333. /*  (void)fclose(sf);   /\* releases lock *\/ */
  334. /* } */
  335.  
  336. /*
  337.  * Get login name, or if that fails, get something suitable.
  338.  * The result is always trimmed to fit in a score.
  339.  */
  340. /* static char * */
  341. /* thisuser(void) */
  342. /* { */
  343. /*  const char *p; */
  344. /*  struct passwd *pw; */
  345. /*  static char u[sizeof(scores[0].hs_name)]; */
  346.  
  347. /*  if (u[0]) */
  348. /*      return (u); */
  349. /*  p = getlogin(); */
  350. /*  if (p == NULL || *p == '\0') { */
  351. /*      pw = getpwuid(getuid()); */
  352. /*      if (pw != NULL) */
  353. /*          p = pw->pw_name; */
  354. /*      else */
  355. /*          p = "  ???"; */
  356. /*  } */
  357. /*  strlcpy(u, p, sizeof(u)); */
  358. /*  return (u); */
  359. /* } */
  360.  
  361. /*
  362.  * Score comparison function for qsort.
  363.  *
  364.  * If two scores are equal, the person who had the score first is
  365.  * listed first in the highscore file.
  366.  */
  367. /* static int */
  368. /* cmpscores(const void *x, const void *y) */
  369. /* { */
  370. /*  const struct highscore *a, *b; */
  371. /*  long l; */
  372.  
  373. /*  a = x; */
  374. /*  b = y; */
  375. /*  l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; */
  376. /*  if (l < 0) */
  377. /*      return (-1); */
  378. /*  if (l > 0) */
  379. /*      return (1); */
  380. /*  if (a->hs_time < b->hs_time) */
  381. /*      return (-1); */
  382. /*  if (a->hs_time > b->hs_time) */
  383. /*      return (1); */
  384. /*  return (0); */
  385. /* } */
  386.  
  387. /*
  388.  * If we've added a score to the file, we need to check the file and ensure
  389.  * that this player has only a few entries.  The number of entries is
  390.  * controlled by MAXSCORES, and is to ensure that the highscore file is not
  391.  * monopolised by just a few people.  People who no longer have accounts are
  392.  * only allowed the highest score.  Scores older than EXPIRATION seconds are
  393.  * removed, unless they are someone's personal best.
  394.  * Caveat:  the highest score on each level is always kept.
  395.  */
  396. /* static int */
  397. /* checkscores(struct highscore *hs, int num) */
  398. /* { */
  399. /*  struct highscore *sp; */
  400. /*  int i, j, k, numnames; */
  401. /*  int levelfound[NLEVELS]; */
  402. /*  struct peruser { */
  403. /*      char *name; */
  404. /*      int times; */
  405. /*  } count[NUMSPOTS]; */
  406. /*  struct peruser *pu; */
  407.  
  408. /*  /\* */
  409. /*   * Sort so that highest totals come first. */
  410. /*   * */
  411. /*   * levelfound[i] becomes set when the first high score for that */
  412. /*   * level is encountered.  By definition this is the highest score. */
  413. /*   *\/ */
  414. /*  qsort((void *)hs, nscores, sizeof(*hs), cmpscores); */
  415. /*  for (i = MINLEVEL; i < NLEVELS; i++) */
  416. /*      levelfound[i] = 0; */
  417. /*  numnames = 0; */
  418. /*  for (i = 0, sp = hs; i < num;) { */
  419. /*      /\* */
  420. /*       * This is O(n^2), but do you think we care? */
  421. /*       *\/ */
  422. /*      for (j = 0, pu = count; j < numnames; j++, pu++) */
  423. /*          if (str_cmp(sp->hs_name, pu->name) == 0) */
  424. /*              break; */
  425. /*      if (j == numnames) { */
  426. /*          /\* */
  427. /*           * Add new user, set per-user count to 1. */
  428. /*           *\/ */
  429. /*          pu->name = sp->hs_name; */
  430. /*          pu->times = 1; */
  431. /*          numnames++; */
  432. /*      } else { */
  433. /*          /\* */
  434. /*           * Two ways to keep this score: */
  435. /*           * - Not too many (per user), still has acct, & */
  436. /*           *  score not dated; or */
  437. /*           * - High score on this level. */
  438. /*           *\/ */
  439. /*          if ((pu->times < MAXSCORES && */
  440. /*               getpwnam(sp->hs_name) != NULL && */
  441. /*               sp->hs_time + EXPIRATION >= now) || */
  442. /*              levelfound[sp->hs_level] == 0) */
  443. /*              pu->times++; */
  444. /*          else { */
  445. /*              /\* */
  446. /*               * Delete this score, do not count it, */
  447. /*               * do not pass go, do not collect $200. */
  448. /*               *\/ */
  449. /*              num--; */
  450. /*              for (k = i; k < num; k++) */
  451. /*                  hs[k] = hs[k + 1]; */
  452. /*              continue; */
  453. /*          } */
  454. /*      } */
  455. /*      levelfound[sp->hs_level] = 1; */
  456. /*      i++, sp++; */
  457. /*  } */
  458. /*  return (num > MAXHISCORES ? MAXHISCORES : num); */
  459. /* } */
  460.  
  461. /*
  462.  * Show current scores.  This must be called after savescore, if
  463.  * savescore is called at all, for two reasons:
  464.  * - Showscores munches the time field.
  465.  * - Even if that were not the case, a new score must be recorded
  466.  *   before it can be shown anyway.
  467.  */
  468. /*
  469. void
  470. showscores(int level)
  471. {
  472.     return;
  473. }
  474. */
  475. /*  struct highscore *sp; */
  476. /*  int i, n, c; */
  477. /*  const char *me; */
  478. /*  int levelfound[NLEVELS]; */
  479.  
  480. /*  if (!gotscores) */
  481. /*      getscores((FILE **)NULL); */
  482. /*  (void)printf("\n\t\t    Tetris High Scores\n"); */
  483.  
  484. /*  /\* */
  485. /*   * If level == 0, the person has not played a game but just asked for */
  486. /*   * the high scores; we do not need to check for printing in highlight */
  487. /*   * mode.  If SOstr is null, we can't do highlighting anyway. */
  488. /*   *\/ */
  489. /*  me = level && SOstr ? thisuser() : NULL; */
  490.  
  491. /*  /\* */
  492. /*   * Set times to 0 except for high score on each level. */
  493. /*   *\/ */
  494. /*  for (i = MINLEVEL; i < NLEVELS; i++) */
  495. /*      levelfound[i] = 0; */
  496. /*  for (i = 0, sp = scores; i < nscores; i++, sp++) { */
  497. /*      if (levelfound[sp->hs_level]) */
  498. /*          sp->hs_time = 0; */
  499. /*      else { */
  500. /*          sp->hs_time = 1; */
  501. /*          levelfound[sp->hs_level] = 1; */
  502. /*      } */
  503. /*  } */
  504.  
  505. /*  /\* */
  506. /*   * Page each screenful of scores. */
  507. /*   *\/ */
  508. /*  for (i = 0, sp = scores; i < nscores; sp += n) { */
  509. /*      n = 20; */
  510. /*      if (i + n > nscores) */
  511. /*          n = nscores - i; */
  512. /*      printem(level, i + 1, sp, n, me); */
  513. /*      if ((i += n) < nscores) { */
  514. /*          (void)printf("\nHit RETURN to continue."); */
  515. /*          (void)fflush(stdout); */
  516. /*          while ((c = getchar()) != '\n') */
  517. /*              if (c == EOF) */
  518. /*                  break; */
  519. /*          (void)printf("\n"); */
  520. /*      } */
  521. /*  } */
  522.  
  523. /*  if (nscores == 0) */
  524. /*      printf("\t\t\t      - none to date.\n"); */
  525. /* } */
  526.  
  527. /* static void */
  528. /* printem(int level, int offset, struct highscore *hs, int n, const char *me) */
  529. /* { */
  530. /*  struct highscore *sp; */
  531. /*  int row, highlight, i; */
  532. /*  char buf[100]; */
  533. /* #define  TITLE "Rank  Score   Name                          (points/level)" */
  534. /* #define  TITL2 "==========================================================" */
  535.  
  536. /*  printf("%s\n%s\n", TITLE, TITL2); */
  537.  
  538. /*  highlight = 0; */
  539.  
  540. /*  for (row = 0; row < n; row++) { */
  541. /*      sp = &hs[row]; */
  542. /*      (void)snprintf(buf, sizeof(buf), */
  543. /*          "%3d%c %6d  %-31s (%6d on %d)\n", */
  544. /*          row + offset, sp->hs_time ? '*' : ' ', */
  545. /*          sp->hs_score * sp->hs_level, */
  546. /*          sp->hs_name, sp->hs_score, sp->hs_level); */
  547. /*      /\* Print leaders every three lines *\/ */
  548. /*      if ((row + 1) % 3 == 0) { */
  549. /*          for (i = 0; i < sizeof(buf); i++) */
  550. /*              if (buf[i] == ' ') */
  551. /*                  buf[i] = '_'; */
  552. /*      } */
  553. /*      /\* */
  554. /*       * Highlight if appropriate.  This works because */
  555. /*       * we only get one score per level. */
  556. /*       *\/ */
  557. /*      if (me != NULL && */
  558. /*          sp->hs_level == level && */
  559. /*          sp->hs_score == score && */
  560. /*          str_cmp(sp->hs_name, me) == 0) { */
  561. /*          putpad(SOstr); */
  562. /*          highlight = 1; */
  563. /*      } */
  564. /*      (void)printf("%s", buf); */
  565. /*      if (highlight) { */
  566. /*          putpad(SEstr); */
  567. /*          highlight = 0; */
  568. /*      } */
  569. /*  } */
  570. /* } */
  571.  
  572. /** @}
  573.  */
  574.  
  575.