1 /*-
2 # X-BASED CUBES
3 #
4 #  Cubes.c
5 #
6 ###
7 #
8 #  Copyright (c) 1994 - 99	David Albert Bagley, bagleyd@tux.org
9 #
10 #                   All Rights Reserved
11 #
12 #  Permission to use, copy, modify, and distribute this software and
13 #  its documentation for any purpose and without fee is hereby granted,
14 #  provided that the above copyright notice appear in all copies and
15 #  that both that copyright notice and this permission notice appear in
16 #  supporting documentation, and that the name of the author not be
17 #  used in advertising or publicity pertaining to distribution of the
18 #  software without specific, written prior permission.
19 #
20 #  This program is distributed in the hope that it will be "playable",
21 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
22 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 #
24 */
25 
26 /* Methods file for Cubes */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #ifdef VMS
31 #include <unixlib.h>
32 #else
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #endif
37 #include <X11/IntrinsicP.h>
38 #include <X11/Intrinsic.h>
39 #include <X11/StringDefs.h>
40 #include <X11/CoreP.h>
41 #include "CubesP.h"
42 
43 #ifndef DATAFILE
44 #define DATAFILE "/usr/games/lib/cubes.data"
45 #endif
46 
47 static void InitializeCubes(Widget request, Widget renew);
48 static void ExposeCubes(Widget renew, XEvent * event, Region region);
49 static void ResizeCubes(CubesWidget w);
50 static void DestroyCubes(Widget old);
51 static Boolean SetValuesCubes(Widget current, Widget request, Widget renew);
52 
53 static void QuitCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
54 static void MoveCubesOut(CubesWidget w, XEvent * event, char **args, int nArgs);
55 static void MoveCubesTop(CubesWidget w, XEvent * event, char **args, int nArgs);
56 static void MoveCubesLeft(CubesWidget w, XEvent * event, char **args, int nArgs);
57 static void MoveCubesIn(CubesWidget w, XEvent * event, char **args, int nArgs);
58 static void MoveCubesRight(CubesWidget w, XEvent * event, char **args, int nArgs);
59 static void MoveCubesBottom(CubesWidget w, XEvent * event, char **args, int nArgs);
60 static void SelectCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
61 static void ReleaseCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
62 static void RandomizeCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
63 static void RandomizeCubesMaybe(CubesWidget w, XEvent * event, char **args, int nArgs);
64 static void GetCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
65 static void WriteCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
66 static void UndoCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
67 static void SolveCubes(CubesWidget w, XEvent * event, char **args, int nArgs);
68 static int  MoveCubes(CubesWidget w, int direction, int control);
69 
70 static int  PositionToBrick(CubesWidget w, int x, int y, int *i, int *j, int *k);
71 static int  MovableBrick(CubesWidget w);
72 static void SelectBricks(CubesWidget w);
73 static void SetAllColors(CubesWidget w, Boolean init);
74 static void CheckBricks(CubesWidget w);
75 static void ResetBricks(CubesWidget w);
76 static void ResizeBricks(CubesWidget w);
77 static void MoveNoBricks(CubesWidget w);
78 static int  MoveBricksDir(CubesWidget w, int direction);
79 static void RandomizeBricks(CubesWidget w);
80 static void MoveBricks(CubesWidget w, int from);
81 static int  ExchangeBricks(CubesWidget w, int pos1, int pos2);
82 static void DrawFrame(CubesWidget w, GC gc);
83 static void DrawBrick(CubesWidget w, int pos, Boolean blank, Boolean erase, int offset);
84 static int  Row(CubesWidget w, int pos);
85 static int  Column(CubesWidget w, int pos);
86 static int  Stack(CubesWidget w, int pos);
87 static int  int2String(char *buf, int number, int base);
88 
89 static char defaultTranslationsCubes[] =
90 "<KeyPress>q: Quit()\n\
91    Ctrl<KeyPress>C: Quit()\n\
92    <KeyPress>o: MoveOut()\n\
93    <KeyPress>KP_Divide: MoveOut()\n\
94    <KeyPress>R5: MoveOut()\n\
95    <KeyPress>Up: MoveTop()\n\
96    <KeyPress>KP_8: MoveTop()\n\
97    <KeyPress>R8: MoveTop()\n\
98    <KeyPress>Left: MoveLeft()\n\
99    <KeyPress>KP_4: MoveLeft()\n\
100    <KeyPress>R10: MoveLeft()\n\
101    <KeyPress>i: MoveIn()\n\
102    <KeyPress>Begin: MoveIn()\n\
103    <KeyPress>KP_5: MoveIn()\n\
104    <KeyPress>R11: MoveIn()\n\
105    <KeyPress>Right: MoveRight()\n\
106    <KeyPress>KP_6: MoveRight()\n\
107    <KeyPress>R12: MoveRight()\n\
108    <KeyPress>Down: MoveBottom()\n\
109    <KeyPress>KP_2: MoveBottom()\n\
110    <KeyPress>R14: MoveBottom()\n\
111    <Btn1Down>: Select()\n\
112    <Btn1Up>: Release()\n\
113    <KeyPress>r: Randomize()\n\
114    <Btn3Down>(2+): Randomize()\n\
115    <Btn3Down>: RandomizeMaybe()\n\
116    <KeyPress>g: Get()\n\
117    <KeyPress>w: Write()\n\
118    <KeyPress>u: Undo()\n\
119    <KeyPress>s: Solve()";
120 
121 static XtActionsRec actionsListCubes[] =
122 {
123 	{"Quit", (XtActionProc) QuitCubes},
124 	{"MoveOut", (XtActionProc) MoveCubesOut},
125 	{"MoveTop", (XtActionProc) MoveCubesTop},
126 	{"MoveLeft", (XtActionProc) MoveCubesLeft},
127 	{"MoveIn", (XtActionProc) MoveCubesIn},
128 	{"MoveRight", (XtActionProc) MoveCubesRight},
129 	{"MoveBottom", (XtActionProc) MoveCubesBottom},
130 	{"Select", (XtActionProc) SelectCubes},
131 	{"Release", (XtActionProc) ReleaseCubes},
132 	{"Randomize", (XtActionProc) RandomizeCubes},
133 	{"RandomizeMaybe", (XtActionProc) RandomizeCubesMaybe},
134 	{"Get", (XtActionProc) GetCubes},
135 	{"Write", (XtActionProc) WriteCubes},
136 	{"Undo", (XtActionProc) UndoCubes},
137 	{"Solve", (XtActionProc) SolveCubes}
138 };
139 
140 static XtResource resourcesCubes[] =
141 {
142 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
143 	 XtOffset(CubesWidget, cubes.username), XtRString, "nobody"},
144 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
145     XtOffset(CubesWidget, cubes.foreground), XtRString, XtDefaultForeground},
146 	{XtNbrickColor, XtCColor, XtRPixel, sizeof (Pixel),
147     XtOffset(CubesWidget, cubes.brickColor), XtRString, XtDefaultForeground},
148 	{XtNbrickBorder, XtCColor, XtRPixel, sizeof (Pixel),
149    XtOffset(CubesWidget, cubes.borderColor), XtRString, XtDefaultBackground},
150 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
151 	 XtOffset(CubesWidget, core.width), XtRString, "100"},
152 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
153 	 XtOffset(CubesWidget, core.height), XtRString, "300"},
154 	{XtNsizeX, XtCSizeX, XtRInt, sizeof (int),
155 	 XtOffset(CubesWidget, cubes.sizeX), XtRString, "3"},	/* DEFAULTCUBES */
156 	{XtNsizeY, XtCSizeY, XtRInt, sizeof (int),
157 	 XtOffset(CubesWidget, cubes.sizeY), XtRString, "3"},	/* DEFAULTCUBES */
158 	{XtNsizeZ, XtCSizeZ, XtRInt, sizeof (int),
159 	 XtOffset(CubesWidget, cubes.sizeZ), XtRString, "3"},	/* DEFAULTCUBES */
160 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
161 	 XtOffset(CubesWidget, cubes.mono), XtRString, "FALSE"},
162 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
163 	 XtOffset(CubesWidget, cubes.reverse), XtRString, "FALSE"},
164 	{XtNbase, XtCBase, XtRInt, sizeof (int),
165 	 XtOffset(CubesWidget, cubes.base), XtRString, "10"},
166 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
167 	 XtOffset(CubesWidget, cubes.started), XtRString, "FALSE"},
168 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
169 	 XtOffset(CubesWidget, cubes.select), XtRCallback, NULL}
170 };
171 
172 CubesClassRec cubesClassRec =
173 {
174 	{
175 		(WidgetClass) & widgetClassRec,		/* superclass */
176 		"Cubes",	/* class name */
177 		sizeof (CubesRec),	/* widget size */
178 		NULL,		/* class initialize */
179 		NULL,		/* class part initialize */
180 		FALSE,		/* class inited */
181 		(XtInitProc) InitializeCubes,	/* initialize */
182 		NULL,		/* initialize hook */
183 		XtInheritRealize,	/* realize */
184 		actionsListCubes,	/* actions */
185 		XtNumber(actionsListCubes),	/* num actions */
186 		resourcesCubes,	/* resources */
187 		XtNumber(resourcesCubes),	/* num resources */
188 		NULLQUARK,	/* xrm class */
189 		TRUE,		/* compress motion */
190 		TRUE,		/* compress exposure */
191 		TRUE,		/* compress enterleave */
192 		TRUE,		/* visible interest */
193 		(XtWidgetProc) DestroyCubes,	/* destroy */
194 		(XtWidgetProc) ResizeCubes,	/* resize */
195 		(XtExposeProc) ExposeCubes,	/* expose */
196 		(XtSetValuesFunc) SetValuesCubes,	/* set values */
197 		NULL,		/* set values hook */
198 		XtInheritSetValuesAlmost,	/* set values almost */
199 		NULL,		/* get values hook */
200 		NULL,		/* accept focus */
201 		XtVersion,	/* version */
202 		NULL,		/* callback private */
203 		defaultTranslationsCubes,	/* tm table */
204 		NULL,		/* query geometry */
205 		NULL,		/* display accelerator */
206 		NULL		/* extension */
207 	},
208 	{
209 		0		/* ignore */
210 	}
211 };
212 
213 WidgetClass cubesWidgetClass = (WidgetClass) & cubesClassRec;
214 
215 #ifndef HAVE_USLEEP
216 #if !defined( VMS ) || defined( XVMSUTILS ) ||  ( __VMS_VER >= 70000000 )
217 #ifdef USE_XVMSUTILS
218 #include <X11/unix_time.h>
219 #endif
220 #if HAVE_SYS_TIME_H
221 #include <sys/time.h>
222 #else
223 #if HAVE_SYS_SELECT_H
224 #include <sys/select.h>
225 #endif
226 #endif
227 #endif
228 #if defined(SYSV) || defined(SVR4)
229 #ifdef LESS_THAN_AIX3_2
230 #include <sys/poll.h>
231 #else /* !LESS_THAN_AIX3_2 */
232 #include <poll.h>
233 #endif /* !LESS_THAN_AIX3_2 */
234 #endif /* defined(SYSV) || defined(SVR4) */
235 
236 static int
usleep(unsigned int usec)237 usleep(unsigned int usec)
238 {
239 #if (defined (SYSV) || defined(SVR4)) && !defined(__hpux)
240 #if defined(HAVE_NANOSLEEP)
241 	{
242 		struct timespec rqt;
243 
244 		rqt.tv_nsec = 1000 * (usec % (unsigned int) 1000000);
245 		rqt.tv_sec = usec / (unsigned int) 1000000;
246 		return nanosleep(&rqt, NULL);
247 	}
248 #else
249 	(void) poll((void *) 0, (int) 0, usec / 1000);	/* ms resolution */
250 #endif
251 #else
252 #ifdef VMS
253 	long        timadr[2];
254 
255 	if (usec != 0) {
256 		timadr[0] = -usec * 10;
257 		timadr[1] = -1;
258 
259 		sys$setimr(4, &timadr, 0, 0, 0);
260 		sys$waitfr(4);
261 	}
262 #else
263 	struct timeval time_out;
264 
265 #if  0
266 	/* (!defined(AIXV3) && !defined(__hpux)) */
267 	extern int  select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
268 
269 #endif
270 
271 	time_out.tv_usec = usec % (unsigned int) 1000000;
272 	time_out.tv_sec = usec / (unsigned int) 1000000;
273 	(void) select(0, (void *) 0, (void *) 0, (void *) 0, &time_out);
274 #endif
275 #endif
276 	return 0;
277 }
278 #endif
279 
280 static void
Sleep(unsigned int cMilliseconds)281 Sleep(unsigned int cMilliseconds)
282 {
283 	(void) usleep(cMilliseconds * 1000);
284 }
285 
286 static void
InitializeCubes(Widget request,Widget renew)287 InitializeCubes(Widget request, Widget renew)
288 {
289 	CubesWidget w = (CubesWidget) renew;
290 
291 	w->cubes.brickOfPosition = NULL;
292 	CheckBricks(w);
293 	InitMoves();
294 	ResetBricks(w);
295 	(void) SRAND(getpid());
296 	w->cubes.depth = DefaultDepthOfScreen(XtScreen(w));
297 	SetAllColors(w, True);
298 	ResizeCubes(w);
299 }
300 
301 static void
DestroyCubes(Widget old)302 DestroyCubes(Widget old)
303 {
304 	CubesWidget w = (CubesWidget) old;
305 
306 	XtReleaseGC(old, w->cubes.brickGC);
307 	XtReleaseGC(old, w->cubes.borderGC);
308 	XtReleaseGC(old, w->cubes.puzzleGC);
309 	XtReleaseGC(old, w->cubes.inverseGC);
310 	XtRemoveCallbacks(old, XtNselectCallback, w->cubes.select);
311 }
312 
313 static void
ResizeCubes(CubesWidget w)314 ResizeCubes(CubesWidget w)
315 {
316 	w->cubes.delta.x = 3;
317 	w->cubes.delta.y = 3;
318 	w->cubes.offset.x = MAX(((int) w->core.width - w->cubes.delta.x) /
319 				w->cubes.sizeX, 0);
320 	w->cubes.offset.y = MAX(((int) w->core.height - w->cubes.delta.y) /
321 				w->cubes.sizeY, 0);
322 	if (w->cubes.offset.y >= w->cubes.offset.x) {
323 		w->cubes.vertical = TRUE;
324 		w->cubes.offset.y = MAX(((int) w->core.height / w->cubes.sizeZ -
325 				      w->cubes.delta.y) / w->cubes.sizeY, 0);
326 	} else {
327 		w->cubes.vertical = FALSE;
328 		w->cubes.offset.x = MAX(((int) w->core.width / w->cubes.sizeZ -
329 				      w->cubes.delta.x) / w->cubes.sizeX, 0);
330 	}
331 	w->cubes.faceSize.x = w->cubes.offset.x * w->cubes.sizeX +
332 		w->cubes.delta.x + 2;
333 	w->cubes.faceSize.y = w->cubes.offset.y * w->cubes.sizeY +
334 		w->cubes.delta.y + 2;
335 	if (w->cubes.vertical) {
336 		w->cubes.puzzleSize.x = w->cubes.faceSize.x;
337 		w->cubes.puzzleSize.y = (w->cubes.faceSize.y - w->cubes.delta.y) *
338 			w->cubes.sizeZ + w->cubes.delta.y;
339 	} else {
340 		w->cubes.puzzleSize.x = (w->cubes.faceSize.x - w->cubes.delta.x) *
341 			w->cubes.sizeZ + w->cubes.delta.x;
342 		w->cubes.puzzleSize.y = w->cubes.faceSize.y;
343 	}
344 	w->cubes.puzzleOffset.x = ((int) w->core.width -
345 				   w->cubes.puzzleSize.x + 2) / 2;
346 	w->cubes.puzzleOffset.y = ((int) w->core.height -
347 				   w->cubes.puzzleSize.y + 2) / 2;
348 	w->cubes.brickSize.x = MAX(w->cubes.offset.x - w->cubes.delta.x, 0);
349 	w->cubes.brickSize.y = MAX(w->cubes.offset.y - w->cubes.delta.y, 0);
350 	ResizeBricks(w);
351 }
352 
353 static void
ExposeCubes(Widget renew,XEvent * event,Region region)354 ExposeCubes(Widget renew, XEvent * event, Region region)
355 {
356 	CubesWidget w = (CubesWidget) renew;
357 
358 	if (w->core.visible) {
359 		if (w->cubes.reverse)
360 			XFillRectangle(XtDisplay(w), XtWindow(w),
361 				       w->cubes.inverseGC, 0, 0, w->core.width, w->core.height);
362 		DrawFrame(w, w->cubes.puzzleGC);
363 		DrawAllBricks(w);
364 	}
365 }
366 
367 static      Boolean
SetValuesCubes(Widget current,Widget request,Widget renew)368 SetValuesCubes(Widget current, Widget request, Widget renew)
369 {
370 	CubesWidget c = (CubesWidget) current, w = (CubesWidget) renew;
371 	Boolean     redraw = FALSE;
372 	Boolean     redrawBricks = FALSE;
373 
374 	CheckBricks(w);
375 	if (w->core.background_pixel != c->core.background_pixel ||
376 	    w->cubes.foreground != c->cubes.foreground ||
377 	    w->cubes.borderColor != c->cubes.borderColor ||
378 	    w->cubes.brickColor != c->cubes.brickColor ||
379 	    w->cubes.reverse != c->cubes.reverse ||
380 	    w->cubes.mono != c->cubes.mono) {
381 		SetAllColors(w, False);
382 		redrawBricks = True;
383 	}
384 	if (w->cubes.sizeX != c->cubes.sizeX ||
385 	    w->cubes.sizeY != c->cubes.sizeY ||
386 	    w->cubes.sizeZ != c->cubes.sizeZ ||
387 	    w->cubes.base != c->cubes.base) {
388 		ResetBricks(w);
389 		ResizeCubes(w);
390 		redraw = TRUE;
391 	} else if (w->cubes.offset.x != c->cubes.offset.x ||
392 		   w->cubes.offset.y != c->cubes.offset.y) {
393 		ResizeCubes(w);
394 		redraw = TRUE;
395 	}
396 	if (redrawBricks && !redraw && XtIsRealized(renew) && renew->core.visible) {
397 		DrawFrame(c, c->cubes.inverseGC);
398 		DrawFrame(w, w->cubes.puzzleGC);
399 		DrawAllBricks(w);
400 	}
401 	return (redraw);
402 }
403 
404 static void
QuitCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)405 QuitCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
406 {
407 	XtCloseDisplay(XtDisplay(w));
408 	exit(0);
409 }
410 
411 static void
SelectCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)412 SelectCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
413 {
414 	int         pos, i, j, k, rowType;
415 
416 	pos = PositionToBrick(w, event->xbutton.x, event->xbutton.y, &i, &j, &k);
417 	if (pos >= 0) {
418 		if (CheckSolved(w)) {
419 			MoveNoBricks(w);
420 			w->cubes.currentPosition = -1;
421 			return;
422 		}
423 		w->cubes.currentPosition = pos;
424 		w->cubes.currentRow[0] = i;
425 		w->cubes.currentRow[1] = j;
426 		w->cubes.currentRow[2] = k;
427 		rowType = MovableBrick(w);
428 		if (rowType < 0) {
429 			cubesCallbackStruct cb;
430 
431 			DrawBrick(w, w->cubes.currentPosition, rowType == CUBES_SPACE, False, TRUE);
432 			XFlush(XtDisplay(w));
433 			Sleep(100);
434 			DrawBrick(w, w->cubes.currentPosition, True, True, TRUE);
435 			if (rowType != CUBES_SPACE)
436 				DrawBrick(w, w->cubes.currentPosition, False, False, FALSE);
437 			cb.reason = rowType;
438 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
439 			w->cubes.currentPosition = -1;
440 			return;
441 		}
442 		DrawBrick(w, w->cubes.currentPosition, False, False, TRUE);
443 	} else
444 		w->cubes.currentPosition = -1;
445 }
446 
447 static void
ReleaseCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)448 ReleaseCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
449 {
450 	if (w->cubes.currentPosition == -1)
451 		return;
452 	DrawBrick(w, w->cubes.currentPosition, True, True, TRUE);
453 	DrawBrick(w, w->cubes.currentPosition, False, False, FALSE);
454 	SelectBricks(w);
455 	w->cubes.currentPosition = -1;
456 }
457 
458 static void
RandomizeCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)459 RandomizeCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
460 {
461 	RandomizeBricks(w);
462 }
463 
464 static void
RandomizeCubesMaybe(CubesWidget w,XEvent * event,char ** args,int nArgs)465 RandomizeCubesMaybe(CubesWidget w, XEvent * event, char **args, int nArgs)
466 {
467 	if (!w->cubes.started)
468 		RandomizeBricks(w);
469 }
470 
471 static void
GetCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)472 GetCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
473 {
474 	FILE       *fp;
475 	char        c;
476 	int         i, sizeX, sizeY, sizeZ, moves;
477 	cubesCallbackStruct cb;
478 
479 	if ((fp = fopen(DATAFILE, "r")) == NULL)
480 		(void) printf("Can not read %s for get.\n", DATAFILE);
481 	else {
482 		FlushMoves(w);
483 		while ((c = getc(fp)) != EOF && c != SYMBOL);
484 		(void) fscanf(fp, "%d", &sizeX);
485 		if (sizeX >= MINCUBES) {
486 			for (i = w->cubes.sizeX; i < sizeX; i++) {
487 				cb.reason = CUBES_INC_X;
488 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
489 			}
490 			for (i = w->cubes.sizeX; i > sizeX; i--) {
491 				cb.reason = CUBES_DEC_X;
492 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
493 			}
494 		} else
495 			(void) printf("%s corrupted: sizeX %d should be between %d and MAXINT\n",
496 				      DATAFILE, sizeX, MINCUBES);
497 		while ((c = getc(fp)) != EOF && c != SYMBOL);
498 		(void) fscanf(fp, "%d", &sizeY);
499 		if (sizeY >= MINCUBES) {
500 			for (i = w->cubes.sizeY; i < sizeY; i++) {
501 				cb.reason = CUBES_INC_Y;
502 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
503 			}
504 			for (i = w->cubes.sizeY; i > sizeY; i--) {
505 				cb.reason = CUBES_DEC_Y;
506 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
507 			}
508 		} else
509 			(void) printf("%s corrupted: sizeY %d should be between %d and MAXINT\n",
510 				      DATAFILE, sizeY, MINCUBES);
511 		while ((c = getc(fp)) != EOF && c != SYMBOL);
512 		(void) fscanf(fp, "%d", &sizeZ);
513 		if (sizeZ >= MINCUBES) {
514 			for (i = w->cubes.sizeZ; i < sizeZ; i++) {
515 				cb.reason = CUBES_INC_Z;
516 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
517 			}
518 			for (i = w->cubes.sizeZ; i > sizeZ; i--) {
519 				cb.reason = CUBES_DEC_Z;
520 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
521 			}
522 		} else
523 			(void) printf("%s corrupted: sizeZ %d should be between %d and MAXINT\n",
524 				      DATAFILE, sizeZ, MINCUBES);
525 		while ((c = getc(fp)) != EOF && c != SYMBOL);
526 		(void) fscanf(fp, "%d", &moves);
527 		ScanStartPosition(fp, w);
528 		cb.reason = CUBES_RESTORE;
529 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
530 		SetStartPosition(w);
531 		ScanMoves(fp, w, moves);
532 		(void) fclose(fp);
533 		(void) printf("%s: sizeX %d, sizeY %d, sizeZ %d, moves %d.\n",
534 			      DATAFILE, sizeX, sizeY, sizeZ, moves);
535 	}
536 }
537 
538 static void
WriteCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)539 WriteCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
540 {
541 	FILE       *fp;
542 
543 	if ((fp = fopen(DATAFILE, "w")) == NULL)
544 		(void) printf("Can not write to %s.\n", DATAFILE);
545 	else {
546 		(void) fprintf(fp, "sizeX%c %d\n", SYMBOL, w->cubes.sizeX);
547 		(void) fprintf(fp, "sizeY%c %d\n", SYMBOL, w->cubes.sizeY);
548 		(void) fprintf(fp, "sizeZ%c %d\n", SYMBOL, w->cubes.sizeZ);
549 		(void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
550 		PrintStartPosition(fp, w);
551 		PrintMoves(fp);
552 		(void) fclose(fp);
553 		(void) printf("Saved to %s.\n", DATAFILE);
554 	}
555 }
556 
557 static void
UndoCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)558 UndoCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
559 {
560 	if (MadeMoves()) {
561 		int         direction;
562 
563 		GetMove(&direction);
564 		direction = (direction < 4) ? (direction + 2) % 4 :
565 			((direction == IN) ? OUT : IN);
566 
567 		if (MoveBricksDir(w, direction)) {
568 			cubesCallbackStruct cb;
569 
570 			cb.reason = CUBES_UNDO;
571 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
572 		}
573 	}
574 }
575 
576 static void
SolveCubes(CubesWidget w,XEvent * event,char ** args,int nArgs)577 SolveCubes(CubesWidget w, XEvent * event, char **args, int nArgs)
578 {
579 #if 0
580 	SolveBricks(w);		/* Sorry, this is not implemented */
581 #endif
582 }
583 
584 static void
MoveCubesOut(CubesWidget w,XEvent * event,char ** args,int nArgs)585 MoveCubesOut(CubesWidget w, XEvent * event, char **args, int nArgs)
586 {
587 	(void) MoveCubes(w, OUT, (int) (event->xkey.state & ControlMask));
588 }
589 
590 static void
MoveCubesTop(CubesWidget w,XEvent * event,char ** args,int nArgs)591 MoveCubesTop(CubesWidget w, XEvent * event, char **args, int nArgs)
592 {
593 	(void) MoveCubes(w, TOP, (int) (event->xkey.state & ControlMask));
594 }
595 
596 static void
MoveCubesLeft(CubesWidget w,XEvent * event,char ** args,int nArgs)597 MoveCubesLeft(CubesWidget w, XEvent * event, char **args, int nArgs)
598 {
599 	(void) MoveCubes(w, LEFT, (int) (event->xkey.state & ControlMask));
600 }
601 
602 static void
MoveCubesIn(CubesWidget w,XEvent * event,char ** args,int nArgs)603 MoveCubesIn(CubesWidget w, XEvent * event, char **args, int nArgs)
604 {
605 	(void) MoveCubes(w, IN, (int) (event->xkey.state & ControlMask));
606 }
607 
608 static void
MoveCubesRight(CubesWidget w,XEvent * event,char ** args,int nArgs)609 MoveCubesRight(CubesWidget w, XEvent * event, char **args, int nArgs)
610 {
611 	(void) MoveCubes(w, RIGHT, (int) (event->xkey.state & ControlMask));
612 }
613 
614 static void
MoveCubesBottom(CubesWidget w,XEvent * event,char ** args,int nArgs)615 MoveCubesBottom(CubesWidget w, XEvent * event, char **args, int nArgs)
616 {
617 	(void) MoveCubes(w, BOTTOM, (int) (event->xkey.state & ControlMask));
618 }
619 
620 static int
MoveCubes(CubesWidget w,int direction,int control)621 MoveCubes(CubesWidget w, int direction, int control)
622 {
623 	cubesCallbackStruct cb;
624 
625 	if (control) {
626 		cb.reason = CUBES_IGNORE;
627 		switch (direction) {
628 			case TOP:
629 				if (w->cubes.sizeY <= MINCUBES)
630 					return FALSE;
631 				cb.reason = CUBES_DEC_Y;
632 				break;
633 			case RIGHT:
634 				cb.reason = CUBES_INC_X;
635 				break;
636 			case BOTTOM:
637 				cb.reason = CUBES_INC_Y;
638 				break;
639 			case LEFT:
640 				if (w->cubes.sizeX <= MINCUBES)
641 					return FALSE;
642 				cb.reason = CUBES_DEC_X;
643 				break;
644 			case IN:
645 				if (w->cubes.sizeZ <= MINCUBES)
646 					return FALSE;
647 				cb.reason = CUBES_DEC_Z;
648 				break;
649 			case OUT:
650 				cb.reason = CUBES_INC_Z;
651 				break;
652 			default:
653 				(void) printf("MoveCubes: direction %d\n", direction);
654 		}
655 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
656 		return FALSE;
657 	}
658 	if (CheckSolved(w)) {
659 		MoveNoBricks(w);
660 		return FALSE;
661 	}
662 	if (!MoveCubesDir(w, direction)) {
663 		cb.reason = CUBES_BLOCKED;
664 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
665 		return FALSE;
666 	}
667 	if (CheckSolved(w)) {
668 		cb.reason = CUBES_SOLVED;
669 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
670 	}
671 	return TRUE;
672 }
673 
674 int
MoveCubesDir(CubesWidget w,int direction)675 MoveCubesDir(CubesWidget w, int direction)
676 {
677 	cubesCallbackStruct cb;
678 
679 	if (MoveBricksDir(w, direction)) {
680 		cb.reason = CUBES_MOVED;
681 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
682 		PutMove(direction);
683 		return TRUE;
684 	}
685 	return FALSE;
686 }
687 
688 static int
PositionToBrick(CubesWidget w,int x,int y,int * i,int * j,int * k)689 PositionToBrick(CubesWidget w, int x, int y, int *i, int *j, int *k)
690 {
691 	if (w->cubes.vertical) {
692 		*i = (x - w->cubes.delta.x / 2 -
693 		      w->cubes.puzzleOffset.x) / w->cubes.offset.x;
694 		*j = ((y - w->cubes.delta.y / 2 -
695 		       w->cubes.puzzleOffset.y) % (w->cubes.sizeY * w->cubes.offset.y +
696 				  w->cubes.delta.y - 1)) / w->cubes.offset.y;
697 		*k = (y - w->cubes.delta.y / 2 -
698 		      w->cubes.puzzleOffset.y) / (w->cubes.sizeY * w->cubes.offset.y +
699 						  w->cubes.delta.y - 1);
700 	} else {
701 		*i = ((x - w->cubes.delta.x / 2 -
702 		       w->cubes.puzzleOffset.x) % (w->cubes.sizeX * w->cubes.offset.x +
703 				  w->cubes.delta.x - 1)) / w->cubes.offset.x;
704 		*j = (y - w->cubes.delta.y / 2 -
705 		      w->cubes.puzzleOffset.y) / w->cubes.offset.y;
706 		*k = (x - w->cubes.delta.x / 2 -
707 		      w->cubes.puzzleOffset.x) / (w->cubes.sizeX * w->cubes.offset.x +
708 						  w->cubes.delta.x - 1);
709 	}
710 	if (*i >= 0 && *j >= 0 && *k >= 0 &&
711 	    *i < w->cubes.sizeX && *j < w->cubes.sizeY && *k < w->cubes.sizeZ)
712 		return (*i + w->cubes.sizeX * *j + w->cubes.sizeRect * *k);
713 	else
714 		return -1;
715 }
716 
717 static int
MovableBrick(CubesWidget w)718 MovableBrick(CubesWidget w)
719 {
720 	int         i, j, k, l, m;
721 
722 	i = w->cubes.currentRow[0];
723 	j = w->cubes.currentRow[1];
724 	k = w->cubes.currentRow[2];
725 	l = i + w->cubes.sizeX * j - w->cubes.spacePosition % w->cubes.sizeRect;
726 	m = i + w->cubes.sizeX * j + w->cubes.sizeRect * k - w->cubes.spacePosition;
727 
728 	/* Order important if w->cubes.sizeX = 1 */
729 	if (l % w->cubes.sizeX == 0 && k == Stack(w, w->cubes.spacePosition)) {
730 		if (l == 0) {
731 			return CUBES_SPACE;
732 		}
733 	} else if (!(l / w->cubes.sizeX == 0 &&
734 		     j == Column(w, w->cubes.spacePosition) &&
735 		     k == Stack(w, w->cubes.spacePosition)) &&
736 		   (m % w->cubes.sizeRect != 0)) {
737 		return CUBES_BLOCKED;
738 	}
739 	return 0;
740 }
741 
742 static void
SelectBricks(CubesWidget w)743 SelectBricks(CubesWidget w)
744 {
745 	cubesCallbackStruct cb;
746 	int         i, j, k, l, m, n;
747 
748 	i = w->cubes.currentRow[0];
749 	j = w->cubes.currentRow[1];
750 	k = w->cubes.currentRow[2];
751 	l = i + w->cubes.sizeX * j - w->cubes.spacePosition % w->cubes.sizeRect;
752 	m = i + w->cubes.sizeX * j + w->cubes.sizeRect * k - w->cubes.spacePosition;
753 
754 	/* Order important if w->cubes.sizeX = 1 */
755 	if (l % w->cubes.sizeX == 0 && k == Stack(w, w->cubes.spacePosition)) {
756 		if (l < 0) {
757 			for (n = 1; n <= -l / w->cubes.sizeX; n++) {
758 				MoveBricks(w, w->cubes.spacePosition - w->cubes.sizeX);
759 				cb.reason = CUBES_MOVED;
760 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
761 				PutMove(BOTTOM);
762 			}
763 		} else if (l > 0) {
764 			for (n = 1; n <= l / w->cubes.sizeX; n++) {
765 				MoveBricks(w, w->cubes.spacePosition + w->cubes.sizeX);
766 				cb.reason = CUBES_MOVED;
767 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
768 				PutMove(TOP);
769 			}
770 		} else {	/* (l == 0) */
771 			(void) printf("SelectBricks: %d\n", CUBES_SPACE);
772 			return;
773 		}
774 	} else if (l / w->cubes.sizeX == 0 &&
775 		   j == Column(w, w->cubes.spacePosition) &&
776 		   k == Stack(w, w->cubes.spacePosition)) {
777 		if (l < 0) {
778 			for (n = 1; n <= -l % w->cubes.sizeX; n++) {
779 				MoveBricks(w, w->cubes.spacePosition - 1);
780 				cb.reason = CUBES_MOVED;
781 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
782 				PutMove(RIGHT);
783 			}
784 		} else {	/* (l > 0) */
785 			for (n = 1; n <= l % w->cubes.sizeX; n++) {
786 				MoveBricks(w, w->cubes.spacePosition + 1);
787 				cb.reason = CUBES_MOVED;
788 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
789 				PutMove(LEFT);
790 			}
791 		}
792 	} else if (m % w->cubes.sizeRect == 0) {
793 		if (m < 0) {
794 			for (n = 1; n <= -Stack(w, m); n++) {
795 				MoveBricks(w, w->cubes.spacePosition - w->cubes.sizeRect);
796 				cb.reason = CUBES_MOVED;
797 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
798 				PutMove(IN);
799 			}
800 		} else {	/* (m > 0) */
801 			for (n = 1; n <= Stack(w, m); n++) {
802 				MoveBricks(w, w->cubes.spacePosition + w->cubes.sizeRect);
803 				cb.reason = CUBES_MOVED;
804 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
805 				PutMove(OUT);
806 			}
807 		}
808 	} else {
809 		(void) printf("SelectBricks: %d\n", CUBES_BLOCKED);
810 		return;
811 	}
812 	if (CheckSolved(w)) {
813 		cb.reason = CUBES_SOLVED;
814 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
815 	}
816 }
817 
818 static void
SetAllColors(CubesWidget w,Boolean init)819 SetAllColors(CubesWidget w, Boolean init)
820 {
821 	XGCValues   values;
822 	XtGCMask    valueMask;
823 
824 	valueMask = GCForeground | GCBackground;
825 
826 	if (w->cubes.reverse) {
827 		values.foreground = w->cubes.foreground;
828 		values.background = w->core.background_pixel;
829 	} else {
830 		values.foreground = w->core.background_pixel;
831 		values.background = w->cubes.foreground;
832 	}
833 	if (!init)
834 		XtReleaseGC((Widget) w, w->cubes.inverseGC);
835 	w->cubes.inverseGC = XtGetGC((Widget) w, valueMask, &values);
836 	if (w->cubes.reverse) {
837 		values.foreground = w->core.background_pixel;
838 		values.background = w->cubes.foreground;
839 	} else {
840 		values.foreground = w->cubes.foreground;
841 		values.background = w->core.background_pixel;
842 	}
843 	if (!init)
844 		XtReleaseGC((Widget) w, w->cubes.puzzleGC);
845 	w->cubes.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
846 	if (w->cubes.depth < 2 || w->cubes.mono) {
847 		if (w->cubes.reverse) {
848 			values.foreground = w->core.background_pixel;
849 			values.background = w->cubes.foreground;
850 		} else {
851 			values.foreground = w->cubes.foreground;
852 			values.background = w->core.background_pixel;
853 		}
854 	} else {
855 		values.foreground = w->cubes.brickColor;
856 		values.background = w->cubes.borderColor;
857 	}
858 	if (!init)
859 		XtReleaseGC((Widget) w, w->cubes.brickGC);
860 	w->cubes.brickGC = XtGetGC((Widget) w, valueMask, &values);
861 	if (w->cubes.depth < 2 || w->cubes.mono) {
862 		if (w->cubes.reverse) {
863 			values.foreground = w->cubes.foreground;
864 			values.background = w->core.background_pixel;
865 		} else {
866 			values.foreground = w->core.background_pixel;
867 			values.background = w->cubes.foreground;
868 		}
869 	} else {
870 		values.foreground = w->cubes.borderColor;
871 		values.background = w->cubes.brickColor;
872 	}
873 	if (!init)
874 		XtReleaseGC((Widget) w, w->cubes.borderGC);
875 	w->cubes.borderGC = XtGetGC((Widget) w, valueMask, &values);
876 }
877 
878 static void
CheckBricks(CubesWidget w)879 CheckBricks(CubesWidget w)
880 {
881 	char        buf[121];
882 
883 	if (w->cubes.sizeX < MINCUBES) {
884 		(void) sprintf(buf,
885 			       "Number of Cubes in X direction out of bounds, use %d..MAXINT",
886 			       MINCUBES);
887 		XtWarning(buf);
888 		w->cubes.sizeX = DEFAULTCUBES;
889 	}
890 	if (w->cubes.sizeY < MINCUBES) {
891 		(void) sprintf(buf,
892 			       "Number of Cubes in Y direction out of bounds, use %d..MAXINT",
893 			       MINCUBES);
894 		XtWarning(buf);
895 		w->cubes.sizeY = DEFAULTCUBES;
896 	}
897 	if (w->cubes.sizeZ < MINCUBES) {
898 		(void) sprintf(buf,
899 			       "Number of Cubes in Z direction out of bounds, use %d..MAXINT",
900 			       MINCUBES);
901 		XtWarning(buf);
902 		w->cubes.sizeZ = DEFAULTCUBES;
903 	}
904 	if (w->cubes.base > 36) {
905 		/* 10 numbers + 26 letters (ASCII or EBCDIC) */
906 		XtWarning("Base must be less than or equal to 36");
907 		w->cubes.base = 10;
908 	} else if (w->cubes.base <= 1) {	/* Base 1 is rediculous :) */
909 		XtWarning("Base must be greater than 1");
910 		w->cubes.base = 10;
911 	}
912 }
913 
914 static void
ResetBricks(CubesWidget w)915 ResetBricks(CubesWidget w)
916 {
917 	int         i;
918 
919 	w->cubes.sizeRect = w->cubes.sizeX * w->cubes.sizeY;
920 	w->cubes.sizeBlock = w->cubes.sizeRect * w->cubes.sizeZ;
921 	if (w->cubes.brickOfPosition)
922 		(void) free((void *) w->cubes.brickOfPosition);
923 	if (!(w->cubes.brickOfPosition = (int *)
924 	      malloc(sizeof (int) * w->cubes.sizeBlock)))
925 		            XtError("Not enough memory, exiting.");
926 
927 	if (startPosition)
928 		(void) free((void *) startPosition);
929 	if (!(startPosition = (int *)
930 	      malloc(sizeof (int) * w->cubes.sizeBlock)))
931 		            XtError("Not enough memory, exiting.");
932 
933 	w->cubes.spacePosition = w->cubes.sizeBlock - 1;
934 	w->cubes.brickOfPosition[w->cubes.sizeBlock - 1] = 0;
935 	for (i = 1; i < w->cubes.sizeBlock; i++)
936 		w->cubes.brickOfPosition[i - 1] = i;
937 	FlushMoves(w);
938 	w->cubes.currentPosition = -1;
939 	w->cubes.started = FALSE;
940 }
941 
942 static void
ResizeBricks(CubesWidget w)943 ResizeBricks(CubesWidget w)
944 {
945 	w->cubes.digitOffset.x = 3;
946 	w->cubes.digitOffset.y = 4;
947 }
948 
949 static void
MoveNoBricks(CubesWidget w)950 MoveNoBricks(CubesWidget w)
951 {
952 	cubesCallbackStruct cb;
953 
954 	cb.reason = CUBES_IGNORE;
955 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
956 }
957 
958 static int
MoveBricksDir(CubesWidget w,int direction)959 MoveBricksDir(CubesWidget w, int direction)
960 {
961 	switch (direction) {
962 		case TOP:
963 			if (Column(w, w->cubes.spacePosition) < w->cubes.sizeY - 1) {
964 				MoveBricks(w, w->cubes.spacePosition + w->cubes.sizeX);
965 				return TRUE;
966 			}
967 			break;
968 		case RIGHT:
969 			if (Row(w, w->cubes.spacePosition) > 0) {
970 				MoveBricks(w, w->cubes.spacePosition - 1);
971 				return TRUE;
972 			}
973 			break;
974 		case BOTTOM:
975 			if (Column(w, w->cubes.spacePosition) > 0) {
976 				MoveBricks(w, w->cubes.spacePosition - w->cubes.sizeX);
977 				return TRUE;
978 			}
979 			break;
980 		case LEFT:
981 			if (Row(w, w->cubes.spacePosition) < w->cubes.sizeX - 1) {
982 				MoveBricks(w, w->cubes.spacePosition + 1);
983 				return TRUE;
984 			}
985 			break;
986 		case IN:
987 			if (Stack(w, w->cubes.spacePosition) > 0) {
988 				MoveBricks(w, w->cubes.spacePosition - w->cubes.sizeRect);
989 				return TRUE;
990 			}
991 			break;
992 		case OUT:
993 			if (Stack(w, w->cubes.spacePosition) < w->cubes.sizeZ - 1) {
994 				MoveBricks(w, w->cubes.spacePosition + w->cubes.sizeRect);
995 				return TRUE;
996 			}
997 			break;
998 		default:
999 			(void) printf("MoveBricksDir: direction %d\n", direction);
1000 	}
1001 	return FALSE;
1002 }
1003 
1004 static void
RandomizeBricks(CubesWidget w)1005 RandomizeBricks(CubesWidget w)
1006 {
1007 	cubesCallbackStruct cb;
1008 
1009 	/* First interchange bricks an even number of times */
1010 	if (w->cubes.sizeX > 1 && w->cubes.sizeY > 1 && w->cubes.sizeZ > 1 &&
1011 	    w->cubes.sizeBlock > 4) {
1012 		int         pos, count = 0;
1013 
1014 		for (pos = 0; pos < w->cubes.sizeBlock; pos++) {
1015 			int         randomPos = pos;
1016 
1017 			while (randomPos == pos)
1018 				randomPos = NRAND(w->cubes.sizeBlock);
1019 			count += ExchangeBricks(w, pos, randomPos);
1020 		}
1021 		if (count % 2)
1022 			if (!ExchangeBricks(w, 0, 1))
1023 				if (!ExchangeBricks(w, w->cubes.sizeBlock - 2, w->cubes.sizeBlock - 1))
1024 					(void) printf("RandomizeBricks: should not get here\n");
1025 		DrawAllBricks(w);
1026 	}
1027 	/* Now move the space around randomly */
1028 	if (w->cubes.sizeX > 1 || w->cubes.sizeY > 1 || w->cubes.sizeZ > 1) {
1029 		int         big = w->cubes.sizeBlock + NRAND(2);
1030 		int         lastDirection = 0;
1031 		int         randomDirection;
1032 
1033 		cb.reason = CUBES_RESET;
1034 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1035 
1036 #ifdef DEBUG
1037 		big = 3;
1038 #endif
1039 
1040 		if (big > 1000)
1041 			big = 1000;
1042 		while (big--) {
1043 			randomDirection = NRAND(COORD);
1044 
1045 #ifdef DEBUG
1046 			sleep(1);
1047 #endif
1048 
1049 			if ((randomDirection + COORD / 2) % COORD != lastDirection ||
1050 			    (w->cubes.sizeX == 1 && w->cubes.sizeY == 1) ||
1051 			    (w->cubes.sizeY == 1 && w->cubes.sizeZ == 1) ||
1052 			    (w->cubes.sizeZ == 1 && w->cubes.sizeX == 1)) {
1053 				if (MoveCubesDir(w, randomDirection))
1054 					lastDirection = randomDirection;
1055 				else
1056 					big++;
1057 			}
1058 		}
1059 		FlushMoves(w);
1060 		cb.reason = CUBES_RANDOMIZE;
1061 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1062 	}
1063 	if (CheckSolved(w)) {
1064 		cb.reason = CUBES_SOLVED;
1065 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1066 	}
1067 }
1068 
1069 static void
MoveBricks(CubesWidget w,int from)1070 MoveBricks(CubesWidget w, int from)
1071 {
1072 	int         tempBrick;
1073 
1074 	tempBrick = w->cubes.brickOfPosition[from];
1075 	w->cubes.brickOfPosition[from] =
1076 		w->cubes.brickOfPosition[w->cubes.spacePosition];
1077 	w->cubes.brickOfPosition[w->cubes.spacePosition] = tempBrick;
1078 	DrawBrick(w, w->cubes.spacePosition, False, False, FALSE);
1079 	w->cubes.spacePosition = from;
1080 	DrawBrick(w, w->cubes.spacePosition, True, True, FALSE);
1081 }
1082 
1083 static int
ExchangeBricks(CubesWidget w,int pos1,int pos2)1084 ExchangeBricks(CubesWidget w, int pos1, int pos2)
1085 {
1086 	int         tempBrick;
1087 
1088 	if (w->cubes.brickOfPosition[pos1] <= 0)
1089 		return FALSE;
1090 	else if (w->cubes.brickOfPosition[pos2] <= 0)
1091 		return FALSE;
1092 	tempBrick = w->cubes.brickOfPosition[pos1];
1093 	w->cubes.brickOfPosition[pos1] = w->cubes.brickOfPosition[pos2];
1094 	w->cubes.brickOfPosition[pos2] = tempBrick;
1095 	return TRUE;
1096 }
1097 
1098 static void
DrawFrame(CubesWidget w,GC gc)1099 DrawFrame(CubesWidget w, GC gc)
1100 {
1101 	int         sumX, sumY, sumZ, offsetX, offsetY, dz, k;
1102 
1103 	sumX = w->cubes.sizeX * w->cubes.offset.x + w->cubes.delta.x / 2 + 1;
1104 	sumY = w->cubes.sizeY * w->cubes.offset.y + w->cubes.delta.y / 2 + 1;
1105 	offsetX = w->cubes.puzzleOffset.x;
1106 	offsetY = w->cubes.puzzleOffset.y;
1107 	if (w->cubes.vertical) {
1108 		sumZ = offsetY;
1109 		dz = sumY;
1110 	} else {
1111 		sumZ = offsetX;
1112 		dz = sumX;
1113 	}
1114 	for (k = 0; k < w->cubes.sizeZ; k++) {
1115 		if (w->cubes.vertical) {
1116 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1117 				       offsetX, sumZ, 1, sumY);
1118 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1119 				       offsetX, sumZ, sumX, 1);
1120 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1121 				       sumX + offsetX, sumZ, 1, sumY + 1);
1122 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1123 				       offsetX, sumY + sumZ, sumX + 1, 1);
1124 		} else {
1125 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1126 				       sumZ, offsetY, 1, sumY);
1127 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1128 				       sumZ, offsetY, sumX, 1);
1129 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1130 				       sumX + sumZ, offsetY, 1, sumY + 1);
1131 			XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1132 				       sumZ, sumY + offsetY, sumX + 1, 1);
1133 		}
1134 		sumZ += dz;
1135 	}
1136 }
1137 
1138 void
DrawAllBricks(CubesWidget w)1139 DrawAllBricks(CubesWidget w)
1140 {
1141 	int         k;
1142 
1143 	for (k = 0; k < w->cubes.sizeBlock; k++)
1144 		DrawBrick(w, k, (w->cubes.brickOfPosition[k] <= 0), (w->cubes.brickOfPosition[k] <= 0), FALSE);
1145 }
1146 
1147 static void
DrawBrick(CubesWidget w,int pos,Boolean blank,Boolean erase,int offset)1148 DrawBrick(CubesWidget w, int pos, Boolean blank, Boolean erase, int offset)
1149 {
1150 	int         dx, dy, dz;
1151 	GC          brickGC, borderGC;
1152 
1153 	if (erase) {
1154 		brickGC = w->cubes.inverseGC;
1155 		borderGC = w->cubes.inverseGC;
1156 	} else if (offset) {
1157 		brickGC = w->cubes.borderGC;
1158 		borderGC = w->cubes.brickGC;
1159 	} else {
1160 		brickGC = w->cubes.brickGC;
1161 		borderGC = w->cubes.borderGC;
1162 	}
1163 	if (w->cubes.vertical) {
1164 		dx = Row(w, pos) * w->cubes.offset.x + w->cubes.delta.x +
1165 			w->cubes.puzzleOffset.x + offset;
1166 		dy = Column(w, pos) * w->cubes.offset.y + w->cubes.delta.y +
1167 			w->cubes.puzzleOffset.y + offset;
1168 		dz = Stack(w, pos) * (w->cubes.sizeY * w->cubes.offset.y +
1169 				      w->cubes.delta.y - 1);
1170 		dy += dz;
1171 	} else {
1172 		dx = Row(w, pos) * w->cubes.offset.x + w->cubes.delta.x +
1173 			w->cubes.puzzleOffset.x + offset;
1174 		dy = Column(w, pos) * w->cubes.offset.y + w->cubes.delta.y +
1175 			w->cubes.puzzleOffset.y + offset;
1176 		dz = Stack(w, pos) * (w->cubes.sizeX * w->cubes.offset.x +
1177 				      w->cubes.delta.x - 1);
1178 		dx += dz;
1179 	}
1180 	XFillRectangle(XtDisplay(w), XtWindow(w), brickGC, dx, dy,
1181 		       w->cubes.brickSize.x, w->cubes.brickSize.y);
1182 	XDrawRectangle(XtDisplay(w), XtWindow(w), borderGC, dx, dy,
1183 		       w->cubes.brickSize.x, w->cubes.brickSize.y);
1184 	if (!blank) {
1185 		int         i = 0, offsetX = 0, brick = w->cubes.brickOfPosition[pos];
1186 		char        buf[5];
1187 
1188 		(void) int2String(buf, brick, w->cubes.base);
1189 		while (brick >= 1) {
1190 			brick /= w->cubes.base;
1191 			offsetX += w->cubes.digitOffset.x;
1192 			i++;
1193 		}
1194 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1195 			    dx + w->cubes.brickSize.x / 2 - offsetX,
1196 			    dy + w->cubes.brickSize.y / 2 + w->cubes.digitOffset.y, buf, i);
1197 	}
1198 }
1199 
1200 static int
Row(CubesWidget w,int pos)1201 Row(CubesWidget w, int pos)
1202 {
1203 	return ((pos % w->cubes.sizeRect) % w->cubes.sizeX);
1204 }
1205 
1206 static int
Column(CubesWidget w,int pos)1207 Column(CubesWidget w, int pos)
1208 {
1209 	return ((pos % w->cubes.sizeRect) / w->cubes.sizeX);
1210 }
1211 
1212 static int
Stack(CubesWidget w,int pos)1213 Stack(CubesWidget w, int pos)
1214 {
1215 	return (pos / w->cubes.sizeRect);
1216 }
1217 
1218 static int
int2String(char * buf,int number,int base)1219 int2String(char *buf, int number, int base)
1220 {
1221 	int         digit, mult = base, last, position;
1222 
1223 	if (number < 0) {
1224 		(void) printf("number %d < 0\n", number);
1225 		return 0;
1226 	}
1227 	last = 1;
1228 	while (number >= mult) {
1229 		last++;
1230 		mult *= base;
1231 	}
1232 	for (position = 0; position < last; position++) {
1233 		mult /= base;
1234 		digit = number / mult;
1235 		number -= digit * mult;
1236 		buf[position] = digit + '0';
1237 		if (buf[position] > '9') {	/* ASCII */
1238 			buf[position] += ('A' - '9' - 1);
1239 		} else if (buf[position] < '0') {	/* EBCDIC */
1240 			buf[position] += ('A' - '9' - 1);
1241 			if (buf[position] > 'I')
1242 				buf[position] += ('J' - 'I' - 1);
1243 			if (buf[position] > 'R')
1244 				buf[position] += ('S' - 'R' - 1);
1245 		}
1246 	}
1247 	buf[last] = '\0';
1248 	return last;
1249 }
1250 
1251 Boolean
CheckSolved(CubesWidget w)1252 CheckSolved(CubesWidget w)
1253 {
1254 	int         i;
1255 
1256 	for (i = 1; i < w->cubes.sizeBlock; i++)
1257 		if (w->cubes.brickOfPosition[i - 1] != i)
1258 			return FALSE;
1259 	return TRUE;
1260 }
1261