source: mainline/uspace/app/tetris/scores.c@ 99272a3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 99272a3 was 0c25c10, checked in by Martin Decky <martin@…>, 17 years ago

update for latest I/O changes

  • Property mode set to 100644
File size: 16.2 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 <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]; */
89static 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
97void 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 */
119static 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
127void 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
197void 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
272void
273savescore(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/*
469void
470showscores(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
Note: See TracBrowser for help on using the repository browser.