source: mainline/tetris/scores.c@ e9073f2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e9073f2 was 9996ed5, checked in by Josef Cejka <malyzelenyhnus@…>, 20 years ago

Tetris has now a new menu.
Hiscore table added.

  • Property mode set to 100644
File size: 15.5 KB
Line 
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/*
39 * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu)
40 * modified 22 January 1992, to limit the number of entries any one
41 * person has.
42 *
43 * Major whacks since then.
44 */
45#include <errno.h>
46/* #include <err.h> */
47/* #include <fcntl.h> */
48/* #include <pwd.h> */
49 #include <stdio.h>
50/* #include <stdlib.h> */
51#include <string.h>
52/* #include <time.h> */
53/* #include <term.h> */
54/* #include <unistd.h> */
55/* #include <sys/param.h> */
56/* #include <sys/stat.h> */
57/* #include <sys/types.h> */
58
59#include "pathnames.h"
60#include "screen.h"
61#include "tetris.h"
62#include "scores.h"
63
64/*
65 * Within this code, we can hang onto one extra "high score", leaving
66 * room for our current score (whether or not it is high).
67 *
68 * We also sometimes keep tabs on the "highest" score on each level.
69 * As long as the scores are kept sorted, this is simply the first one at
70 * that level.
71 */
72#define NUMSPOTS (MAXHISCORES + 1)
73#define NLEVELS (MAXLEVEL + 1)
74
75/* static time_t now; */
76/* static int nscores; */
77/* static int gotscores; */
78/* static struct highscore scores[NUMSPOTS]; */
79static struct highscore scores[NUMSPOTS];
80
81/* static int checkscores(struct highscore *, int); */
82/* static int cmpscores(const void *, const void *); */
83/* static void getscores(FILE **); */
84/* static void printem(int, int, struct highscore *, int, const char *); */
85/* static char *thisuser(void); */
86
87void showscores(int firstgame)
88{
89 int i;
90
91 clear_screen();
92 moveto(10, 0);
93 printf("\tRank \tLevel \tName\t points\n");
94 printf("\t========================================================\n");
95 for (i = 0; i < NUMSPOTS - 1; i++) {
96 printf("\t%6d %6d %-16s %20d\n", i+1, scores[i].hs_level, scores[i].hs_name, scores[i].hs_score);
97 }
98 if (!firstgame) {
99 printf("\t========================================================\n");
100 printf("\t Last %6d %-16s %20d\n", scores[NUMSPOTS - 1].hs_level, scores[NUMSPOTS - 1].hs_name, scores[NUMSPOTS - 1].hs_score);
101 }
102 printf("\n\n\n\n\tPress any key to return to main menu.");
103 getchar();
104}
105
106/** Copy from hiscore table score with index src to dest
107 *
108 */
109static void copyhiscore(int dest, int src)
110{
111 strcpy(scores[dest].hs_name, scores[src].hs_name);
112 scores[dest].hs_score = scores[src].hs_score;
113 scores[dest].hs_level = scores[src].hs_level;
114}
115
116void insertscore(int score, int level)
117{
118 int i,j;
119 int key;
120
121
122 clear_screen();
123 moveto(10 , 10);
124 puts("Insert your name: ");
125 strncpy(scores[NUMSPOTS - 1].hs_name, "Player", MAXLOGNAME);
126 i = 6;
127
128 moveto(10 , 28);
129 printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
130 key = getchar();
131 while(key != '\n') {
132 if (key == '\b') {
133 if (i > 0)
134 scores[NUMSPOTS - 1].hs_name[--i] = '\0';
135 } else {
136 if (i < (MAXLOGNAME - 1))
137 scores[NUMSPOTS - 1].hs_name[i++] = key;
138 scores[NUMSPOTS - 1].hs_name[i] = '\0';
139 }
140
141 moveto(10 , 28);
142 printf("%s%.*s",scores[NUMSPOTS - 1].hs_name,MAXLOGNAME-i,"........................................");
143
144 key = getchar();
145 }
146
147 scores[NUMSPOTS - 1].hs_score = score;
148 scores[NUMSPOTS - 1].hs_level = level;
149
150 i = NUMSPOTS-1;
151 while ((i > 0) && (scores[i - 1].hs_score < score))
152 i--;
153
154 for (j = NUMSPOTS - 2; j > i; j--) {
155 copyhiscore(j,j-1);
156 }
157 copyhiscore(i, NUMSPOTS - 1);
158}
159
160void initscores(void)
161{
162 int i;
163 for(i = 0; i < NUMSPOTS; i++) {
164 strncpy(scores[i].hs_name, "HelenOS Team", MAXLOGNAME);
165 scores[i].hs_score = (NUMSPOTS - i) * 200;
166 scores[i].hs_level = (i + 1 > MAXLEVEL?MAXLEVEL:i + 1);
167 }
168}
169
170/*
171 * Read the score file. Can be called from savescore (before showscores)
172 * or showscores (if savescore will not be called). If the given pointer
173 * is not NULL, sets *fpp to an open file pointer that corresponds to a
174 * read/write score file that is locked with LOCK_EX. Otherwise, the
175 * file is locked with LOCK_SH for the read and closed before return.
176 *
177 * Note, we assume closing the stdio file releases the lock.
178 */
179/* static void */
180/* getscores(FILE **fpp) */
181/* { */
182/* int sd, mint, lck, mask, i; */
183/* char *mstr, *human; */
184/* FILE *sf; */
185
186/* if (fpp != NULL) { */
187/* mint = O_RDWR | O_CREAT; */
188/* mstr = "r+"; */
189/* human = "read/write"; */
190/* lck = LOCK_EX; */
191/* } else { */
192/* mint = O_RDONLY; */
193/* mstr = "r"; */
194/* human = "reading"; */
195/* lck = LOCK_SH; */
196/* } */
197/* setegid(egid); */
198/* mask = umask(S_IWOTH); */
199/* sd = open(_PATH_SCOREFILE, mint, 0666); */
200/* (void)umask(mask); */
201/* setegid(gid); */
202/* if (sd < 0) { */
203/* if (fpp == NULL) { */
204/* nscores = 0; */
205/* return; */
206/* } */
207/* err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); */
208/* } */
209/* setegid(egid); */
210/* if ((sf = fdopen(sd, mstr)) == NULL) */
211/* err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); */
212/* setegid(gid); */
213
214/* /\* */
215/* * Grab a lock. */
216/* *\/ */
217/* if (flock(sd, lck)) */
218/* warn("warning: score file %s cannot be locked", */
219/* _PATH_SCOREFILE); */
220
221/* nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); */
222/* if (ferror(sf)) */
223/* err(1, "error reading %s", _PATH_SCOREFILE); */
224/* for (i = 0; i < nscores; i++) */
225/* if (scores[i].hs_level < MINLEVEL || */
226/* scores[i].hs_level > MAXLEVEL) */
227/* errx(1, "scorefile %s corrupt", _PATH_SCOREFILE); */
228
229/* if (fpp) */
230/* *fpp = sf; */
231/* else */
232/* (void)fclose(sf); */
233/* } */
234
235void
236savescore(int level)
237{
238 return;
239}
240/* struct highscore *sp; */
241/* int i; */
242/* int change; */
243/* FILE *sf; */
244/* const char *me; */
245
246/* getscores(&sf); */
247/* gotscores = 1; */
248/* (void)time(&now); */
249
250/* /\* */
251/* * Allow at most one score per person per level -- see if we */
252/* * can replace an existing score, or (easiest) do nothing. */
253/* * Otherwise add new score at end (there is always room). */
254/* *\/ */
255/* change = 0; */
256/* me = thisuser(); */
257/* for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { */
258/* if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0) */
259/* continue; */
260/* if (score > sp->hs_score) { */
261/* (void)printf("%s bettered %s %d score of %d!\n", */
262/* "\nYou", "your old level", level, */
263/* sp->hs_score * sp->hs_level); */
264/* sp->hs_score = score; /\* new score *\/ */
265/* sp->hs_time = now; /\* and time *\/ */
266/* change = 1; */
267/* } else if (score == sp->hs_score) { */
268/* (void)printf("%s tied %s %d high score.\n", */
269/* "\nYou", "your old level", level); */
270/* sp->hs_time = now; /\* renew it *\/ */
271/* change = 1; /\* gotta rewrite, sigh *\/ */
272/* } /\* else new score < old score: do nothing *\/ */
273/* break; */
274/* } */
275/* if (i >= nscores) { */
276/* strlcpy(sp->hs_name, me, sizeof sp->hs_name); */
277/* sp->hs_level = level; */
278/* sp->hs_score = score; */
279/* sp->hs_time = now; */
280/* nscores++; */
281/* change = 1; */
282/* } */
283
284/* if (change) { */
285/* /\* */
286/* * Sort & clean the scores, then rewrite. */
287/* *\/ */
288/* nscores = checkscores(scores, nscores); */
289/* rewind(sf); */
290/* if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || */
291/* fflush(sf) == EOF) */
292/* warnx("error writing %s: %s\n\t-- %s", */
293/* _PATH_SCOREFILE, strerror(errno), */
294/* "high scores may be damaged"); */
295/* } */
296/* (void)fclose(sf); /\* releases lock *\/ */
297/* } */
298
299/*
300 * Get login name, or if that fails, get something suitable.
301 * The result is always trimmed to fit in a score.
302 */
303/* static char * */
304/* thisuser(void) */
305/* { */
306/* const char *p; */
307/* struct passwd *pw; */
308/* static char u[sizeof(scores[0].hs_name)]; */
309
310/* if (u[0]) */
311/* return (u); */
312/* p = getlogin(); */
313/* if (p == NULL || *p == '\0') { */
314/* pw = getpwuid(getuid()); */
315/* if (pw != NULL) */
316/* p = pw->pw_name; */
317/* else */
318/* p = " ???"; */
319/* } */
320/* strlcpy(u, p, sizeof(u)); */
321/* return (u); */
322/* } */
323
324/*
325 * Score comparison function for qsort.
326 *
327 * If two scores are equal, the person who had the score first is
328 * listed first in the highscore file.
329 */
330/* static int */
331/* cmpscores(const void *x, const void *y) */
332/* { */
333/* const struct highscore *a, *b; */
334/* long l; */
335
336/* a = x; */
337/* b = y; */
338/* l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; */
339/* if (l < 0) */
340/* return (-1); */
341/* if (l > 0) */
342/* return (1); */
343/* if (a->hs_time < b->hs_time) */
344/* return (-1); */
345/* if (a->hs_time > b->hs_time) */
346/* return (1); */
347/* return (0); */
348/* } */
349
350/*
351 * If we've added a score to the file, we need to check the file and ensure
352 * that this player has only a few entries. The number of entries is
353 * controlled by MAXSCORES, and is to ensure that the highscore file is not
354 * monopolised by just a few people. People who no longer have accounts are
355 * only allowed the highest score. Scores older than EXPIRATION seconds are
356 * removed, unless they are someone's personal best.
357 * Caveat: the highest score on each level is always kept.
358 */
359/* static int */
360/* checkscores(struct highscore *hs, int num) */
361/* { */
362/* struct highscore *sp; */
363/* int i, j, k, numnames; */
364/* int levelfound[NLEVELS]; */
365/* struct peruser { */
366/* char *name; */
367/* int times; */
368/* } count[NUMSPOTS]; */
369/* struct peruser *pu; */
370
371/* /\* */
372/* * Sort so that highest totals come first. */
373/* * */
374/* * levelfound[i] becomes set when the first high score for that */
375/* * level is encountered. By definition this is the highest score. */
376/* *\/ */
377/* qsort((void *)hs, nscores, sizeof(*hs), cmpscores); */
378/* for (i = MINLEVEL; i < NLEVELS; i++) */
379/* levelfound[i] = 0; */
380/* numnames = 0; */
381/* for (i = 0, sp = hs; i < num;) { */
382/* /\* */
383/* * This is O(n^2), but do you think we care? */
384/* *\/ */
385/* for (j = 0, pu = count; j < numnames; j++, pu++) */
386/* if (strcmp(sp->hs_name, pu->name) == 0) */
387/* break; */
388/* if (j == numnames) { */
389/* /\* */
390/* * Add new user, set per-user count to 1. */
391/* *\/ */
392/* pu->name = sp->hs_name; */
393/* pu->times = 1; */
394/* numnames++; */
395/* } else { */
396/* /\* */
397/* * Two ways to keep this score: */
398/* * - Not too many (per user), still has acct, & */
399/* * score not dated; or */
400/* * - High score on this level. */
401/* *\/ */
402/* if ((pu->times < MAXSCORES && */
403/* getpwnam(sp->hs_name) != NULL && */
404/* sp->hs_time + EXPIRATION >= now) || */
405/* levelfound[sp->hs_level] == 0) */
406/* pu->times++; */
407/* else { */
408/* /\* */
409/* * Delete this score, do not count it, */
410/* * do not pass go, do not collect $200. */
411/* *\/ */
412/* num--; */
413/* for (k = i; k < num; k++) */
414/* hs[k] = hs[k + 1]; */
415/* continue; */
416/* } */
417/* } */
418/* levelfound[sp->hs_level] = 1; */
419/* i++, sp++; */
420/* } */
421/* return (num > MAXHISCORES ? MAXHISCORES : num); */
422/* } */
423
424/*
425 * Show current scores. This must be called after savescore, if
426 * savescore is called at all, for two reasons:
427 * - Showscores munches the time field.
428 * - Even if that were not the case, a new score must be recorded
429 * before it can be shown anyway.
430 */
431/*
432void
433showscores(int level)
434{
435 return;
436}
437*/
438/* struct highscore *sp; */
439/* int i, n, c; */
440/* const char *me; */
441/* int levelfound[NLEVELS]; */
442
443/* if (!gotscores) */
444/* getscores((FILE **)NULL); */
445/* (void)printf("\n\t\t Tetris High Scores\n"); */
446
447/* /\* */
448/* * If level == 0, the person has not played a game but just asked for */
449/* * the high scores; we do not need to check for printing in highlight */
450/* * mode. If SOstr is null, we can't do highlighting anyway. */
451/* *\/ */
452/* me = level && SOstr ? thisuser() : NULL; */
453
454/* /\* */
455/* * Set times to 0 except for high score on each level. */
456/* *\/ */
457/* for (i = MINLEVEL; i < NLEVELS; i++) */
458/* levelfound[i] = 0; */
459/* for (i = 0, sp = scores; i < nscores; i++, sp++) { */
460/* if (levelfound[sp->hs_level]) */
461/* sp->hs_time = 0; */
462/* else { */
463/* sp->hs_time = 1; */
464/* levelfound[sp->hs_level] = 1; */
465/* } */
466/* } */
467
468/* /\* */
469/* * Page each screenful of scores. */
470/* *\/ */
471/* for (i = 0, sp = scores; i < nscores; sp += n) { */
472/* n = 20; */
473/* if (i + n > nscores) */
474/* n = nscores - i; */
475/* printem(level, i + 1, sp, n, me); */
476/* if ((i += n) < nscores) { */
477/* (void)printf("\nHit RETURN to continue."); */
478/* (void)fflush(stdout); */
479/* while ((c = getchar()) != '\n') */
480/* if (c == EOF) */
481/* break; */
482/* (void)printf("\n"); */
483/* } */
484/* } */
485
486/* if (nscores == 0) */
487/* printf("\t\t\t - none to date.\n"); */
488/* } */
489
490/* static void */
491/* printem(int level, int offset, struct highscore *hs, int n, const char *me) */
492/* { */
493/* struct highscore *sp; */
494/* int row, highlight, i; */
495/* char buf[100]; */
496/* #define TITLE "Rank Score Name (points/level)" */
497/* #define TITL2 "==========================================================" */
498
499/* printf("%s\n%s\n", TITLE, TITL2); */
500
501/* highlight = 0; */
502
503/* for (row = 0; row < n; row++) { */
504/* sp = &hs[row]; */
505/* (void)snprintf(buf, sizeof(buf), */
506/* "%3d%c %6d %-31s (%6d on %d)\n", */
507/* row + offset, sp->hs_time ? '*' : ' ', */
508/* sp->hs_score * sp->hs_level, */
509/* sp->hs_name, sp->hs_score, sp->hs_level); */
510/* /\* Print leaders every three lines *\/ */
511/* if ((row + 1) % 3 == 0) { */
512/* for (i = 0; i < sizeof(buf); i++) */
513/* if (buf[i] == ' ') */
514/* buf[i] = '_'; */
515/* } */
516/* /\* */
517/* * Highlight if appropriate. This works because */
518/* * we only get one score per level. */
519/* *\/ */
520/* if (me != NULL && */
521/* sp->hs_level == level && */
522/* sp->hs_score == score && */
523/* strcmp(sp->hs_name, me) == 0) { */
524/* putpad(SOstr); */
525/* highlight = 1; */
526/* } */
527/* (void)printf("%s", buf); */
528/* if (highlight) { */
529/* putpad(SEstr); */
530/* highlight = 0; */
531/* } */
532/* } */
533/* } */
Note: See TracBrowser for help on using the repository browser.