source: mainline/uspace/app/tetris/scores.c@ 7afb4a5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7afb4a5 was 92fd52d7, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

Nuke strcpy() and strcmp().

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