home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume21 / xsokoban / part01 / xsokoban-3.0 / score.c < prev   
Encoding:
C/C++ Source or Header  |  1993-12-18  |  8.9 KB  |  366 lines

  1. #include "config_local.h"
  2.  
  3. #include <stdio.h>
  4. #include <signal.h>
  5. #include <sys/types.h>
  6. #include <assert.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <netinet/in.h>
  10. #ifdef NEED_ENDIAN
  11. #include <machine/endian.h> /* for ntohs */
  12. #endif
  13.  
  14. #include "externs.h"
  15. #include "globals.h"
  16.  
  17. extern FILE *fopen();
  18.  
  19. extern char *username;
  20.  
  21. static short scoreentries;
  22. static struct st_entry {
  23.   char user[MAXUSERNAME];
  24.   unsigned short lv, pad1, mv, pad2, ps, pad3;
  25. } scoretable[MAXSCOREENTRIES];
  26.  
  27. static FILE *scorefile;
  28. static long sfdbn;
  29.  
  30. short LockScore(void)
  31. {
  32.      int i, fd;
  33.  
  34.      for (i = 0; i < 10; i++) {
  35.       fd = creat(LOCKFILE, 0666);
  36.       if (fd < 0)
  37.            sleep(1);
  38.       else
  39.            break;
  40.      }
  41.  
  42.      if (fd < 0) {
  43.       /* assume that the last process to muck with the score file */
  44.       /* is dead                              */
  45.       /* XXX Should really be checking the datestamps on the score*/
  46.       /* file to make sure some other process hasn't mucked with  */
  47.       /* in the last 10 seconds! */
  48.       unlink(LOCKFILE);
  49.       fd = creat(LOCKFILE, 0666);
  50.      }
  51.  
  52.      if (fd < 0)
  53.       return E_WRITESCORE;
  54.      else {
  55.       close(fd);
  56.       return 0;
  57.      }
  58. }
  59.  
  60. void UnlockScore(void)
  61. {
  62.      unlink(LOCKFILE);
  63. }
  64.      
  65. /* print out the score list */
  66. short OutputScore(void)
  67. {
  68.   short ret;
  69.  
  70.   if ((ret = LockScore()))
  71.        return ret;
  72.  
  73.   if ((ret = ReadScore()) == 0)
  74.     ShowScore();
  75.   UnlockScore();
  76.   return ((ret == 0) ? E_ENDGAME : ret);
  77. }
  78.  
  79. /* create a new score file */
  80. short MakeNewScore(void)
  81. {
  82.   short ret = 0;
  83.  
  84.   if ((ret = LockScore()))
  85.        return ret;
  86.   
  87.   scoreentries = 0;
  88.  
  89.   if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
  90.     ret = E_FOPENSCORE;
  91.   else {
  92.     sfdbn = fileno(scorefile);
  93.     if (write(sfdbn, &scoreentries, 2) != 2)
  94.       ret = E_WRITESCORE;
  95.     fclose(scorefile);
  96.   }
  97.   UnlockScore();
  98.   return ((ret == 0) ? E_ENDGAME : ret);
  99. }
  100.  
  101. /* get the players current level based on the level they last scored on */
  102. short GetUserLevel(short *lv)
  103. {
  104.   short ret = 0, pos;
  105.  
  106.   if ((ret = LockScore()))
  107.        return ret;
  108.  
  109.   if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
  110.     ret = E_FOPENSCORE;
  111.   else {
  112.     if ((ret = ReadScore()) == 0)
  113.       *lv = ((pos = FindUser()) > -1) ? scoretable[pos].lv + 1 : 1;
  114.   }
  115.   UnlockScore();
  116.   return (ret);
  117. }
  118.  
  119. /* Add a new score to the score file. Show the current scores if "show". */
  120. short Score(Boolean show)
  121. {
  122.   short ret;
  123.  
  124.   if ((ret = LockScore()))
  125.        return ret;
  126.   if ((ret = ReadScore()) == 0)
  127.     if ((ret = MakeScore()) == 0)
  128.       if ((ret = WriteScore()) == 0)
  129.     if (show) ShowScore();
  130.   UnlockScore();
  131.   return ((ret == 0) ? E_ENDGAME : ret);
  132. }
  133.  
  134. void ntohs_entry(struct st_entry *entry)
  135. {
  136.     entry->lv = ntohs(entry->lv);
  137.     entry->mv = ntohs(entry->mv);
  138.     entry->ps = ntohs(entry->ps);
  139. }
  140.  
  141. /* read in an existing score file.  Uses the ntoh() and hton() functions
  142.  * so that the score files transfer across systems.
  143.  */
  144. short ReadScore(void)
  145. {
  146.   short ret = 0;
  147.   long tmp;
  148.  
  149.   if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
  150.     ret = E_FOPENSCORE;
  151.   else {
  152.     sfdbn = fileno(scorefile);
  153.     if (read(sfdbn, &scoreentries, 2) != 2)
  154.       ret = E_READSCORE;
  155.     else {
  156.       scoreentries = ntohs(scoreentries);
  157.       tmp = scoreentries * sizeof(scoretable[0]);
  158.       if (read(sfdbn, &(scoretable[0]), tmp) != tmp)
  159.     ret = E_READSCORE;
  160.  
  161.       /* swap up for little-endian machines */
  162.       for (tmp = 0; tmp < scoreentries; tmp++) ntohs_entry(&scoretable[tmp]);
  163.     }
  164.     fclose(scorefile);
  165.   }
  166.   return ret;
  167. }
  168.  
  169. /* Return the solution rank for table index "j". The solution rank for
  170.    an entry is one greater than the number of entries that are better
  171.    than it, unless there is a better or equal solution that is by the
  172.    same person, in which case the solution rank is at least "BADSOLN".
  173.    If two solutions are equal, the one that was arrived at first, and
  174.    thus has a lower table index, is considered to be better.
  175.    One solution is at least as good as another solution if it is at
  176.    least as good in numbers of moves and pushes. Note that
  177.    non-comparable solutions may exist.
  178.  
  179.    The array "ignore" indicates that some scoretable entries should
  180.    be ignored for the purpose of computing rank.
  181. */
  182. #define BADSOLN 100
  183. int SolnRank(int j, Boolean *ignore)
  184. {
  185.     int i, rank = 1;
  186.     unsigned short level = scoretable[j].lv;
  187.     for (i = 0; i < j; i++) {
  188.     if ((!ignore || !ignore[i]) && scoretable[i].lv == level) {
  189.         if (scoretable[i].mv <= scoretable[j].mv &&
  190.         scoretable[i].ps <= scoretable[j].ps)
  191.         {
  192.         if (0 == strcmp(scoretable[i].user,
  193.                 scoretable[j].user))
  194.             rank = BADSOLN;
  195.         else
  196.             rank++;
  197.         }
  198.     }
  199.     }
  200.     return rank;
  201. }
  202.  
  203. /* Removes all score entries for a user who has multiple entries,
  204.  * that are for a level below the user's top level, and that are not "best
  205.  * solutions" as defined by "SolnRank". Also removes duplicate entries
  206.  * for a level that is equal to the user's top level, but which are not
  207.  * the user's best solution as defined by table position.
  208.  *
  209.  * The current implementation is O(n^2) in the number of actual score entries.
  210.  * A hash table would fix this.
  211.  */
  212.  
  213. void CleanupScoreTable()
  214. {
  215.     int i;
  216.     Boolean deletable[MAXSCOREENTRIES];
  217.     for (i = 0; i < scoreentries; i++) {
  218.     deletable[i] = _false_;
  219.     if (SolnRank(i, deletable) > MAXSOLNRANK) {
  220.         char *user = scoretable[i].user;
  221.         int j;
  222.         for (j = 0; j < i; j++) {
  223.         if (0 == strcmp(scoretable[j].user, user))
  224.           deletable[i] = _true_;
  225.         }
  226.     }
  227.     }
  228.     FlushDeletedScores(deletable);
  229. }
  230.  
  231. /* Deletes entries from the score table for which the boolean array
  232.    contains true.
  233. */
  234. void FlushDeletedScores(Boolean delete[])
  235. {
  236.     int i, k = 0;
  237.     for (i = 0; i < scoreentries; i++) {
  238.     if (i != k) CopyEntry(k, i);
  239.     if (!delete[i]) k++;
  240.     }
  241.     scoreentries = k;
  242. }
  243.  
  244. /* Adds a new user score to the score table, if appropriate. Users' top
  245.  * level scores, and the best scores for a particular level (in moves and
  246.  * pushes, separately considered), are always preserved.
  247.  */
  248. short MakeScore(void)
  249. {
  250.   short pos, i;
  251.  
  252.   pos = FindPos();        /* find the new score position */
  253.   if (pos > -1) {        /* score table not empty */
  254.       for (i = scoreentries; i > pos; i--)
  255.     CopyEntry(i, i - 1);
  256.     } else {
  257.       pos = scoreentries;
  258.     }
  259.  
  260.   strcpy(scoretable[pos].user, username);
  261.   scoretable[pos].lv = scorelevel;
  262.   scoretable[pos].mv = scoremoves;
  263.   scoretable[pos].ps = scorepushes;
  264.   scoreentries++;
  265.  
  266.   CleanupScoreTable();
  267.   if (scoreentries == MAXSCOREENTRIES)
  268.     return E_TOMUCHSE;
  269.   else
  270.     return 0;
  271. }
  272.  
  273.  
  274. /* searches the score table to find a specific player. */
  275. short FindUser(void)
  276. {
  277.   short i;
  278.   Boolean found = _false_;
  279.  
  280.   for (i = 0; (i < scoreentries) && (!found); i++)
  281.     found = (strcmp(scoretable[i].user, username) == 0);
  282.   return ((found) ? i - 1 : -1);
  283. }
  284.  
  285. /* finds the position for a new score in the score table */ 
  286. short FindPos(void)
  287. {
  288.   short i;
  289.   Boolean found = _false_;
  290.  
  291.   for (i = 0; (i < scoreentries) && (!found); i++)
  292.     found = ((scorelevel > scoretable[i].lv) ||
  293.          ((scorelevel == scoretable[i].lv) &&
  294.           (scoremoves < scoretable[i].mv)) ||
  295.          ((scorelevel == scoretable[i].lv) &&
  296.           (scoremoves == scoretable[i].mv) &&
  297.           (scorepushes < scoretable[i].ps)));
  298.   return ((found) ? i - 1 : -1);
  299. }
  300.  
  301. /* writes out the score table.  It uses ntoh() and hton() functions to make
  302.  * the scorefile transfer across systems.
  303.  */
  304. short WriteScore(void)
  305. {
  306.   short ret = 0;
  307.   long tmp;
  308.  
  309.   if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
  310.     ret = E_FOPENSCORE;
  311.   else {
  312.     sfdbn = fileno(scorefile);
  313.     scoreentries = htons(scoreentries);
  314.     if (write(sfdbn, &scoreentries, 2) != 2)
  315.       ret = E_WRITESCORE;
  316.     else {
  317.       scoreentries = ntohs(scoreentries);
  318.  
  319.       /* swap around for little-endian machines */
  320.       for (tmp = 0; tmp < scoreentries; tmp++) {
  321.     scoretable[tmp].lv = htons(scoretable[tmp].lv);
  322.     scoretable[tmp].mv = htons(scoretable[tmp].mv);
  323.     scoretable[tmp].ps = htons(scoretable[tmp].ps);
  324.       }
  325.       tmp = scoreentries * sizeof(scoretable[0]);
  326.       if (write(sfdbn, &(scoretable[0]), tmp) != tmp)
  327.     ret = E_WRITESCORE;
  328.  
  329.       /* and swap back for the rest of the run ... */
  330.       for (tmp = 0; tmp < scoreentries; tmp++) {
  331.     scoretable[tmp].lv = ntohs(scoretable[tmp].lv);
  332.     scoretable[tmp].mv = ntohs(scoretable[tmp].mv);
  333.     scoretable[tmp].ps = ntohs(scoretable[tmp].ps);
  334.       }
  335.     }
  336.     fclose(scorefile);
  337.   }
  338.   return ret;
  339. }
  340.  
  341.  
  342. /* displays the score table to the user */
  343. void ShowScore(void)
  344. {
  345.   register i;
  346.  
  347.   fprintf(stdout, "Rank      User     Level     Moves    Pushes\n");
  348.   fprintf(stdout, "============================================\n");
  349.   for (i = 0; i < scoreentries; i++) {
  350.     int rank = SolnRank(i, 0);
  351.     if (rank <= MAXSOLNRANK) fprintf(stdout, "%4d", rank);
  352.     else fprintf(stdout, "    ");
  353.     fprintf(stdout, "%10s  %8d  %8d  %8d\n", scoretable[i].user,
  354.         scoretable[i].lv, scoretable[i].mv, scoretable[i].ps);
  355.   }
  356. }
  357.  
  358. /* duplicates a score entry */
  359. void CopyEntry(short i1, short i2)
  360. {
  361.   strcpy(scoretable[i1].user, scoretable[i2].user);
  362.   scoretable[i1].lv = scoretable[i2].lv;
  363.   scoretable[i1].mv = scoretable[i2].mv;
  364.   scoretable[i1].ps = scoretable[i2].ps;
  365. }
  366.