1
2 /*-
3 # X-BASED PYRAMINX(tm)
4 #
5 # xpyraminx.c
6 #
7 ###
8 #
9 # Copyright (c) 1994 - 99 David Albert Bagley, bagleyd@tux.org
10 #
11 # All Rights Reserved
12 #
13 # Permission to use, copy, modify, and distribute this software and
14 # its documentation for any purpose and without fee is hereby granted,
15 # provided that the above copyright notice appear in all copies and
16 # that both that copyright notice and this permission notice appear in
17 # supporting documentation, and that the name of the author not be
18 # used in advertising or publicity pertaining to distribution of the
19 # software without specific, written prior permission.
20 #
21 # This program is distributed in the hope that it will be "playable",
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 #
25 */
26
27 /*-
28 Version 4: 94/05/31 Xt
29 Version 3: 93/04/01 Motif
30 Version 2: 92/01/29 XView
31 Version 1: 91/03/19 SunView
32 */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #ifdef VMS
38 #include <unixlib.h>
39 #define getlogin() cuserid(NULL)
40 #else
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #endif
45 #if HAVE_FCNTL_H
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #endif
50 #include <X11/Intrinsic.h>
51 #include <X11/StringDefs.h>
52 #include <X11/Shell.h>
53 #include <X11/cursorfont.h>
54 #include "Pyraminx.h"
55 #include "pyraminx.xbm"
56
57 #ifndef SCOREFILE
58 #define SCOREFILE "/usr/games/lib/pyraminx.scores"
59 #endif
60
61 /* The following are in PyraminxP.h also */
62 #define MINTETRAS 1
63 #define PERIOD2 2
64 #define PERIOD3 3
65 #define BOTH 4
66 #define MAXMODES 3
67
68 #define MAXTETRAS 7
69 #define MAXRECORD 32767
70 #define FILENAMELEN 1024
71 #define USERNAMELEN 128
72 #define NOACCESS "noaccess"
73 #define NOBODY "nobody"
74
75 typedef struct {
76 int score;
77 char name[USERNAMELEN];
78 } GameRecord;
79
80 static void Initialize(Widget w);
81 static void CallbackPyraminx(Widget w, caddr_t clientData,
82 pyraminxCallbackStruct * callData);
83
84 static void PrintRecord(int size, int mode,
85 Boolean orient, Boolean sticky, Boolean practice, char *record);
86 static Boolean HandleSolved(int counter, int size, int mode,
87 Boolean orient, Boolean sticky);
88 static void PrintState(Widget w, char *prog, int mode, int size,
89 Boolean sticky, int moves, char *record, char *message);
90 static void InitRecords(void);
91 static void ReadRecords(void);
92 static void WriteRecords(void);
93
94 static Arg arg[5];
95 static GameRecord pyraminxRecord[MAXMODES][2][MAXTETRAS - MINTETRAS + 2];
96 static int movesDsp = 0;
97 static char progDsp[64] = "xpyraminx";
98 static char recordDsp[16] = "INF";
99 static char messageDsp[128] = "Welcome";
100 static char titleDsp[256] = "";
101 static char usernameDsp[USERNAMELEN] = "";
102 static int oldSize;
103
104 static void
Usage(void)105 Usage(void)
106 {
107 (void) fprintf(stderr, "usage: xpyraminx\n");
108 (void) fprintf(stderr,
109 "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
110 (void) fprintf(stderr,
111 "\t[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n");
112 (void) fprintf(stderr,
113 "\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
114 (void) fprintf(stderr,
115 "\t[-{border|bd} {color}] [-face{0|1|2|3} {color}]\n");
116 (void) fprintf(stderr,
117 "\t[-{size {int} | sticky}] [-{mode {int} | both}]\n");
118 (void) fprintf(stderr,
119 "\t[-[no]orient] [-[no]practice] [-username {string}]\n");
120 exit(1);
121 }
122
123 static XrmOptionDescRec options[] =
124 {
125 {"-mono", "*pyraminx.mono", XrmoptionNoArg, "TRUE"},
126 {"-nomono", "*pyraminx.mono", XrmoptionNoArg, "FALSE"},
127 {"-rv", "*pyraminx.reverse", XrmoptionNoArg, "TRUE"},
128 {"-reverse", "*pyraminx.reverse", XrmoptionNoArg, "TRUE"},
129 {"-norv", "*pyraminx.reverse", XrmoptionNoArg, "FALSE"},
130 {"-noreverse", "*pyraminx.reverse", XrmoptionNoArg, "FALSE"},
131 {"-fg", "*pyraminx.Foreground", XrmoptionSepArg, NULL},
132 {"-foreground", "*pyraminx.Foreground", XrmoptionSepArg, NULL},
133 {"-bg", "*Background", XrmoptionSepArg, NULL},
134 {"-background", "*Background", XrmoptionSepArg, NULL},
135 {"-bd", "*pyraminx.pieceBorder", XrmoptionSepArg, NULL},
136 {"-border", "*pyraminx.pieceBorder", XrmoptionSepArg, NULL},
137 {"-face0", "*pyraminx.faceColor0", XrmoptionSepArg, NULL},
138 {"-face1", "*pyraminx.faceColor1", XrmoptionSepArg, NULL},
139 {"-face2", "*pyraminx.faceColor2", XrmoptionSepArg, NULL},
140 {"-face3", "*pyraminx.faceColor3", XrmoptionSepArg, NULL},
141 {"-size", "*pyraminx.size", XrmoptionSepArg, NULL},
142 {"-sticky", "*pyraminx.sticky", XrmoptionNoArg, "FALSE"},
143 {"-mode", "*pyraminx.mode", XrmoptionSepArg, NULL},
144 {"-both", "*pyraminx.mode", XrmoptionNoArg, "4"},
145 {"-orient", "*pyraminx.orient", XrmoptionNoArg, "TRUE"},
146 {"-noorient", "*pyraminx.orient", XrmoptionNoArg, "FALSE"},
147 {"-practice", "*pyraminx.practice", XrmoptionNoArg, "TRUE"},
148 {"-nopractice", "*pyraminx.practice", XrmoptionNoArg, "FALSE"},
149 {"-username", "*pyraminx.userName", XrmoptionSepArg, NULL}
150 };
151
152 int
main(int argc,char ** argv)153 main(int argc, char **argv)
154 {
155 Widget toplevel, pyraminx;
156
157 toplevel = XtInitialize(argv[0], "Pyraminx",
158 options, XtNumber(options), &argc, argv);
159 if (argc != 1)
160 Usage();
161
162 XtSetArg(arg[0],
163 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
164 RootWindowOfScreen(XtScreen(toplevel)),
165 (char *) pyraminx_bits, pyraminx_width, pyraminx_height));
166 XtSetArg(arg[1], XtNinput, True);
167 XtSetValues(toplevel, arg, 2);
168 pyraminx = XtCreateManagedWidget("pyraminx",
169 pyraminxWidgetClass, toplevel, NULL, 0);
170 XtAddCallback(pyraminx,
171 XtNselectCallback, (XtCallbackProc) CallbackPyraminx, (XtPointer) NULL);
172 Initialize(pyraminx);
173 XtRealizeWidget(toplevel);
174 XGrabButton(XtDisplay(pyraminx), (unsigned int) AnyButton, AnyModifier,
175 XtWindow(pyraminx), TRUE,
176 (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
177 GrabModeAsync, GrabModeAsync, XtWindow(pyraminx),
178 XCreateFontCursor(XtDisplay(pyraminx), XC_crosshair));
179 XtMainLoop();
180
181 #ifdef VMS
182 return 1;
183 #else
184 return 0;
185 #endif
186 }
187
188 static void
Initialize(Widget w)189 Initialize(Widget w)
190 {
191 int size, mode;
192 Boolean orient, sticky, practice;
193 String username;
194
195 XtVaSetValues(w,
196 XtNstart, False, NULL);
197 XtVaGetValues(w,
198 XtNuserName, &username,
199 XtNsize, &size,
200 XtNmode, &mode,
201 XtNorient, &orient,
202 XtNsticky, &sticky,
203 XtNpractice, &practice, NULL);
204 InitRecords();
205 ReadRecords();
206 (void) strcpy(usernameDsp, username);
207 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
208 !strcmp(usernameDsp, NOBODY)) {
209 /* The NOACCESS is not necasary, but it stops people from being cute. */
210 (void) sprintf(usernameDsp, "%s", getlogin());
211 if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
212 (void) sprintf(usernameDsp, "%s", NOBODY); /* It really IS nobody */
213 }
214 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
215 oldSize = size;
216 PrintState(XtParent(w), progDsp, mode, size, sticky, movesDsp,
217 recordDsp, messageDsp);
218 }
219
220 static void
CallbackPyraminx(Widget w,caddr_t clientData,pyraminxCallbackStruct * callData)221 CallbackPyraminx(Widget w, caddr_t clientData, pyraminxCallbackStruct * callData)
222 {
223 int size, mode;
224 Boolean orient, sticky, practice, start;
225
226 XtVaGetValues(w,
227 XtNsize, &size,
228 XtNorient, &orient,
229 XtNmode, &mode,
230 XtNorient, &orient,
231 XtNsticky, &sticky,
232 XtNpractice, &practice,
233 XtNstart, &start, NULL);
234 (void) strcpy(messageDsp, "");
235 switch (callData->reason) {
236 case PYRAMINX_RESTORE:
237 if (practice)
238 (void) strcpy(recordDsp, "practice");
239 movesDsp = 0;
240 break;
241 case PYRAMINX_RESET:
242 movesDsp = 0;
243 break;
244 case PYRAMINX_AMBIGUOUS:
245 (void) strcpy(messageDsp, "Ambiguous move");
246 break;
247 case PYRAMINX_ILLEGAL:
248 if (practice || start)
249 (void) strcpy(messageDsp, "Illegal move");
250 else
251 (void) strcpy(messageDsp, "Randomize to start");
252 break;
253 case PYRAMINX_MOVED:
254 movesDsp++;
255 #ifdef DEBUG
256 if (movesDsp > 256)
257 exit(1);
258 #endif
259 XtSetArg(arg[0], XtNstart, True);
260 XtSetValues(w, arg, 1);
261 break;
262 case PYRAMINX_CONTROL:
263 return;
264 case PYRAMINX_SOLVED:
265 if (practice)
266 movesDsp = 0;
267 else {
268 if (HandleSolved(movesDsp, size, mode, orient, sticky))
269 (void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
270 else
271 (void) strcpy(messageDsp, "Solved!");
272 }
273 XtSetArg(arg[0], XtNstart, False);
274 XtSetValues(w, arg, 1);
275 break;
276 case PYRAMINX_PRACTICE:
277 movesDsp = 0;
278 practice = !practice;
279 if (!practice)
280 (void) strcpy(messageDsp, "Randomize to start");
281 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
282 XtSetArg(arg[0], XtNpractice, practice);
283 XtSetArg(arg[1], XtNstart, False);
284 XtSetValues(w, arg, 2);
285 break;
286 case PYRAMINX_RANDOMIZE:
287 movesDsp = 0;
288 XtSetArg(arg[0], XtNpractice, False);
289 XtSetArg(arg[1], XtNstart, False);
290 XtSetValues(w, arg, 2);
291 break;
292 case PYRAMINX_DEC:
293 if (!sticky) {
294 movesDsp = 0;
295 size--;
296 oldSize = size;
297 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
298 XtSetArg(arg[0], XtNsize, size);
299 XtSetValues(w, arg, 1);
300 }
301 break;
302 case PYRAMINX_ORIENT:
303 movesDsp = 0;
304 orient = !orient;
305 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
306 XtSetArg(arg[0], XtNorient, orient);
307 XtSetValues(w, arg, 1);
308 break;
309 case PYRAMINX_INC:
310 if (!sticky) {
311 movesDsp = 0;
312 size++;
313 oldSize = size;
314 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
315 XtSetArg(arg[0], XtNsize, size);
316 XtSetValues(w, arg, 1);
317 }
318 break;
319 case PYRAMINX_PERIOD2:
320 case PYRAMINX_PERIOD3:
321 case PYRAMINX_BOTH:
322 movesDsp = 0;
323 mode = callData->reason - PYRAMINX_PERIOD2 + PERIOD2;
324 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
325 XtSetArg(arg[0], XtNmode, mode);
326 XtSetValues(w, arg, 1);
327 break;
328 case PYRAMINX_STICKY:
329 movesDsp = 0;
330 sticky = !sticky;
331 if (sticky)
332 size = 4;
333 else
334 size = oldSize;
335 PrintRecord(size, mode, orient, sticky, practice, recordDsp);
336 XtSetArg(arg[0], XtNsticky, sticky);
337 XtSetArg(arg[1], XtNsize, size);
338 XtSetValues(w, arg, 2);
339 break;
340 case PYRAMINX_COMPUTED:
341 XtSetArg(arg[0], XtNstart, False);
342 XtSetValues(w, arg, 1);
343 break;
344 case PYRAMINX_UNDO:
345 movesDsp--;
346 XtSetArg(arg[0], XtNstart, True);
347 XtSetValues(w, arg, 1);
348 break;
349 }
350 PrintState(XtParent(w), progDsp, mode, size, sticky, movesDsp,
351 recordDsp, messageDsp);
352 }
353
354 static void
PrintRecord(int size,int mode,Boolean orient,Boolean sticky,Boolean practice,char * record)355 PrintRecord(int size, int mode, Boolean orient, Boolean sticky, Boolean practice, char *record)
356 {
357 int i = mode - PERIOD2;
358 int j = (orient) ? 1 : 0;
359 int k = (sticky) ? MAXTETRAS - MINTETRAS + 1 : size - MINTETRAS;
360
361 if (practice)
362 (void) strcpy(record, "practice");
363 else if (!sticky && size > MAXTETRAS)
364 (void) strcpy(record, "NOT RECORDED");
365 else if (pyraminxRecord[i][j][k].score >= MAXRECORD)
366 (void) sprintf(record, "NEVER %s", NOACCESS);
367 else
368 (void) sprintf(record, "%d %s",
369 pyraminxRecord[i][j][k].score, pyraminxRecord[i][j][k].name);
370 }
371
372 static Boolean
HandleSolved(int counter,int size,int mode,Boolean orient,Boolean sticky)373 HandleSolved(int counter, int size, int mode, Boolean orient, Boolean sticky)
374 {
375 int i = mode - PERIOD2;
376 int j = (orient) ? 1 : 0;
377 int k = (sticky) ? MAXTETRAS - MINTETRAS + 1 : size - MINTETRAS;
378
379 if ((sticky || size <= MAXTETRAS) &&
380 counter < pyraminxRecord[i][j][k].score) {
381 pyraminxRecord[i][j][k].score = counter;
382 (void) strcpy(pyraminxRecord[i][j][k].name, usernameDsp);
383 if (size < 4 || mode == PERIOD2 ||
384 (orient && (counter < pyraminxRecord[i][!j][k].score))) {
385 pyraminxRecord[i][!j][k].score = counter;
386 (void) strcpy(pyraminxRecord[i][!j][k].name, usernameDsp);
387 }
388 WriteRecords();
389 PrintRecord(size, mode, orient, sticky, False, recordDsp);
390 return True;
391 }
392 return False;
393 }
394
395 static void
PrintState(Widget w,char * prog,int mode,int size,Boolean sticky,int moves,char * record,char * message)396 PrintState(Widget w, char *prog, int mode, int size, Boolean sticky, int moves, char *record, char *message)
397 {
398 char ss[10], mb[10];
399
400 if (sticky)
401 (void) strcpy(ss, "sticky");
402 else
403 (void) sprintf(ss, "%d", size);
404 if (mode == BOTH)
405 (void) strcpy(mb, "both");
406 else
407 (void) sprintf(mb, "%d", mode);
408 (void) sprintf(titleDsp, "%s.%s: %s@ (%d/%s) - %s", prog, mb, ss, moves,
409 record, message);
410 XtSetArg(arg[0], XtNtitle, titleDsp);
411 XtSetValues(w, arg, 1);
412 }
413
414 static void
InitRecords(void)415 InitRecords(void)
416 {
417 int i, mode, orient;
418
419 for (mode = 0; mode < MAXMODES; mode++)
420 for (orient = 0; orient < 2; orient++)
421 for (i = 0; i <= MAXTETRAS - MINTETRAS + 1; i++) {
422 pyraminxRecord[mode][orient][i].score = MAXRECORD;
423 (void) strcpy(pyraminxRecord[mode][orient][i].name, NOACCESS);
424 }
425 }
426
427 static void
ReadRecords(void)428 ReadRecords(void)
429 {
430 FILE *fp;
431 int i, n, mode, orient;
432 char username[USERNAMELEN];
433
434 if ((fp = fopen(SCOREFILE, "r")) == NULL)
435 (void) sprintf(messageDsp, "Can not open %s, taking defaults.", SCOREFILE);
436 else {
437 for (mode = 0; mode < MAXMODES; mode++)
438 for (orient = 0; orient < 2; orient++)
439 for (i = 0; i <= MAXTETRAS - MINTETRAS + 1; i++) {
440 (void) fscanf(fp, "%d %s\n", &n, username);
441 if (n <= pyraminxRecord[mode][orient][i].score) {
442 pyraminxRecord[mode][orient][i].score = n;
443 (void) strcpy(pyraminxRecord[mode][orient][i].name, username);
444 }
445 }
446 (void) fclose(fp);
447 }
448 }
449
450 static void
WriteRecords(void)451 WriteRecords(void)
452 {
453 FILE *fp;
454 int i, mode, orient;
455
456 ReadRecords(); /* Maybe its been updated by another */
457 if ((fp = fopen(SCOREFILE, "w")) == NULL)
458 (void) sprintf(messageDsp, "Can not write to %s.", SCOREFILE);
459 else {
460 #if HAVE_FCNTL_H
461 int lfd;
462 char lockfile[FILENAMELEN];
463
464 (void) strcpy(lockfile, SCOREFILE);
465 (void) strcat(lockfile, ".lock");
466 while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
467 errno == EEXIST)
468 (void) sleep(1);
469 if (lfd < 0) {
470 #if 1
471 (void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
472 #else
473 (void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
474 );
475 return;
476 #endif
477 }
478 #endif
479 for (mode = 0; mode < MAXMODES; mode++) {
480 for (orient = 0; orient < 2; orient++) {
481 for (i = 0; i <= MAXTETRAS - MINTETRAS + 1; i++)
482 (void) fprintf(fp, "%d %s\n",
483 pyraminxRecord[mode][orient][i].score,
484 pyraminxRecord[mode][orient][i].name);
485 (void) fprintf(fp, "\n");
486 }
487 (void) fprintf(fp, "\n");
488 }
489 #if HAVE_FCNTL_H
490 (void) close(lfd);
491 (void) unlink(lockfile);
492 #endif
493 (void) fclose(fp);
494 }
495 }
496