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