1 /*-
2 # X-BASED PYRAMINX(tm)
3 #
4 #  Pyraminx.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 Pyraminx */
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 "PyraminxP.h"
42 
43 #ifndef DATAFILE
44 #define DATAFILE "/usr/games/lib/pyraminx.data"
45 #endif
46 
47 #define NOTDIR(x) ((x==CW)?CCW:CW)
48 
49 typedef struct _CRD {
50 	int         column, row, diagonal;
51 } CRD;
52 
53 static void InitializePyraminx(Widget request, Widget renew);
54 static void ExposePyraminx(Widget renew, XEvent * event, Region region);
55 static void ResizePyraminx(PyraminxWidget w);
56 static void DestroyPyraminx(Widget old);
57 static Boolean SetValuesPyraminx(Widget current, Widget request, Widget renew);
58 static void QuitPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
59 static void PracticePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
60 static void PracticePyraminxMaybe(PyraminxWidget w, XEvent * event, char **args, int nArgs);
61 static void RandomizePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
62 static void RandomizePyraminxMaybe(PyraminxWidget w, XEvent * event, char **args, int nArgs);
63 static void GetPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
64 static void WritePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
65 static void UndoPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
66 static void SolvePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
67 static void IncrementPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
68 static void DecrementPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
69 static void OrientizePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
70 static void Period2ModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
71 static void Period3ModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
72 static void BothModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
73 static void StickyModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
74 static void MovePyraminxTop(PyraminxWidget w, XEvent * event, char **args, int nArgs);
75 static void MovePyraminxTr(PyraminxWidget w, XEvent * event, char **args, int nArgs);
76 static void MovePyraminxLeft(PyraminxWidget w, XEvent * event, char **args, int nArgs);
77 static void MovePyraminxCw(PyraminxWidget w, XEvent * event, char **args, int nArgs);
78 static void MovePyraminxRight(PyraminxWidget w, XEvent * event, char **args, int nArgs);
79 static void MovePyraminxBl(PyraminxWidget w, XEvent * event, char **args, int nArgs);
80 static void MovePyraminxBottom(PyraminxWidget w, XEvent * event, char **args, int nArgs);
81 static void MovePyraminxCcw(PyraminxWidget w, XEvent * event, char **args, int nArgs);
82 static void MovePyraminxInput(PyraminxWidget w, int x, int y, int direction, int shift, int control);
83 static void SelectPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
84 static void ReleasePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs);
85 
86 static void SetAllColors(PyraminxWidget w, Boolean init);
87 static void GetColor(PyraminxWidget w, int face, Boolean init);
88 static void MoveControlCb(PyraminxWidget w, int face, int direction, int style);
89 static void CheckPolyhedrons(PyraminxWidget w);
90 static void ResetPolyhedrons(PyraminxWidget w);
91 static void ResizePolyhedrons(PyraminxWidget w);
92 static Boolean SelectPolyhedrons(PyraminxWidget w, int x, int y, int *face, CRD * crd);
93 static void NarrowSelection(PyraminxWidget w, int style, int *face, CRD * crd, int *direction);
94 static Boolean PositionPolyhedrons(PyraminxWidget w, int x, int y, int style, int *face, CRD * crd, int *direction);
95 static void MoveNoPolyhedrons(PyraminxWidget w);
96 static void PracticePolyhedrons(PyraminxWidget w);
97 static void RandomizePolyhedrons(PyraminxWidget w);
98 static void MovePolyhedrons(PyraminxWidget w, int face, CRD crd, int direction, int style);
99 static void RotateFace(PyraminxWidget w, int view, int faceOnView, int direction);
100 
101 /* CRD : Column, Row, Diagonal */
102 static void ReadCRD2(PyraminxWidget w, int view, int dir, int h, int orient);
103 static void ReadCRD3(PyraminxWidget w, int view, int faceOnView, int dir, int h, int len, int orient);
104 static void RotateCRD2(PyraminxWidget w, int rotate, int orient);
105 static void RotateCRD3(PyraminxWidget w, int faceOnView, int dir, int len, int orient);
106 static void ReverseCRD2(PyraminxWidget w, int orient);
107 static void ReverseCRD3(PyraminxWidget w, int len, int orient);
108 static void WriteCRD2(PyraminxWidget w, int view, int dir, int h, int orient);
109 static void WriteCRD3(PyraminxWidget w, int view, int faceOnView, int dir, int h, int len, int orient);
110 static void DrawFrame(PyraminxWidget w, GC gc);
111 static void DrawTriangle(PyraminxWidget w, int face, int position, int offset);
112 static void DrawOrientLine(PyraminxWidget w, int orient, int dx, int dy, int side, GC borderGC);
113 static int  Crd(int dir, int I, int J, int side);
114 static int  Length(PyraminxWidget w, int face, int dir, int h);
115 static int  CheckMoveDir(PyraminxWidget w, CRD crd1, CRD crd2, int face, int *direction);
116 static CRD  ToCRD(PyraminxWidget w, int face, int position);
117 static int  ToPosition(PyraminxWidget w, CRD crd);
118 static int  Sqrt(int i);
119 
120 #ifdef DEBUG
121 static void PrintTetra(PyraminxWidget w);
122 static void PrintFace(PyraminxWidget w);
123 static void PrintRow2(PyraminxWidget w, int orient);
124 static void PrintRow3(PyraminxWidget w, int len, int orient);
125 
126 #endif
127 
128 static char defaultTranslationsPyraminx[] =
129 "<KeyPress>q: Quit()\n\
130    Ctrl<KeyPress>C: Quit()\n\
131    <KeyPress>KP_Divide: MoveCcw()\n\
132    <KeyPress>Up: MoveTop()\n\
133    <KeyPress>KP_8: MoveTop()\n\
134    <KeyPress>R8: MoveTop()\n\
135    <KeyPress>Prior: MoveTr()\n\
136    <KeyPress>KP_9: MoveTr()\n\
137    <KeyPress>R9: MoveTr()\n\
138    <KeyPress>Left: MoveLeft()\n\
139    <KeyPress>KP_4: MoveLeft()\n\
140    <KeyPress>R10: MoveLeft()\n\
141    <KeyPress>Begin: MoveCw()\n\
142    <KeyPress>KP_5: MoveCw()\n\
143    <KeyPress>R11: MoveCw()\n\
144    <KeyPress>Right: MoveRight()\n\
145    <KeyPress>KP_6: MoveRight()\n\
146    <KeyPress>R12: MoveRight()\n\
147    <KeyPress>End: MoveBl()\n\
148    <KeyPress>KP_1: MoveBl()\n\
149    <KeyPress>R13: MoveBl()\n\
150    <KeyPress>Down: MoveBottom()\n\
151    <KeyPress>KP_2: MoveBottom()\n\
152    <KeyPress>R14: MoveBottom()\n\
153    <Btn1Down>: Select()\n\
154    <Btn1Up>: Release()\n\
155    <KeyPress>p: Practice()\n\
156    <Btn2Down>(2+): Practice()\n\
157    <Btn2Down>: PracticeMaybe()\n\
158    <KeyPress>r: Randomize()\n\
159    <Btn3Down>(2+): Randomize()\n\
160    <Btn3Down>: RandomizeMaybe()\n\
161    <KeyPress>g: Get()\n\
162    <KeyPress>w: Write()\n\
163    <KeyPress>u: Undo()\n\
164    <KeyPress>s: Solve()\n\
165    <KeyPress>i: Increment()\n\
166    <KeyPress>d: Decrement()\n\
167    <KeyPress>o: Orientize()\n\
168    <KeyPress>2: Period2()\n\
169    <KeyPress>3: Period3()\n\
170    <KeyPress>b: Both()\n\
171    <KeyPress>y: Sticky()";
172 
173 static XtActionsRec actionsListPyraminx[] =
174 {
175 	{"Quit", (XtActionProc) QuitPyraminx},
176 	{"MoveCcw", (XtActionProc) MovePyraminxCcw},
177 	{"MoveTop", (XtActionProc) MovePyraminxTop},
178 	{"MoveTr", (XtActionProc) MovePyraminxTr},
179 	{"MoveLeft", (XtActionProc) MovePyraminxLeft},
180 	{"MoveCw", (XtActionProc) MovePyraminxCw},
181 	{"MoveRight", (XtActionProc) MovePyraminxRight},
182 	{"MoveBl", (XtActionProc) MovePyraminxBl},
183 	{"MoveBottom", (XtActionProc) MovePyraminxBottom},
184 	{"Select", (XtActionProc) SelectPyraminx},
185 	{"Release", (XtActionProc) ReleasePyraminx},
186 	{"Practice", (XtActionProc) PracticePyraminx},
187 	{"PracticeMaybe", (XtActionProc) PracticePyraminxMaybe},
188 	{"Randomize", (XtActionProc) RandomizePyraminx},
189 	{"RandomizeMaybe", (XtActionProc) RandomizePyraminxMaybe},
190 	{"Get", (XtActionProc) GetPyraminx},
191 	{"Write", (XtActionProc) WritePyraminx},
192 	{"Undo", (XtActionProc) UndoPyraminx},
193 	{"Solve", (XtActionProc) SolvePyraminx},
194 	{"Increment", (XtActionProc) IncrementPyraminx},
195 	{"Decrement", (XtActionProc) DecrementPyraminx},
196 	{"Orientize", (XtActionProc) OrientizePyraminx},
197 	{"Period2", (XtActionProc) Period2ModePyraminx},
198 	{"Period3", (XtActionProc) Period3ModePyraminx},
199 	{"Both", (XtActionProc) BothModePyraminx},
200 	{"Sticky", (XtActionProc) StickyModePyraminx}
201 };
202 
203 static XtResource resourcesPyraminx[] =
204 {
205 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
206 	 XtOffset(PyraminxWidget, pyraminx.username), XtRString, "nobody"},
207   /* Beware color values are swapped */
208 	{XtNfaceColor0, XtCLabel, XtRString, sizeof (String),
209 	 XtOffset(PyraminxWidget, pyraminx.faceName[0]), XtRString, "Blue"},
210 	{XtNfaceColor1, XtCLabel, XtRString, sizeof (String),
211 	 XtOffset(PyraminxWidget, pyraminx.faceName[1]), XtRString, "Red"},
212 	{XtNfaceColor2, XtCLabel, XtRString, sizeof (String),
213 	 XtOffset(PyraminxWidget, pyraminx.faceName[2]), XtRString, "Yellow"},
214 	{XtNfaceColor3, XtCLabel, XtRString, sizeof (String),
215 	 XtOffset(PyraminxWidget, pyraminx.faceName[3]), XtRString, "Green"},
216 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
217 	 XtOffset(PyraminxWidget, pyraminx.foreground), XtRString,
218 	 XtDefaultForeground},
219 	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
220 	 XtOffset(PyraminxWidget, pyraminx.borderColor), XtRString,
221 	 XtDefaultForeground},
222 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
223 	 XtOffset(PyraminxWidget, core.width), XtRString, "200"},
224 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
225 	 XtOffset(PyraminxWidget, core.height), XtRString, "400"},
226 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
227 	 XtOffset(PyraminxWidget, pyraminx.mono), XtRString, "FALSE"},
228 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
229 	 XtOffset(PyraminxWidget, pyraminx.reverse), XtRString, "FALSE"},
230 	{XtNsize, XtCSize, XtRInt, sizeof (int),
231 	 XtOffset(PyraminxWidget, pyraminx.size), XtRString, "3"},	/*DEFAULTTETRAS */
232 	{XtNsticky, XtCSticky, XtRBoolean, sizeof (Boolean),
233 	 XtOffset(PyraminxWidget, pyraminx.sticky), XtRString, "FALSE"},
234 	{XtNmode, XtCMode, XtRInt, sizeof (int),
235 	 XtOffset(PyraminxWidget, pyraminx.mode), XtRString, "3"},	/*DEFAULTMODE */
236 	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
237 	 XtOffset(PyraminxWidget, pyraminx.orient), XtRString,
238 	 "FALSE"},		/*DEFAULTORIENT */
239 	{XtNpractice, XtCPractice, XtRBoolean, sizeof (Boolean),
240 	 XtOffset(PyraminxWidget, pyraminx.practice), XtRString,
241 	 "FALSE"},		/*DEFAULTPRACTICE */
242 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
243 	 XtOffset(PyraminxWidget, pyraminx.started), XtRString, "FALSE"},
244 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
245 	 XtOffset(PyraminxWidget, pyraminx.select), XtRCallback, NULL}
246 };
247 
248 PyraminxClassRec pyraminxClassRec =
249 {
250 	{
251 		(WidgetClass) & widgetClassRec,		/* superclass */
252 		"Pyraminx",	/* class name */
253 		sizeof (PyraminxRec),	/* widget size */
254 		NULL,		/* class initialize */
255 		NULL,		/* class part initialize */
256 		FALSE,		/* class inited */
257 		(XtInitProc) InitializePyraminx,	/* initialize */
258 		NULL,		/* initialize hook */
259 		XtInheritRealize,	/* realize */
260 		actionsListPyraminx,	/* actions */
261 		XtNumber(actionsListPyraminx),	/* num actions */
262 		resourcesPyraminx,	/* resources */
263 		XtNumber(resourcesPyraminx),	/* num resources */
264 		NULLQUARK,	/* xrm class */
265 		TRUE,		/* compress motion */
266 		TRUE,		/* compress exposure */
267 		TRUE,		/* compress enterleave */
268 		TRUE,		/* visible interest */
269 		(XtWidgetProc) DestroyPyraminx,		/* destroy */
270 		(XtWidgetProc) ResizePyraminx,	/* resize */
271 		(XtExposeProc) ExposePyraminx,	/* expose */
272 		(XtSetValuesFunc) SetValuesPyraminx,	/* set values */
273 		NULL,		/* set values hook */
274 		XtInheritSetValuesAlmost,	/* set values almost */
275 		NULL,		/* get values hook */
276 		NULL,		/* accept focus */
277 		XtVersion,	/* version */
278 		NULL,		/* callback private */
279 		defaultTranslationsPyraminx,	/* tm table */
280 		NULL,		/* query geometry */
281 		NULL,		/* display accelerator */
282 		NULL		/* extension */
283 	},
284 	{
285 		0		/* ignore */
286 	}
287 };
288 
289 WidgetClass pyraminxWidgetClass = (WidgetClass) & pyraminxClassRec;
290 
291 typedef struct _RowNextP3 {
292 	int         viewChanged, face, direction, reverse;
293 } RowNextP3;
294 static PyraminxLoc slideNextRowP2[MAXSIDES][3] =
295 {
296 	{
297 		{2, 0},
298 		{1, 3},
299 		{2, 3}},
300 	{
301 		{2, 0},
302 		{0, 3},
303 		{2, 3}}
304 };
305 static RowNextP3 slideNextRowP3[MAXSIDES][MAXORIENT] =
306 {
307 	{
308 		{TRUE, UP, TR, FALSE},
309 		{TRUE, UP, TOP, FALSE},
310 		{FALSE, UP, BOTTOM, TRUE},
311 		{FALSE, UP, RIGHT, TRUE},
312 		{TRUE, DOWN, RIGHT, TRUE},
313 		{TRUE, DOWN, TR, TRUE}
314 	},
315 	{
316 		{FALSE, DOWN, LEFT, TRUE},
317 		{TRUE, UP, LEFT, TRUE},
318 		{TRUE, UP, BL, TRUE},
319 		{TRUE, DOWN, BL, FALSE},
320 		{TRUE, DOWN, BOTTOM, FALSE},
321 		{FALSE, DOWN, TOP, TRUE}
322 	}
323 };
324 static int  rotOrientRowP3[3][MAXORIENT] =
325 /* current orient, rotation */
326 {
327 	{1, 5, 1, 5, 4, 2},
328 	{2, 0, 2, 0, 5, 3},
329 	{3, 1, 3, 1, 0, 4}
330 };
331 
332 static XPoint triangleUnit[MAXSIDES][4] =
333 {
334 	{
335 		{0, 2},
336 		{1, 0},
337 		{-1, 1},
338 		{0, -1}},
339 	{
340 		{1, 3},
341 		{-1, 0},
342 		{1, -1},
343 		{0, 1}}
344 };
345 static XPoint triangleList[MAXSIDES][4], letterList[MAXSIDES], offsetList[MAXSIDES];
346 
347 static void
InitializePyraminx(Widget request,Widget renew)348 InitializePyraminx(Widget request, Widget renew)
349 {
350 	PyraminxWidget w = (PyraminxWidget) renew;
351 	int         face, orient, side;
352 
353 	for (face = 0; face < MAXFACES; face++)
354 		w->pyraminx.tetraLoc[face] = NULL;
355 	for (orient = 0; orient < 3; orient++)
356 		for (side = 0; side < MAXSIDES; side++)
357 			w->pyraminx.rowLoc[orient][side] = NULL;
358 	for (side = 0; side < MAXSIDES; side++)
359 		w->pyraminx.faceLoc[side] = NULL;
360 	CheckPolyhedrons(w);
361 	InitMoves();
362 	ResetPolyhedrons(w);
363 	(void) SRAND(getpid());
364 	w->pyraminx.depth = DefaultDepthOfScreen(XtScreen(w));
365 	SetAllColors(w, True);
366 	ResizePyraminx(w);
367 }
368 
369 static void
DestroyPyraminx(Widget old)370 DestroyPyraminx(Widget old)
371 {
372 	PyraminxWidget w = (PyraminxWidget) old;
373 	int         face;
374 
375 	for (face = 0; face < MAXFACES; face++)
376 		XtReleaseGC(old, w->pyraminx.faceGC[face]);
377 	XtReleaseGC(old, w->pyraminx.borderGC);
378 	XtReleaseGC(old, w->pyraminx.puzzleGC);
379 	XtReleaseGC(old, w->pyraminx.inverseGC);
380 	XtRemoveCallbacks(old, XtNselectCallback, w->pyraminx.select);
381 }
382 
383 static void
ResizePyraminx(PyraminxWidget w)384 ResizePyraminx(PyraminxWidget w)
385 {
386 	int         tempLength;
387 
388 	w->pyraminx.delta = 4;
389 	w->pyraminx.vertical = (w->core.height >= w->core.width);
390 	if (w->pyraminx.vertical)
391 		tempLength = MIN(w->core.height / 2, w->core.width);
392 	else
393 		tempLength = MIN(w->core.height, w->core.width / 2);
394 	w->pyraminx.tetraLength = MAX((tempLength - w->pyraminx.delta + 1) /
395 				      w->pyraminx.size, 0);
396 	w->pyraminx.faceLength = w->pyraminx.size * w->pyraminx.tetraLength;
397 	w->pyraminx.viewLength = w->pyraminx.faceLength + w->pyraminx.delta + 3;
398 	if (w->pyraminx.vertical) {
399 		w->pyraminx.puzzleSize.x = w->pyraminx.viewLength - 1;
400 		w->pyraminx.puzzleSize.y = 2 * w->pyraminx.viewLength -
401 			w->pyraminx.delta - 2;
402 	} else {
403 		w->pyraminx.puzzleSize.x = 2 * w->pyraminx.viewLength -
404 			w->pyraminx.delta - 2;
405 		w->pyraminx.puzzleSize.y = w->pyraminx.viewLength - 1;
406 	}
407 	w->pyraminx.puzzleOffset.x = ((int) w->core.width -
408 				      w->pyraminx.puzzleSize.x) / 2;
409 	w->pyraminx.puzzleOffset.y = ((int) w->core.height -
410 				      w->pyraminx.puzzleSize.y) / 2;
411 	ResizePolyhedrons(w);
412 }
413 
414 static void
ExposePyraminx(Widget renew,XEvent * event,Region region)415 ExposePyraminx(Widget renew, XEvent * event, Region region)
416 {
417 	PyraminxWidget w = (PyraminxWidget) renew;
418 
419 	if (w->core.visible) {
420 		if (w->pyraminx.reverse)
421 			XFillRectangle(XtDisplay(w), XtWindow(w),
422 				       w->pyraminx.inverseGC, 0, 0, w->core.width, w->core.height);
423 		DrawFrame(w, w->pyraminx.puzzleGC);
424 		DrawAllPolyhedrons(w);
425 	}
426 }
427 
428 static      Boolean
SetValuesPyraminx(Widget current,Widget request,Widget renew)429 SetValuesPyraminx(Widget current, Widget request, Widget renew)
430 {
431 	PyraminxWidget c = (PyraminxWidget) current, w = (PyraminxWidget) renew;
432 	Boolean     redraw = False, setColors = False;
433 	int         face;
434 
435 	CheckPolyhedrons(w);
436 	for (face = 0; face < MAXFACES; face++) {
437 		if (strcmp(w->pyraminx.faceName[face], c->pyraminx.faceName[face])) {
438 			setColors = True;
439 			break;
440 		}
441 	}
442 	if (w->core.background_pixel != c->core.background_pixel ||
443 	    w->pyraminx.foreground != c->pyraminx.foreground ||
444 	    w->pyraminx.borderColor != c->pyraminx.borderColor ||
445 	    w->pyraminx.reverse != c->pyraminx.reverse ||
446 	    w->pyraminx.mono != c->pyraminx.mono ||
447 	    setColors) {
448 		SetAllColors(w, False);
449 		redraw = True;
450 	}
451 	if (w->pyraminx.orient != c->pyraminx.orient) {
452 		ResetPolyhedrons(w);
453 		redraw = True;
454 	} else if (w->pyraminx.practice != c->pyraminx.practice) {
455 		ResetPolyhedrons(w);
456 		redraw = True;
457 	}
458 	if (w->pyraminx.size != c->pyraminx.size ||
459 	    w->pyraminx.mode != c->pyraminx.mode ||
460 	    w->pyraminx.sticky != c->pyraminx.sticky) {
461 		ResetPolyhedrons(w);
462 		ResizePyraminx(w);
463 		redraw = True;
464 	}
465 	if (w->pyraminx.tetraLength != c->pyraminx.tetraLength) {
466 		ResizePyraminx(w);
467 		redraw = True;
468 	}
469 	return (redraw);
470 }
471 
472 static void
QuitPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)473 QuitPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
474 {
475 	XtCloseDisplay(XtDisplay(w));
476 	exit(0);
477 }
478 
479 static void
SelectPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)480 SelectPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
481 {
482 	int         control;
483 	CRD         crd;
484 
485 	if (SelectPolyhedrons(w, event->xbutton.x, event->xbutton.y,
486 			      &(w->pyraminx.currentFace), &crd)) {
487 		control = (int) (event->xkey.state & ControlMask);
488 		w->pyraminx.currentPosition = ToPosition(w, crd);
489 		if (control || w->pyraminx.practice || !CheckSolved(w))
490 			DrawTriangle(w, w->pyraminx.currentFace, w->pyraminx.currentPosition,
491 				     TRUE);
492 	} else
493 		w->pyraminx.currentFace = -1;
494 }
495 
496 static void
ReleasePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)497 ReleasePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
498 {
499 	int         shift, control, style, face, count = -1, direction = 0;
500 	CRD         crd;
501 	pyraminxCallbackStruct cb;
502 
503 	if (w->pyraminx.currentFace == -1)
504 		return;
505 	DrawTriangle(w, w->pyraminx.currentFace, w->pyraminx.currentPosition,
506 		     FALSE);
507 	shift = (int) (event->xbutton.state & (ShiftMask | LockMask));
508 	control = (int) (event->xkey.state & ControlMask);
509 	if (!control && !w->pyraminx.practice && CheckSolved(w))
510 		MoveNoPolyhedrons(w);
511 	else if (SelectPolyhedrons(w, event->xbutton.x, event->xbutton.y,
512 				   &face, &crd)) {
513 		control = (control) ? 1 : 0;
514 		if (w->pyraminx.mode != BOTH) {
515 			if (control && shift)
516 				style = (w->pyraminx.mode == PERIOD3) ? PERIOD2 : PERIOD3;
517 			else
518 				style = (w->pyraminx.mode == PERIOD2) ? PERIOD2 : PERIOD3;
519 		} else
520 			style = (shift) ? PERIOD3 : PERIOD2;
521 		if (face == w->pyraminx.currentFace)
522 			count = CheckMoveDir(w, ToCRD(w, face, w->pyraminx.currentPosition),
523 					     crd, face, &direction);
524 		if (count == 1) {
525 			NarrowSelection(w, style, &face, &crd, &direction);
526 			MovePyraminx(w, face, w->pyraminx.currentPosition, direction, style,
527 				     control);
528 			if (!control && CheckSolved(w)) {
529 				cb.reason = PYRAMINX_SOLVED;
530 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
531 			}
532 		} else if (count == 2) {
533 			cb.reason = PYRAMINX_AMBIGUOUS;
534 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
535 		} else if (count == 0)
536 			MoveNoPolyhedrons(w);
537 	}
538 }
539 
540 static void
PracticePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)541 PracticePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
542 {
543 	PracticePolyhedrons(w);
544 }
545 
546 static void
PracticePyraminxMaybe(PyraminxWidget w,XEvent * event,char ** args,int nArgs)547 PracticePyraminxMaybe(PyraminxWidget w, XEvent * event, char **args, int nArgs)
548 {
549 	if (!w->pyraminx.started)
550 		PracticePolyhedrons(w);
551 }
552 
553 static void
RandomizePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)554 RandomizePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
555 {
556 	RandomizePolyhedrons(w);
557 }
558 
559 static void
RandomizePyraminxMaybe(PyraminxWidget w,XEvent * event,char ** args,int nArgs)560 RandomizePyraminxMaybe(PyraminxWidget w, XEvent * event, char **args, int nArgs)
561 {
562 	if (!w->pyraminx.started)
563 		RandomizePolyhedrons(w);
564 }
565 
566 static void
GetPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)567 GetPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
568 {
569 	FILE       *fp;
570 	char        c;
571 	int         i, size, mode, sticky, orient, practice, moves;
572 	pyraminxCallbackStruct cb;
573 
574 	if ((fp = fopen(DATAFILE, "r")) == NULL)
575 		(void) printf("Can not read %s for a get.\n", DATAFILE);
576 	else {
577 		FlushMoves(w);
578 		while ((c = getc(fp)) != EOF && c != SYMBOL);
579 		(void) fscanf(fp, "%d", &size);
580 		if (size >= MINTETRAS) {
581 			for (i = w->pyraminx.size; i < size; i++) {
582 				cb.reason = PYRAMINX_INC;
583 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
584 			}
585 			for (i = w->pyraminx.size; i > size; i--) {
586 				cb.reason = PYRAMINX_DEC;
587 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
588 			}
589 		} else
590 			(void) printf("%s corrupted: size %d should be between %d and MAXINT\n",
591 				      DATAFILE, size, MINTETRAS);
592 		while ((c = getc(fp)) != EOF && c != SYMBOL);
593 		(void) fscanf(fp, "%d", &mode);
594 		if (mode >= PERIOD2 && mode <= BOTH)
595 			switch (mode) {
596 				case PERIOD2:
597 					cb.reason = PYRAMINX_PERIOD2;
598 					XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
599 					break;
600 				case PERIOD3:
601 					cb.reason = PYRAMINX_PERIOD3;
602 					XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
603 					break;
604 				case BOTH:
605 					cb.reason = PYRAMINX_BOTH;
606 					XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
607 		} else
608 			(void) printf("%s corrupted: mode %d should be between %d and %d\n",
609 				      DATAFILE, mode, PERIOD2, BOTH);
610 		while ((c = getc(fp)) != EOF && c != SYMBOL);
611 		(void) fscanf(fp, "%d", &sticky);
612 		if (w->pyraminx.sticky != (Boolean) sticky) {
613 			cb.reason = PYRAMINX_STICKY;
614 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
615 		}
616 		while ((c = getc(fp)) != EOF && c != SYMBOL);
617 		(void) fscanf(fp, "%d", &orient);
618 		if (w->pyraminx.orient != (Boolean) orient) {
619 			cb.reason = PYRAMINX_ORIENT;
620 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
621 		}
622 		while ((c = getc(fp)) != EOF && c != SYMBOL);
623 		(void) fscanf(fp, "%d", &practice);
624 		if (w->pyraminx.practice != (Boolean) practice) {
625 			cb.reason = PYRAMINX_PRACTICE;
626 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
627 		}
628 		while ((c = getc(fp)) != EOF && c != SYMBOL);
629 		(void) fscanf(fp, "%d", &moves);
630 		ScanStartPosition(fp, w);
631 		cb.reason = PYRAMINX_RESTORE;
632 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
633 		SetStartPosition(w);
634 		ScanMoves(fp, w, moves);
635 		(void) fclose(fp);
636 		(void) printf("%s: size %d, mode %d, sticky %d, orient %d",
637 			      DATAFILE, size, mode, sticky, orient);
638 		(void) printf(", practice %d, moves %d.\n", practice, moves);
639 	}
640 }
641 
642 static void
WritePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)643 WritePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
644 {
645 	FILE       *fp;
646 
647 	if ((fp = fopen(DATAFILE, "w")) == NULL)
648 		(void) printf("Can not write to %s.\n", DATAFILE);
649 	else {
650 		(void) fprintf(fp, "size%c %d\n", SYMBOL, w->pyraminx.size);
651 		(void) fprintf(fp, "mode%c %d\n", SYMBOL, w->pyraminx.mode);
652 		(void) fprintf(fp, "sticky%c %d\n", SYMBOL, (w->pyraminx.sticky) ? 1 : 0);
653 		(void) fprintf(fp, "orient%c %d\n", SYMBOL, (w->pyraminx.orient) ? 1 : 0);
654 		(void) fprintf(fp, "practice%c %d\n", SYMBOL,
655 			       (w->pyraminx.practice) ? 1 : 0);
656 		(void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
657 		PrintStartPosition(fp, w);
658 		PrintMoves(fp);
659 		(void) fclose(fp);
660 		(void) printf("Saved to %s.\n", DATAFILE);
661 	}
662 }
663 
664 static void
UndoPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)665 UndoPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
666 {
667 	if (MadeMoves()) {
668 		int         face, position, direction, style, control;
669 
670 		GetMove(&face, &position, &direction, &style, &control);
671 		direction = (direction < MAXORIENT) ? (direction + MAXORIENT / 2) %
672 			MAXORIENT : 3 * MAXORIENT - direction;
673 		if (control)
674 			MoveControlCb(w, face, direction, style);
675 		else {
676 			pyraminxCallbackStruct cb;
677 
678 			MovePolyhedrons(w, face, ToCRD(w, face, position), direction, style);
679 			cb.reason = PYRAMINX_UNDO;
680 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
681 		}
682 	}
683 }
684 
685 static void
SolvePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)686 SolvePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
687 {
688 #if 0
689 	SolvePolyhedrons(w);	/* Sorry, this is not implemented */
690 #endif
691 }
692 
693 static void
IncrementPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)694 IncrementPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
695 {
696 	pyraminxCallbackStruct cb;
697 
698 	cb.reason = PYRAMINX_INC;
699 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
700 }
701 
702 static void
DecrementPyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)703 DecrementPyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
704 {
705 	pyraminxCallbackStruct cb;
706 
707 	if (w->pyraminx.size <= MINTETRAS)
708 		return;
709 	cb.reason = PYRAMINX_DEC;
710 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
711 }
712 
713 static void
OrientizePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)714 OrientizePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
715 {
716 	pyraminxCallbackStruct cb;
717 
718 	cb.reason = PYRAMINX_ORIENT;
719 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
720 }
721 
722 static void
Period2ModePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)723 Period2ModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
724 {
725 	pyraminxCallbackStruct cb;
726 
727 	cb.reason = PYRAMINX_PERIOD2;
728 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
729 }
730 
731 static void
Period3ModePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)732 Period3ModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
733 {
734 	pyraminxCallbackStruct cb;
735 
736 	cb.reason = PYRAMINX_PERIOD3;
737 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
738 }
739 
740 static void
BothModePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)741 BothModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
742 {
743 	pyraminxCallbackStruct cb;
744 
745 	cb.reason = PYRAMINX_BOTH;
746 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
747 }
748 
749 static void
StickyModePyraminx(PyraminxWidget w,XEvent * event,char ** args,int nArgs)750 StickyModePyraminx(PyraminxWidget w, XEvent * event, char **args, int nArgs)
751 {
752 	pyraminxCallbackStruct cb;
753 
754 	cb.reason = PYRAMINX_STICKY;
755 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
756 }
757 
758 static void
MovePyraminxCcw(PyraminxWidget w,XEvent * event,char ** args,int nArgs)759 MovePyraminxCcw(PyraminxWidget w, XEvent * event, char **args, int nArgs)
760 {
761 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, CCW,
762 		       (int) (event->xbutton.state & (ShiftMask | LockMask)),
763 			  (int) (event->xbutton.state & ControlMask));
764 }
765 
766 static void
MovePyraminxTop(PyraminxWidget w,XEvent * event,char ** args,int nArgs)767 MovePyraminxTop(PyraminxWidget w, XEvent * event, char **args, int nArgs)
768 {
769 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, TOP,
770 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
771 			  (int) (event->xkey.state & ControlMask));
772 }
773 
774 static void
MovePyraminxTr(PyraminxWidget w,XEvent * event,char ** args,int nArgs)775 MovePyraminxTr(PyraminxWidget w, XEvent * event, char **args, int nArgs)
776 {
777 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, TR,
778 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
779 			  (int) (event->xkey.state & ControlMask));
780 }
781 
782 static void
MovePyraminxLeft(PyraminxWidget w,XEvent * event,char ** args,int nArgs)783 MovePyraminxLeft(PyraminxWidget w, XEvent * event, char **args, int nArgs)
784 {
785 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, LEFT,
786 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
787 			  (int) (event->xkey.state & ControlMask));
788 }
789 
790 static void
MovePyraminxCw(PyraminxWidget w,XEvent * event,char ** args,int nArgs)791 MovePyraminxCw(PyraminxWidget w, XEvent * event, char **args, int nArgs)
792 {
793 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, CW,
794 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
795 			  (int) (event->xkey.state & ControlMask));
796 }
797 
798 static void
MovePyraminxRight(PyraminxWidget w,XEvent * event,char ** args,int nArgs)799 MovePyraminxRight(PyraminxWidget w, XEvent * event, char **args, int nArgs)
800 {
801 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, RIGHT,
802 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
803 			  (int) (event->xkey.state & ControlMask));
804 }
805 
806 static void
MovePyraminxBl(PyraminxWidget w,XEvent * event,char ** args,int nArgs)807 MovePyraminxBl(PyraminxWidget w, XEvent * event, char **args, int nArgs)
808 {
809 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, BL,
810 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
811 			  (int) (event->xkey.state & ControlMask));
812 }
813 
814 static void
MovePyraminxBottom(PyraminxWidget w,XEvent * event,char ** args,int nArgs)815 MovePyraminxBottom(PyraminxWidget w, XEvent * event, char **args, int nArgs)
816 {
817 	MovePyraminxInput(w, event->xbutton.x, event->xbutton.y, BOTTOM,
818 			  (int) (event->xkey.state & (ShiftMask | LockMask)),
819 			  (int) (event->xkey.state & ControlMask));
820 }
821 
822 static void
MovePyraminxInput(PyraminxWidget w,int x,int y,int direction,int shift,int control)823 MovePyraminxInput(PyraminxWidget w, int x, int y, int direction, int shift, int control)
824 {
825 	int         style, face;
826 	CRD         crd;
827 
828 	if (w->pyraminx.mode != BOTH) {
829 		if (control && shift)
830 			style = (w->pyraminx.mode == PERIOD3) ? PERIOD2 : PERIOD3;
831 		else
832 			style = (w->pyraminx.mode == PERIOD2) ? PERIOD2 : PERIOD3;
833 	} else
834 		style = (shift) ? PERIOD3 : PERIOD2;
835 	if (!w->pyraminx.practice && !control && CheckSolved(w)) {
836 		MoveNoPolyhedrons(w);
837 		return;
838 	}
839 	if (!PositionPolyhedrons(w, x, y, style, &face, &crd, &direction))
840 		return;
841 	control = (control) ? 1 : 0;
842 	MovePyraminx(w, face, ToPosition(w, crd), direction, style, control);
843 	if (!control && CheckSolved(w)) {
844 		pyraminxCallbackStruct cb;
845 
846 		cb.reason = PYRAMINX_SOLVED;
847 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
848 	}
849 }
850 
851 void
MovePyraminx(PyraminxWidget w,int face,int position,int direction,int style,int control)852 MovePyraminx(PyraminxWidget w, int face, int position, int direction, int style, int control)
853 {
854 	if (control)
855 		MoveControlCb(w, face, direction, style);
856 	else {
857 		pyraminxCallbackStruct cb;
858 
859 		MovePolyhedrons(w, face, ToCRD(w, face, position), direction, style);
860 		cb.reason = PYRAMINX_MOVED;
861 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
862 	}
863 	PutMove(face, position, direction, style, control);
864 }
865 
866 static void
SetAllColors(PyraminxWidget w,Boolean init)867 SetAllColors(PyraminxWidget w, Boolean init)
868 {
869 	XGCValues   values;
870 	XtGCMask    valueMask;
871 	int         face;
872 
873 	valueMask = GCForeground | GCBackground;
874 
875 	if (w->pyraminx.reverse) {
876 		values.background = w->core.background_pixel;
877 		values.foreground = w->pyraminx.foreground;
878 	} else {
879 		values.foreground = w->core.background_pixel;
880 		values.background = w->pyraminx.foreground;
881 	}
882 	if (!init)
883 		XtReleaseGC((Widget) w, w->pyraminx.inverseGC);
884 	w->pyraminx.inverseGC = XtGetGC((Widget) w, valueMask, &values);
885 	if (w->pyraminx.reverse) {
886 		values.background = w->pyraminx.foreground;
887 		values.foreground = w->core.background_pixel;
888 	} else {
889 		values.foreground = w->pyraminx.foreground;
890 		values.background = w->core.background_pixel;
891 	}
892 	if (!init)
893 		XtReleaseGC((Widget) w, w->pyraminx.puzzleGC);
894 	w->pyraminx.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
895 	if (w->pyraminx.depth < 2 || w->pyraminx.mono) {
896 		if (w->pyraminx.reverse) {
897 			values.background = w->pyraminx.foreground;
898 			values.foreground = w->core.background_pixel;
899 		} else {
900 			values.foreground = w->pyraminx.foreground;
901 			values.background = w->core.background_pixel;
902 		}
903 	} else {
904 		values.foreground = w->pyraminx.borderColor;
905 		values.background = w->core.background_pixel;
906 	}
907 	if (!init)
908 		XtReleaseGC((Widget) w, w->pyraminx.borderGC);
909 	w->pyraminx.borderGC = XtGetGC((Widget) w, valueMask, &values);
910 	for (face = 0; face < MAXFACES; face++)
911 		GetColor(w, face, init);
912 }
913 
914 static void
GetColor(PyraminxWidget w,int face,Boolean init)915 GetColor(PyraminxWidget w, int face, Boolean init)
916 {
917 	XGCValues   values;
918 	XtGCMask    valueMask;
919 	XColor      colorCell, rgb;
920 
921 	valueMask = GCForeground | GCBackground;
922 	if (w->pyraminx.reverse) {
923 		values.background = w->pyraminx.foreground;
924 	} else {
925 		values.background = w->core.background_pixel;
926 	}
927 	if (w->pyraminx.depth > 1 && !w->pyraminx.mono) {
928 		if (XAllocNamedColor(XtDisplay(w),
929 				  DefaultColormap(XtDisplay(w), XtWindow(w)),
930 			     w->pyraminx.faceName[face], &colorCell, &rgb)) {
931 			values.foreground = w->pyraminx.faceColor[face] = colorCell.pixel;
932 			if (!init)
933 				XtReleaseGC((Widget) w, w->pyraminx.faceGC[face]);
934 			w->pyraminx.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
935 			return;
936 		} else {
937 			char        buf[121];
938 
939 			(void) sprintf(buf, "Color name \"%s\" is not defined",
940 				       w->pyraminx.faceName[face]);
941 			XtWarning(buf);
942 		}
943 	}
944 	if (w->pyraminx.reverse) {
945 		values.background = w->pyraminx.foreground;
946 		values.foreground = w->core.background_pixel;
947 	} else {
948 		values.background = w->core.background_pixel;
949 		values.foreground = w->pyraminx.foreground;
950 	}
951 	if (!init)
952 		XtReleaseGC((Widget) w, w->pyraminx.faceGC[face]);
953 	w->pyraminx.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
954 }
955 
956 static void
MoveControlCb(PyraminxWidget w,int face,int direction,int style)957 MoveControlCb(PyraminxWidget w, int face, int direction, int style)
958 {
959 	pyraminxCallbackStruct cb;
960 	int         i, faceOnView;
961 	CRD         crd;
962 
963 	faceOnView = face % MAXSIDES;
964 	if (w->pyraminx.sticky) {
965 		if (style == PERIOD2)
966 			for (i = 0; i < 3; i++) {
967 				if (direction == TR || direction == BL) {
968 					crd.column = 0;
969 					crd.row = 3 * i / 2;
970 					crd.diagonal = crd.row + i % 2;
971 					MovePolyhedrons(w, face, crd, direction, style);
972 				} else {
973 					crd.column = 3 * i / 2;
974 					crd.row = 3 * i / 2;
975 					crd.diagonal = crd.row + crd.column;
976 					MovePolyhedrons(w, face, crd, direction, style);
977 				}
978 				cb.reason = PYRAMINX_CONTROL;
979 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
980 		} else		/* (style == PERIOD3) */
981 			for (i = 0; i < 2; i++) {
982 				if (direction == TR || direction == BL) {
983 					crd.column = 1 + faceOnView;
984 					crd.row = 1 + faceOnView;
985 					crd.diagonal = crd.row + crd.column + i;
986 					MovePolyhedrons(w, face, crd, direction, style);
987 				} else {
988 					crd.column = i + 2 * faceOnView;
989 					crd.row = i + 2 * faceOnView;
990 					crd.diagonal = crd.row + crd.column + faceOnView;
991 					MovePolyhedrons(w, face, crd, direction, style);
992 				}
993 				cb.reason = PYRAMINX_CONTROL;
994 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
995 			}
996 	} else {
997 		for (i = 0; i < w->pyraminx.size; i++) {
998 			if (direction == TR || direction == BL) {
999 				if (style == PERIOD2) {
1000 					crd.column = 0;
1001 					crd.row = i;
1002 					crd.diagonal = crd.row;
1003 					MovePolyhedrons(w, face, crd, direction, style);
1004 				} else {
1005 					crd.column = faceOnView * (w->pyraminx.size - 1);
1006 					crd.row = i;
1007 					crd.diagonal = crd.column + crd.row + faceOnView;
1008 					MovePolyhedrons(w, face, crd, direction, style);
1009 				}
1010 			} else {
1011 				crd.column = i;
1012 				crd.row = w->pyraminx.size - 1 - i;
1013 				crd.diagonal = crd.column + crd.row + faceOnView;
1014 				MovePolyhedrons(w, face, crd, direction, style);
1015 			}
1016 			cb.reason = PYRAMINX_CONTROL;
1017 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1018 		}
1019 	}
1020 }
1021 
1022 static void
CheckPolyhedrons(PyraminxWidget w)1023 CheckPolyhedrons(PyraminxWidget w)
1024 {
1025 	if (w->pyraminx.size < MINTETRAS) {
1026 		char        buf[121];
1027 
1028 		(void) sprintf(buf,
1029 		    "Number of Tetras on edge out of bounds, use %d..MAXINT",
1030 			       MINTETRAS);
1031 		XtWarning(buf);
1032 		w->pyraminx.size = DEFAULTTETRAS;
1033 	}
1034 	if (w->pyraminx.mode < PERIOD2 || w->pyraminx.mode > BOTH) {
1035 		XtWarning("Mode is in error, use 2 for Period2, 3 for Period3, 4 for Both");
1036 		w->pyraminx.mode = DEFAULTMODE;
1037 	}
1038 }
1039 
1040 static void
ResetPolyhedrons(PyraminxWidget w)1041 ResetPolyhedrons(PyraminxWidget w)
1042 {
1043 	int         face, position, orient, side;
1044 
1045 	w->pyraminx.sizeSize = w->pyraminx.size * w->pyraminx.size;
1046 	for (face = 0; face < MAXFACES; face++) {
1047 		if (w->pyraminx.tetraLoc[face])
1048 			(void) free((void *) w->pyraminx.tetraLoc[face]);
1049 		if (!(w->pyraminx.tetraLoc[face] = (PyraminxLoc *)
1050 		      malloc(sizeof (PyraminxLoc) * w->pyraminx.sizeSize)))
1051 			XtError("Not enough memory, exiting.");
1052 		if (startLoc[face])
1053 			(void) free((void *) startLoc[face]);
1054 		if (!(startLoc[face] = (PyraminxLoc *)
1055 		      malloc(sizeof (PyraminxLoc) * w->pyraminx.sizeSize)))
1056 			XtError("Not enough memory, exiting.");
1057 	}
1058 	for (orient = 0; orient < 3; orient++)
1059 		for (side = 0; side < MAXSIDES; side++) {
1060 			if (w->pyraminx.rowLoc[orient][side])
1061 				(void) free((void *) w->pyraminx.rowLoc[orient][side]);
1062 			if (!(w->pyraminx.rowLoc[orient][side] = (PyraminxLoc *)
1063 			    malloc(sizeof (PyraminxLoc) * w->pyraminx.size)))
1064 				XtError("Not enough memory, exiting.");
1065 		}
1066 	for (side = 0; side < MAXSIDES; side++) {
1067 		if (w->pyraminx.faceLoc)
1068 			(void) free((void *) w->pyraminx.faceLoc[side]);
1069 		if (!(w->pyraminx.faceLoc[side] = (PyraminxLoc *)
1070 		      malloc(sizeof (PyraminxLoc) * w->pyraminx.sizeSize)))
1071 			XtError("Not enough memory, exiting.");
1072 	}
1073 	for (face = 0; face < MAXFACES; face++)
1074 		for (position = 0; position < w->pyraminx.sizeSize; position++) {
1075 			w->pyraminx.tetraLoc[face][position].face = face;
1076 			w->pyraminx.tetraLoc[face][position].rotation = TOP;
1077 		}
1078 	FlushMoves(w);
1079 	w->pyraminx.started = False;
1080 }
1081 
1082 static void
ResizePolyhedrons(PyraminxWidget w)1083 ResizePolyhedrons(PyraminxWidget w)
1084 {
1085 	int         i, j;
1086 
1087 	w->pyraminx.tetraLength = w->pyraminx.faceLength / w->pyraminx.size -
1088 		w->pyraminx.delta - 1;
1089 	for (i = 0; i <= 3; i++)
1090 		for (j = 0; j < MAXSIDES; j++) {
1091 			triangleList[j][i].x = triangleUnit[j][i].x *
1092 				w->pyraminx.tetraLength;
1093 			triangleList[j][i].y = triangleUnit[j][i].y *
1094 				w->pyraminx.tetraLength;
1095 		}
1096 	offsetList[DOWN].x = 0;
1097 	offsetList[UP].x = w->pyraminx.tetraLength + 2;
1098 	offsetList[DOWN].y = 0;
1099 	offsetList[UP].y = w->pyraminx.tetraLength + 2;
1100 	letterList[DOWN].x = w->pyraminx.tetraLength / 4 - 3;
1101 	letterList[UP].x = 3 * w->pyraminx.tetraLength / 4;
1102 	letterList[DOWN].y = w->pyraminx.tetraLength / 4 + 5;
1103 	letterList[UP].y = 3 * w->pyraminx.tetraLength / 4 + 5;
1104 	w->pyraminx.sideOffset = 3 * w->pyraminx.size / 4;
1105 	w->pyraminx.orientLineLength = w->pyraminx.tetraLength / 4;
1106 	w->pyraminx.orientDiagLength = MAX(w->pyraminx.orientLineLength - 3, 0);
1107 }
1108 
1109 static      Boolean
SelectPolyhedrons(PyraminxWidget w,int x,int y,int * face,CRD * crd)1110 SelectPolyhedrons(PyraminxWidget w, int x, int y, int *face, CRD * crd)
1111 {
1112 	int         offset, modI, modJ, side, view;
1113 
1114 	x -= w->pyraminx.puzzleOffset.x;
1115 	y -= w->pyraminx.puzzleOffset.y;
1116 	if (w->pyraminx.vertical && y > w->pyraminx.viewLength - 1) {
1117 		y -= (w->pyraminx.viewLength - 1);
1118 		view = DOWN;
1119 	} else if (!w->pyraminx.vertical && x > w->pyraminx.viewLength - 1) {
1120 		x -= (w->pyraminx.viewLength - 1);
1121 		view = DOWN;
1122 	} else
1123 		view = UP;
1124 	if (x <= 0 || y <= 0 ||
1125 	    x >= w->pyraminx.faceLength + w->pyraminx.delta ||
1126 	    y >= w->pyraminx.faceLength + w->pyraminx.delta)
1127 		return False;
1128 	else if (x + y > w->pyraminx.faceLength)
1129 		offset = 2 * w->pyraminx.delta + 1;
1130 	else
1131 		offset = w->pyraminx.delta;
1132 	crd->column = (x - offset) / (w->pyraminx.tetraLength + w->pyraminx.delta);
1133 	crd->row = (y - offset) / (w->pyraminx.tetraLength + w->pyraminx.delta);
1134 	modI = (x - offset) % (w->pyraminx.tetraLength + w->pyraminx.delta);
1135 	modJ = (y - offset) % (w->pyraminx.tetraLength + w->pyraminx.delta);
1136 	side = (modI + modJ > w->pyraminx.tetraLength + 1);
1137 	if (!w->pyraminx.vertical && view == DOWN) {
1138 		crd->row = w->pyraminx.size - crd->row - 1;
1139 		crd->column = w->pyraminx.size - crd->column - 1;
1140 		side = !side;
1141 	}
1142 	crd->diagonal = crd->row + crd->column + side;
1143 	*face = view * MAXSIDES + (crd->diagonal >= w->pyraminx.size);
1144 	return True;
1145 }
1146 
1147 static void
NarrowSelection(PyraminxWidget w,int style,int * face,CRD * crd,int * direction)1148 NarrowSelection(PyraminxWidget w, int style, int *face, CRD * crd, int *direction)
1149 {
1150 	if (!w->pyraminx.vertical && *face >= MAXSIDES && *direction < MAXORIENT)
1151 		*direction = (*direction + MAXORIENT / 2) % MAXORIENT;
1152 	if (style == PERIOD2) {
1153 		if (*direction == CW)
1154 			*direction = TR;
1155 		else if (*direction == CCW)
1156 			*direction = BL;
1157 	} else {		/* style == PERIOD3 */
1158 		if (*direction == CW || *direction == CCW) {
1159 			crd->diagonal = w->pyraminx.size - (crd->diagonal < w->pyraminx.size);
1160 			*direction = ((*direction == CW && crd->diagonal == w->pyraminx.size) ||
1161 				      (*direction == CCW && crd->diagonal != w->pyraminx.size))
1162 				? TR : BL;
1163 			*face = !(*face % 2) + 2 * (*face / 2);
1164 			crd->row = w->pyraminx.size - 1;
1165 			crd->column = 0;
1166 		}
1167 	}
1168 }
1169 
1170 static      Boolean
PositionPolyhedrons(PyraminxWidget w,int x,int y,int style,int * face,CRD * crd,int * direction)1171 PositionPolyhedrons(PyraminxWidget w, int x, int y, int style, int *face, CRD * crd, int *direction)
1172 {
1173 	if (!SelectPolyhedrons(w, x, y, face, crd))
1174 		return False;
1175 	NarrowSelection(w, style, face, crd, direction);
1176 	return True;
1177 }
1178 
1179 static void
MoveNoPolyhedrons(PyraminxWidget w)1180 MoveNoPolyhedrons(PyraminxWidget w)
1181 {
1182 	pyraminxCallbackStruct cb;
1183 
1184 	cb.reason = PYRAMINX_ILLEGAL;
1185 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1186 }
1187 
1188 static void
PracticePolyhedrons(PyraminxWidget w)1189 PracticePolyhedrons(PyraminxWidget w)
1190 {
1191 	pyraminxCallbackStruct cb;
1192 
1193 	cb.reason = PYRAMINX_PRACTICE;
1194 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1195 }
1196 
1197 static void
RandomizePolyhedrons(PyraminxWidget w)1198 RandomizePolyhedrons(PyraminxWidget w)
1199 {
1200 	pyraminxCallbackStruct cb;
1201 	int         randomDirection, face, position, style;
1202 	int         big = w->pyraminx.sizeSize * 3 + NRAND(2);
1203 
1204 	if (big > 1000)
1205 		big = 1000;
1206 	if (w->pyraminx.practice)
1207 		PracticePolyhedrons(w);
1208 	if (w->pyraminx.sticky)
1209 		big /= 3;
1210 	cb.reason = PYRAMINX_RESET;
1211 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1212 
1213 #ifdef DEBUG
1214 	big = 3;
1215 #endif
1216 
1217 	while (big--) {
1218 		face = NRAND(MAXFACES);
1219 		if (w->pyraminx.mode == BOTH)
1220 			style = NRAND(MAXMODES - 1) + PERIOD2;
1221 		else
1222 			style = w->pyraminx.mode;
1223 		if (w->pyraminx.sticky) {
1224 			if (style == PERIOD2) {
1225 				if (NRAND(3) == 2) {
1226 					position = (NRAND(2)) ? 9 : 6;
1227 					randomDirection = (NRAND(2)) ? TR : BL;
1228 				} else {
1229 					position = (NRAND(2)) ? 6 : 0;
1230 					if (NRAND(2))
1231 						randomDirection = (NRAND(2)) ? LEFT : RIGHT;
1232 					else
1233 						randomDirection = (NRAND(2)) ? TOP : BOTTOM;
1234 				}
1235 			} else {	/* style == PERIOD3 */
1236 				position = 6;
1237 				randomDirection = NRAND(6);
1238 			}
1239 		} else {	/* (!w->pyraminx.sticky) */
1240 			randomDirection = NRAND(MAXORIENT);
1241 			position = NRAND(w->pyraminx.sizeSize);
1242 			if (w->pyraminx.mode == BOTH)
1243 				style = NRAND(BOTH);
1244 			else
1245 				style = w->pyraminx.mode;
1246 		}
1247 		MovePyraminx(w, face, position, randomDirection, style, FALSE);
1248 		cb.reason = PYRAMINX_MOVED;
1249 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1250 	}
1251 	FlushMoves(w);
1252 	cb.reason = PYRAMINX_RANDOMIZE;
1253 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1254 	if (CheckSolved(w)) {
1255 		cb.reason = PYRAMINX_SOLVED;
1256 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1257 	}
1258 }
1259 
1260 static void
MovePolyhedrons(PyraminxWidget w,int face,CRD crd,int direction,int style)1261 MovePolyhedrons(PyraminxWidget w, int face, CRD crd, int direction, int style)
1262 {
1263 	int         view, side;
1264 	int         newSide, newView, rotate, reverse, h, k, newH, faceOnView,
1265 	            len;
1266 	int         newFace, newDirection, bound, l = 0;
1267 
1268 	view = face / MAXSIDES;
1269 	side = crd.diagonal - crd.column - crd.row;
1270 	if (style == PERIOD2) {
1271 		/* Period 2 Slide rows */
1272 		h = Crd(direction, crd.column, crd.row, side);
1273 		if (w->pyraminx.sticky && (h % 4 == 1 || h % 4 == 2)) {
1274 			bound = TRUE;
1275 			l = 0;
1276 			if (h % 4 == 2)
1277 				h = h - 1;
1278 		} else
1279 			bound = FALSE;
1280 		do {
1281 			ReadCRD2(w, view, direction, h, 0);
1282 			for (k = 1; k <= 2; k++) {
1283 				rotate = slideNextRowP2[side][direction % 3].rotation;
1284 				if (direction == TR || direction == BL) {
1285 					newView = view;
1286 					newSide = !side;
1287 					newH = 2 * w->pyraminx.size - 1 - h;
1288 					reverse = FALSE;
1289 				} else if (!rotate) {
1290 					newView = !view;
1291 					newSide = side;
1292 					newH = h;
1293 					reverse = FALSE;
1294 				} else {	/* rotate == 3 */
1295 					newView = !view;
1296 					newSide = !side;
1297 					newH = w->pyraminx.size - 1 - h;
1298 					reverse = TRUE;
1299 				}
1300 				if (k != 2)
1301 					ReadCRD2(w, newView, direction, newH, k);
1302 				RotateCRD2(w, rotate, k - 1);
1303 				if (reverse == TRUE)
1304 					ReverseCRD2(w, k - 1);
1305 				WriteCRD2(w, newView, direction, newH, k - 1);
1306 				view = newView;
1307 				h = newH;
1308 				side = newSide;
1309 			}
1310 			l++;
1311 			h++;
1312 		} while (bound && l < 2);
1313 	} else {		/* style == PERIOD3 */
1314 		faceOnView = (crd.diagonal >= w->pyraminx.size);
1315 		h = Crd(direction, crd.column, crd.row, side);
1316 		bound = FALSE;
1317 		if (direction == TR || direction == BL) {
1318 			if (h == w->pyraminx.size)
1319 				RotateFace(w, view, DOWN, (direction == TR) ? CCW : CW);
1320 			else if (h == w->pyraminx.size - 1)
1321 				RotateFace(w, view, UP, (direction == TR) ? CW : CCW);
1322 			else if (w->pyraminx.sticky)
1323 				bound = TRUE;
1324 		} else if (!crd.column && !faceOnView &&
1325 			   (direction == TOP || direction == BOTTOM))
1326 			RotateFace(w, !view, DOWN,
1327 			 (direction == TOP || direction == LEFT) ? CCW : CW);
1328 		else if (crd.column == w->pyraminx.size - 1 && faceOnView &&
1329 			 (direction == TOP || direction == BOTTOM))
1330 			RotateFace(w, !view, UP,
1331 				   (direction == BOTTOM || direction == RIGHT) ? CCW : CW);
1332 		else if (!crd.row && !faceOnView &&
1333 			 (direction == RIGHT || direction == LEFT))
1334 			RotateFace(w, !view, UP,
1335 				   (direction == BOTTOM || direction == RIGHT) ? CCW : CW);
1336 		else if (crd.row == w->pyraminx.size - 1 && faceOnView &&
1337 			 (direction == RIGHT || direction == LEFT))
1338 			RotateFace(w, !view, DOWN,
1339 			 (direction == TOP || direction == LEFT) ? CCW : CW);
1340 		else if (w->pyraminx.sticky)
1341 			bound = TRUE;
1342 		/* Slide rows */
1343 		if (bound == TRUE) {
1344 			l = 0;
1345 			if (direction == TR || direction == BL)
1346 				h = (faceOnView == UP) ? 0 : w->pyraminx.size + 1;
1347 			else
1348 				h = (faceOnView == UP) ? 1 : 0;
1349 		}
1350 		do {
1351 			len = Length(w, faceOnView, direction, h);
1352 			ReadCRD3(w, view, faceOnView, direction, h, len, 0);
1353 			for (k = 1; k <= 3; k++) {
1354 				newView = (slideNextRowP3[faceOnView][direction].viewChanged)
1355 					? !view : view;
1356 				newFace = !slideNextRowP3[faceOnView][direction].face;
1357 				newDirection = slideNextRowP3[faceOnView][direction].direction;
1358 				reverse = slideNextRowP3[faceOnView][direction].reverse;
1359 				newH = w->pyraminx.size - 1 - h;
1360 				if (!faceOnView) {
1361 					if (direction == TOP)
1362 						newH = w->pyraminx.size + h;
1363 					else if (direction == TR)
1364 						newH = h;
1365 					else
1366 						newH = w->pyraminx.size - 1 - h;
1367 				} else {
1368 					if (direction == TR || direction == RIGHT)
1369 						newH = 2 * w->pyraminx.size - 1 - h;
1370 					else if (direction == BOTTOM)
1371 						newH = h;
1372 					else if (direction == BL)
1373 						newH = h - w->pyraminx.size;
1374 					else
1375 						newH = w->pyraminx.size - 1 - h;
1376 				}
1377 				if (k != 3)
1378 					ReadCRD3(w, newView, newFace, newDirection, newH, len, k);
1379 				RotateCRD3(w, faceOnView, direction, len, k - 1);
1380 				if (reverse == TRUE)
1381 					ReverseCRD3(w, len, k - 1);
1382 				WriteCRD3(w, newView, newFace, newDirection, newH, len, k - 1);
1383 				view = newView;
1384 				faceOnView = newFace;
1385 				direction = newDirection;
1386 				h = newH;
1387 			}
1388 			h++;
1389 			l++;
1390 		} while (bound && l < w->pyraminx.size - 1);
1391 	}
1392 }
1393 
1394 static void
RotateFace(PyraminxWidget w,int view,int faceOnView,int direction)1395 RotateFace(PyraminxWidget w, int view, int faceOnView, int direction)
1396 {
1397 	int         g, h, side, face, position;
1398 	CRD         crd;
1399 
1400 	/* Read Face */
1401 	for (g = 0; g < w->pyraminx.size; g++)
1402 		for (h = 0; h < w->pyraminx.size - g; h++)
1403 			for (side = 0; side < MAXSIDES; side++)
1404 				if (g + h + side < w->pyraminx.size) {
1405 					if (faceOnView == DOWN) {
1406 						crd.column = h;
1407 						crd.row = g;
1408 						crd.diagonal = h + g + side;
1409 						face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1410 						position = ToPosition(w, crd);
1411 						w->pyraminx.faceLoc[side][h + g * w->pyraminx.size] =
1412 							w->pyraminx.tetraLoc[face][position];
1413 					} else {	/* faceOnView == UP */
1414 						crd.column = w->pyraminx.size - 1 - h;
1415 						crd.row = w->pyraminx.size - 1 - g;
1416 						crd.diagonal = crd.column + crd.row + !side;
1417 						face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1418 						position = ToPosition(w, crd);
1419 						w->pyraminx.faceLoc[side][h + g * w->pyraminx.size] =
1420 							w->pyraminx.tetraLoc[face][position];
1421 					}
1422 				}
1423 	/* Write Face */
1424 	if (faceOnView == DOWN) {
1425 		for (g = 0; g < w->pyraminx.size; g++)
1426 			for (h = 0; h < w->pyraminx.size - g; h++)
1427 				for (side = 0; side < MAXSIDES; side++)
1428 					if (g + h + side < w->pyraminx.size) {
1429 						crd.column = h;
1430 						crd.row = g;
1431 						crd.diagonal = h + g + side;
1432 						face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1433 						position = ToPosition(w, crd);
1434 						if (direction == CCW)
1435 							w->pyraminx.tetraLoc[face][position] =
1436 								w->pyraminx.faceLoc[side][w->pyraminx.size - 1 - g - h - side +
1437 							h * w->pyraminx.size];
1438 						else	/* direction == CW */
1439 							w->pyraminx.tetraLoc[face][position] =
1440 								w->pyraminx.faceLoc[side][g +
1441 											  (w->pyraminx.size - 1 - g - h - side) * w->pyraminx.size];
1442 						w->pyraminx.tetraLoc[face][position].rotation =
1443 							(direction == CW) ?
1444 							(w->pyraminx.tetraLoc[face][position].rotation + 2) %
1445 							MAXORIENT :
1446 							(w->pyraminx.tetraLoc[face][position].rotation + 4) %
1447 							MAXORIENT;
1448 						DrawTriangle(w, face, position, FALSE);
1449 					}
1450 	} else {		/* faceOnView == UP */
1451 		for (g = w->pyraminx.size - 1; g >= 0; g--)
1452 			for (h = w->pyraminx.size - 1; h >= w->pyraminx.size - 1 - g; h--)
1453 				for (side = 1; side >= 0; side--)
1454 					if (g + h + side >= w->pyraminx.size) {
1455 						crd.column = h;
1456 						crd.row = g;
1457 						crd.diagonal = h + g + side;
1458 						face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1459 						position = ToPosition(w, crd);
1460 						if (direction == CCW)
1461 							w->pyraminx.tetraLoc[face][position] =
1462 								w->pyraminx.faceLoc[!side][g + h - w->pyraminx.size + 1 - !side
1463 											   + (w->pyraminx.size - 1 - h) * w->pyraminx.size];
1464 						else	/* (direction == CW) */
1465 							w->pyraminx.tetraLoc[face][position] =
1466 								w->pyraminx.faceLoc[!side][w->pyraminx.size - 1 - g +
1467 											   (g + h - w->pyraminx.size + 1 - !side) * w->pyraminx.size];
1468 						w->pyraminx.tetraLoc[face][position].rotation =
1469 							(direction == CW) ?
1470 							(w->pyraminx.tetraLoc[face][position].rotation + 2) %
1471 							MAXORIENT :
1472 							(w->pyraminx.tetraLoc[face][position].rotation + 4) %
1473 							MAXORIENT;
1474 						DrawTriangle(w, face, position, FALSE);
1475 					}
1476 	}
1477 }
1478 
1479 static void
ReadCRD2(PyraminxWidget w,int view,int dir,int h,int orient)1480 ReadCRD2(PyraminxWidget w, int view, int dir, int h, int orient)
1481 {
1482 	int         g, i, j, side, faceOnView, s, face, position;
1483 	CRD         crd;
1484 
1485 	if (dir == TOP || dir == BOTTOM)
1486 		for (g = 0; g < w->pyraminx.size; g++)
1487 			for (side = 0; side < MAXSIDES; side++) {
1488 				crd.column = h;
1489 				crd.row = g;
1490 				crd.diagonal = h + g + side;
1491 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1492 				position = ToPosition(w, crd);
1493 				w->pyraminx.rowLoc[orient][side][g] =
1494 					w->pyraminx.tetraLoc[face][position];
1495 	} else if (dir == RIGHT || dir == LEFT)
1496 		for (g = 0; g < w->pyraminx.size; g++)
1497 			for (side = 0; side < MAXSIDES; side++) {
1498 				crd.column = g;
1499 				crd.row = h;
1500 				crd.diagonal = h + g + side;
1501 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1502 				position = ToPosition(w, crd);
1503 				w->pyraminx.rowLoc[orient][side][g] =
1504 					w->pyraminx.tetraLoc[face][position];
1505 	} else {		/* dir == TR || dir == BL */
1506 		faceOnView = (h < w->pyraminx.size);
1507 		i = (faceOnView == UP) ? w->pyraminx.size - 1 : 0;
1508 		j = h % w->pyraminx.size;
1509 		for (g = 0; g < w->pyraminx.size; g++) {
1510 			for (side = 0; side < MAXSIDES; side++) {
1511 				s = (side == UP) ? !faceOnView : faceOnView;
1512 				crd.column = i;
1513 				crd.row = j;
1514 				crd.diagonal = i + j + s;
1515 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1516 				position = ToPosition(w, crd);
1517 				w->pyraminx.rowLoc[orient][side][g] =
1518 					w->pyraminx.tetraLoc[face][position];
1519 				if (!side) {
1520 					if (faceOnView == UP) {
1521 						if (j == w->pyraminx.size - 1)
1522 							view = !view;
1523 						j = (j + 1) % w->pyraminx.size;
1524 					} else {	/* faceOnView == DOWN */
1525 						if (!j)
1526 							view = !view;
1527 						j = (j - 1 + w->pyraminx.size) % w->pyraminx.size;
1528 					}
1529 				}
1530 			}
1531 			i = (faceOnView == UP) ? i - 1 : i + 1;
1532 		}
1533 	}
1534 }
1535 
1536 static void
ReadCRD3(PyraminxWidget w,int view,int faceOnView,int dir,int h,int len,int orient)1537 ReadCRD3(PyraminxWidget w, int view, int faceOnView, int dir, int h, int len, int orient)
1538 {
1539 	int         g, i, j, side, s, face, position;
1540 	CRD         crd;
1541 
1542 	if (dir == TOP || dir == BOTTOM) {
1543 		for (g = 0; g <= len; g++)
1544 			for (side = 0; side < MAXSIDES; side++)
1545 				if (!side || g < len) {
1546 					crd.column = h;
1547 					crd.row = (faceOnView == UP) ? g : w->pyraminx.size - 1 - g;
1548 					crd.diagonal = h + crd.row + ((faceOnView == UP) ? side : !side);
1549 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1550 					position = ToPosition(w, crd);
1551 					w->pyraminx.rowLoc[orient][side][g] =
1552 						w->pyraminx.tetraLoc[face][position];
1553 				}
1554 	} else if (dir == RIGHT || dir == LEFT) {
1555 		for (g = 0; g <= len; g++)
1556 			for (side = 0; side < MAXSIDES; side++)
1557 				if (!side || g < len) {
1558 					crd.column = (faceOnView == UP) ? g : w->pyraminx.size - 1 - g;
1559 					crd.row = h;
1560 					crd.diagonal = h + crd.column + ((faceOnView == UP) ? side : !side);
1561 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1562 					position = ToPosition(w, crd);
1563 					w->pyraminx.rowLoc[orient][side][g] =
1564 						w->pyraminx.tetraLoc[face][position];
1565 				}
1566 	} else {		/* dir == TR || dir == BL */
1567 		i = (faceOnView == DOWN) ? w->pyraminx.size - 1 : 0;
1568 		j = h % w->pyraminx.size;
1569 		for (g = 0; g <= len; g++) {
1570 			for (side = 0; side < MAXSIDES; side++) {
1571 				if (!side || g < len) {
1572 					s = (side == UP) ? faceOnView : !faceOnView;
1573 					crd.column = i;
1574 					crd.row = j;
1575 					crd.diagonal = i + j + s;
1576 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1577 					position = ToPosition(w, crd);
1578 					w->pyraminx.rowLoc[orient][side][g] =
1579 						w->pyraminx.tetraLoc[face][position];
1580 					if (!side)
1581 						j = (faceOnView == DOWN) ? j + 1 : j - 1;
1582 				}
1583 			}
1584 			i = (faceOnView == DOWN) ? i - 1 : i + 1;
1585 		}
1586 	}
1587 }
1588 
1589 static void
RotateCRD2(PyraminxWidget w,int rotate,int orient)1590 RotateCRD2(PyraminxWidget w, int rotate, int orient)
1591 {
1592 	int         g, side;
1593 
1594 	for (g = 0; g < w->pyraminx.size; g++)
1595 		for (side = 0; side < MAXSIDES; side++)
1596 			w->pyraminx.rowLoc[orient][side][g].rotation =
1597 				(w->pyraminx.rowLoc[orient][side][g].rotation + rotate) % MAXORIENT;
1598 }
1599 
1600 static void
RotateCRD3(PyraminxWidget w,int faceOnView,int dir,int len,int orient)1601 RotateCRD3(PyraminxWidget w, int faceOnView, int dir, int len, int orient)
1602 {
1603 	int         g, side, direction, tetraOrient;
1604 
1605 	for (g = 0; g <= len; g++)
1606 		for (side = 0; side < MAXSIDES; side++)
1607 			if (!side || g < len) {
1608 				direction = (faceOnView == DOWN) ? (dir + 3) % MAXORIENT : dir;
1609 				tetraOrient = w->pyraminx.rowLoc[orient][side][g].rotation;
1610 				w->pyraminx.rowLoc[orient][side][g].rotation =
1611 					(tetraOrient >= 3) ?
1612 					(rotOrientRowP3[tetraOrient - 3][direction] + 3) % MAXORIENT :
1613 					rotOrientRowP3[tetraOrient][direction];
1614 			}
1615 }
1616 
1617 static void
ReverseCRD2(PyraminxWidget w,int orient)1618 ReverseCRD2(PyraminxWidget w, int orient)
1619 {
1620 	PyraminxLoc temp;
1621 	int         g;
1622 
1623 	for (g = 0; g < w->pyraminx.size; g++) {
1624 		temp = w->pyraminx.rowLoc[orient][g % 2][g / 2];
1625 		w->pyraminx.rowLoc[orient][g % 2][g / 2] =
1626 			w->pyraminx.rowLoc[orient][!(g % 2)][w->pyraminx.size - 1 - g / 2];
1627 		w->pyraminx.rowLoc[orient][!(g % 2)][w->pyraminx.size - 1 - g / 2] =
1628 			temp;
1629 	}
1630 }
1631 
1632 static void
ReverseCRD3(PyraminxWidget w,int len,int orient)1633 ReverseCRD3(PyraminxWidget w, int len, int orient)
1634 {
1635 	PyraminxLoc temp;
1636 	int         g;
1637 
1638 	for (g = 0; g < len; g++) {
1639 		temp = w->pyraminx.rowLoc[orient][g % 2][len - ((g + 1) / 2)];
1640 		w->pyraminx.rowLoc[orient][g % 2][len - ((g + 1) / 2)] =
1641 			w->pyraminx.rowLoc[orient][g % 2][g / 2];
1642 		w->pyraminx.rowLoc[orient][g % 2][g / 2] = temp;
1643 	}
1644 }
1645 
1646 static void
WriteCRD2(PyraminxWidget w,int view,int dir,int h,int orient)1647 WriteCRD2(PyraminxWidget w, int view, int dir, int h, int orient)
1648 {
1649 	int         g, side, i, j, s, faceOnView, face, position;
1650 	CRD         crd;
1651 
1652 	if (dir == TOP || dir == BOTTOM) {
1653 		for (g = 0; g < w->pyraminx.size; g++)
1654 			for (side = 0; side < MAXSIDES; side++) {
1655 				crd.column = h;
1656 				crd.row = g;
1657 				crd.diagonal = h + g + side;
1658 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1659 				position = ToPosition(w, crd);
1660 				w->pyraminx.tetraLoc[face][position] =
1661 					w->pyraminx.rowLoc[orient][side][g];
1662 				DrawTriangle(w,
1663 					     view * MAXSIDES + (crd.diagonal >= w->pyraminx.size),
1664 					     ToPosition(w, crd), FALSE);
1665 			}
1666 	} else if (dir == RIGHT || dir == LEFT) {
1667 		for (g = 0; g < w->pyraminx.size; g++)
1668 			for (side = 0; side < MAXSIDES; side++) {
1669 				crd.column = g;
1670 				crd.row = h;
1671 				crd.diagonal = h + g + side;
1672 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1673 				position = ToPosition(w, crd);
1674 				w->pyraminx.tetraLoc[face][position] =
1675 					w->pyraminx.rowLoc[orient][side][g];
1676 				DrawTriangle(w, face, position, FALSE);
1677 			}
1678 	} else {		/* dir == TR || dir == BL */
1679 		faceOnView = (h < w->pyraminx.size);
1680 		i = (faceOnView == UP) ? w->pyraminx.size - 1 : 0;
1681 		j = h % w->pyraminx.size;
1682 		for (g = 0; g < w->pyraminx.size; g++) {
1683 			for (side = 0; side < MAXSIDES; side++) {
1684 				s = (side == UP) ? !faceOnView : faceOnView;
1685 				crd.column = i;
1686 				crd.row = j;
1687 				crd.diagonal = i + j + s;
1688 				face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1689 				position = ToPosition(w, crd);
1690 				w->pyraminx.tetraLoc[face][position] =
1691 					w->pyraminx.rowLoc[orient][side][g];
1692 				DrawTriangle(w, face, position, FALSE);
1693 				if (!side) {
1694 					if (faceOnView == UP) {
1695 						if (j == w->pyraminx.size - 1) {
1696 							view = !view;
1697 							j = 0;
1698 						} else
1699 							++j;
1700 					} else {	/* FACE == DOWN */
1701 						if (!j) {
1702 							view = !view;
1703 							j = w->pyraminx.size - 1;
1704 						} else
1705 							--j;
1706 					}
1707 				}
1708 			}
1709 			if (faceOnView == UP)
1710 				--i;
1711 			else	/* faceOnView == DOWN */
1712 				++i;
1713 		}
1714 	}
1715 }
1716 
1717 static void
WriteCRD3(PyraminxWidget w,int view,int faceOnView,int dir,int h,int len,int orient)1718 WriteCRD3(PyraminxWidget w, int view, int faceOnView, int dir, int h, int len, int orient)
1719 {
1720 	int         g, side, i, j, k, s, face, position;
1721 	CRD         crd;
1722 
1723 	if (dir == TOP || dir == BOTTOM) {
1724 		for (k = 0; k <= len; k++)
1725 			for (side = 0; side < MAXSIDES; side++)
1726 				if (!side || k < len) {
1727 					g = (faceOnView == DOWN) ? w->pyraminx.size - 1 - k : k;
1728 					s = (faceOnView == DOWN) ? !side : side;
1729 					crd.column = h;
1730 					crd.row = g;
1731 					crd.diagonal = h + g + s;
1732 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1733 					position = ToPosition(w, crd);
1734 					w->pyraminx.tetraLoc[face][position] =
1735 						w->pyraminx.rowLoc[orient][side][k];
1736 					DrawTriangle(w, face, position, FALSE);
1737 				}
1738 	} else if (dir == RIGHT || dir == LEFT) {
1739 		for (k = 0; k <= len; k++)
1740 			for (side = 0; side < MAXSIDES; side++)
1741 				if (!side || k < len) {
1742 					g = (faceOnView == DOWN) ? w->pyraminx.size - 1 - k : k;
1743 					s = (faceOnView == DOWN) ? !side : side;
1744 					crd.column = g;
1745 					crd.row = h;
1746 					crd.diagonal = h + g + s;
1747 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1748 					position = ToPosition(w, crd);
1749 					w->pyraminx.tetraLoc[face][position] =
1750 						w->pyraminx.rowLoc[orient][side][k];
1751 					DrawTriangle(w, face, position, FALSE);
1752 				}
1753 	} else {		/* dir == TR || dir == BL */
1754 		faceOnView = (h < w->pyraminx.size);
1755 		i = (faceOnView == UP) ? w->pyraminx.size - 1 : 0;
1756 		j = h % w->pyraminx.size;
1757 		for (k = 0; k <= len; k++) {
1758 			for (side = 0; side < MAXSIDES; side++)
1759 				if (!side || k < len) {
1760 					s = (side == UP) ? !faceOnView : faceOnView;
1761 					crd.column = i;
1762 					crd.row = j;
1763 					crd.diagonal = i + j + s;
1764 					face = view * MAXSIDES + (crd.diagonal >= w->pyraminx.size);
1765 					position = ToPosition(w, crd);
1766 					w->pyraminx.tetraLoc[face][position] =
1767 						w->pyraminx.rowLoc[orient][side][k];
1768 					DrawTriangle(w, face, position, FALSE);
1769 					if (!side) {
1770 						if (faceOnView == UP) {
1771 							if (j == w->pyraminx.size - 1) {
1772 								view = !view;
1773 								j = 0;
1774 							} else
1775 								++j;
1776 						} else {	/* FACE == DOWN */
1777 							if (!j) {
1778 								view = !view;
1779 								j = w->pyraminx.size - 1;
1780 							} else
1781 								--j;
1782 						}
1783 					}
1784 				}
1785 			if (faceOnView == UP)
1786 				--i;
1787 			else	/* faceOnView == DOWN */
1788 				++i;
1789 		}
1790 	}
1791 }
1792 
1793 static void
DrawFrame(PyraminxWidget w,GC gc)1794 DrawFrame(PyraminxWidget w, GC gc)
1795 {
1796 	int         startx, starty, lengthx, lengthy, longlength;
1797 
1798 	startx = 1 + w->pyraminx.puzzleOffset.x;
1799 	starty = 1 + w->pyraminx.puzzleOffset.y;
1800 	lengthx = w->pyraminx.viewLength - w->pyraminx.delta +
1801 		w->pyraminx.puzzleOffset.x;
1802 	lengthy = w->pyraminx.viewLength - w->pyraminx.delta +
1803 		w->pyraminx.puzzleOffset.y;
1804 	XDrawLine(XtDisplay(w), XtWindow(w), gc, startx, starty, lengthx, starty);
1805 	XDrawLine(XtDisplay(w), XtWindow(w), gc, startx, starty, startx, lengthy);
1806 	XDrawLine(XtDisplay(w), XtWindow(w), gc, lengthx, starty, lengthx, lengthy);
1807 	XDrawLine(XtDisplay(w), XtWindow(w), gc, startx, lengthy, lengthx, lengthy);
1808 	XDrawLine(XtDisplay(w), XtWindow(w), gc, startx, lengthy, lengthx, starty);
1809 	if (w->pyraminx.vertical) {
1810 		longlength = 2 * w->pyraminx.viewLength - 2 * w->pyraminx.delta - 1 +
1811 			w->pyraminx.puzzleOffset.y;
1812 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1813 			  startx, lengthy, startx, longlength);
1814 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1815 			  lengthx, lengthy, lengthx, longlength);
1816 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1817 			  startx, longlength, lengthx, longlength);
1818 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1819 			  startx, longlength, lengthx, lengthy);
1820 	} else {
1821 		longlength = 2 * w->pyraminx.viewLength - 2 * w->pyraminx.delta - 1 +
1822 			w->pyraminx.puzzleOffset.x;
1823 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1824 			  lengthx, starty, longlength, starty);
1825 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1826 			  lengthx, lengthy, longlength, lengthy);
1827 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1828 			  longlength, starty, longlength, lengthy);
1829 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
1830 			  longlength, starty, lengthx, lengthy);
1831 	}
1832 }
1833 
1834 void
DrawAllPolyhedrons(PyraminxWidget w)1835 DrawAllPolyhedrons(PyraminxWidget w)
1836 {
1837 	int         face, position;
1838 
1839 	for (face = 0; face < MAXFACES; face++)
1840 		for (position = 0; position < w->pyraminx.sizeSize; position++)
1841 			DrawTriangle(w, face, position, FALSE);
1842 }
1843 
1844 
1845 static void
DrawTriangle(PyraminxWidget w,int face,int position,int offset)1846 DrawTriangle(PyraminxWidget w, int face, int position, int offset)
1847 {
1848 	GC          faceGC, borderGC;
1849 	int         dx, dy, faceOnView, orient, side, view;
1850 	CRD         crd;
1851 
1852 	crd = ToCRD(w, face, position);
1853 	view = face / MAXSIDES;
1854 	side = crd.diagonal - crd.column - crd.row;
1855 	if (!w->pyraminx.vertical && view == DOWN) {
1856 		dx = (w->pyraminx.size - crd.column - 1) *
1857 			(w->pyraminx.tetraLength + w->pyraminx.delta) + w->pyraminx.delta;
1858 		dy = (w->pyraminx.size - crd.row - 1) *
1859 			(w->pyraminx.tetraLength + w->pyraminx.delta) + w->pyraminx.delta;
1860 		faceOnView = !side;
1861 		orient = (w->pyraminx.tetraLoc[face][position].rotation +
1862 			  MAXORIENT / 2) % MAXORIENT;
1863 		if (2 * w->pyraminx.size - crd.column - crd.row - 2 + faceOnView >=
1864 		    w->pyraminx.size) {
1865 			dx += w->pyraminx.sideOffset;
1866 			dy += w->pyraminx.sideOffset;
1867 		}
1868 	} else {
1869 		dx = crd.column * (w->pyraminx.tetraLength + w->pyraminx.delta) +
1870 			w->pyraminx.delta;
1871 		dy = crd.row * (w->pyraminx.tetraLength + w->pyraminx.delta) +
1872 			w->pyraminx.delta;
1873 		faceOnView = side;
1874 		orient = w->pyraminx.tetraLoc[face][position].rotation;
1875 		if (crd.column + crd.row + faceOnView >= w->pyraminx.size) {
1876 			dx += w->pyraminx.sideOffset;
1877 			dy += w->pyraminx.sideOffset;
1878 		}
1879 	}
1880 	dx += w->pyraminx.puzzleOffset.x;
1881 	dy += w->pyraminx.puzzleOffset.y;
1882 	if (view == DOWN) {
1883 		if (w->pyraminx.vertical)
1884 			dy += w->pyraminx.viewLength - w->pyraminx.delta - 1;
1885 		else
1886 			dx += w->pyraminx.viewLength - w->pyraminx.delta - 1;
1887 	}
1888 	triangleList[faceOnView][0].x = offsetList[!faceOnView].x + dx;
1889 	triangleList[faceOnView][0].y = offsetList[!faceOnView].y + dy;
1890 	if (offset) {
1891 		borderGC = w->pyraminx.faceGC[w->pyraminx.tetraLoc[face][position].face];
1892 		if (w->pyraminx.depth < 2 || w->pyraminx.mono) {
1893 			faceGC = w->pyraminx.inverseGC;
1894 		} else {
1895 			faceGC = w->pyraminx.borderGC;
1896 		}
1897 	} else {
1898 		faceGC = w->pyraminx.faceGC[w->pyraminx.tetraLoc[face][position].face];
1899 		borderGC = w->pyraminx.borderGC;
1900 	}
1901 
1902 	XFillPolygon(XtDisplay(w), XtWindow(w), faceGC,
1903 		     triangleList[faceOnView], 3, Convex, CoordModePrevious);
1904 	XDrawLines(XtDisplay(w), XtWindow(w), borderGC,
1905 		   triangleList[faceOnView], 4, CoordModePrevious);
1906 	if (w->pyraminx.depth < 2 || w->pyraminx.mono) {
1907 		int         letterX, letterY;
1908 		char        buf[2];
1909 
1910 		(void) sprintf(buf, "%c",
1911 			       w->pyraminx.faceName[w->pyraminx.tetraLoc[face][position].face][0]);
1912 		letterX = dx + letterList[!faceOnView].x;
1913 		letterY = dy + letterList[!faceOnView].y;
1914 		if (offset) {
1915 			borderGC = w->pyraminx.borderGC;
1916 		} else {
1917 			borderGC = w->pyraminx.inverseGC;
1918 		}
1919 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1920 			    letterX, letterY, buf, 1);
1921 	}
1922 	if (w->pyraminx.orient)
1923 		DrawOrientLine(w, orient, dx, dy, !faceOnView, borderGC);
1924 }
1925 
1926 static void
DrawOrientLine(PyraminxWidget w,int orient,int dx,int dy,int side,GC borderGC)1927 DrawOrientLine(PyraminxWidget w, int orient, int dx, int dy, int side, GC borderGC)
1928 {
1929 	int         x1 = 0, x2 = 0, y1 = 0, y2 = 0;
1930 	int         temp1 = w->pyraminx.tetraLength / 2 + 1;
1931 	int         temp2 = w->pyraminx.tetraLength + 1;
1932 	int         fix = (w->pyraminx.size == 1) ? 1 : 0;
1933 
1934 	switch (orient) {
1935 		case TOP:
1936 			x2 = x1 = dx + temp1 + 2;
1937 			y1 = (side == UP) ? dy + temp1 + 1 : dy;
1938 			y2 = y1 + w->pyraminx.orientLineLength;
1939 			break;
1940 		case TR:
1941 			x1 = (side == UP) ? dx + temp2 + 1 : dx + temp1;
1942 			x2 = x1 - w->pyraminx.orientDiagLength;
1943 			y1 = (side == UP) ? dy + temp1 + 1 : dy;
1944 			y2 = y1 + w->pyraminx.orientDiagLength;
1945 			break;
1946 		case RIGHT:
1947 			x1 = (side == UP) ? dx + temp2 + 1 : dx + temp1 - fix - 1;
1948 			x2 = x1 - w->pyraminx.orientLineLength;
1949 			y2 = y1 = dy + temp1 - 1;
1950 			break;
1951 		case BOTTOM:
1952 			x2 = x1 = dx + temp1 - 1;
1953 			y1 = (side == UP) ? dy + temp2 + 1 : dy + temp1 - fix - 1;
1954 			y2 = y1 - w->pyraminx.orientLineLength;
1955 			break;
1956 		case BL:
1957 			x1 = (side == UP) ? dx + temp1 : dx;
1958 			x2 = x1 + w->pyraminx.orientDiagLength;
1959 			y1 = (side == UP) ? dy + temp2 + 1 : dy + temp1;
1960 			y2 = y1 - w->pyraminx.orientDiagLength;
1961 			break;
1962 		case LEFT:
1963 			x1 = (side == UP) ? dx + temp1 + 1 : dx;
1964 			x2 = x1 + w->pyraminx.orientLineLength;
1965 			y2 = y1 = dy + temp1 + 2;
1966 			break;
1967 		default:
1968 			(void) printf("DrawOrientLine: orient %d\n", orient);
1969 	}
1970 	XDrawLine(XtDisplay(w), XtWindow(w), borderGC, x1, y1, x2, y2);
1971 }
1972 
1973 Boolean
CheckSolved(PyraminxWidget w)1974 CheckSolved(PyraminxWidget w)
1975 {
1976 	int         face, position;
1977 	PyraminxLoc test;
1978 
1979 	for (face = 0; face < MAXFACES; face++)
1980 		for (position = 0; position < w->pyraminx.sizeSize; position++) {
1981 			if (!position) {
1982 				test.face = w->pyraminx.tetraLoc[face][position].face;
1983 				test.rotation = w->pyraminx.tetraLoc[face][position].rotation;
1984 			} else if (test.face !=		/* MAXSIDES * view + face */
1985 				 w->pyraminx.tetraLoc[face][position].face ||
1986 				   (w->pyraminx.orient && test.rotation !=
1987 			      w->pyraminx.tetraLoc[face][position].rotation))
1988 				return False;
1989 		}
1990 	return True;
1991 }
1992 
1993 static int
Crd(int dir,int I,int J,int side)1994 Crd(int dir, int I, int J, int side)
1995 {
1996 	if (dir == TOP || dir == BOTTOM)
1997 		return (I);
1998 	else if (dir == RIGHT || dir == LEFT)
1999 		return (J);
2000 	else			/* dir == TR || dir == BL */
2001 		return (I + J + side);
2002 }
2003 
2004 static int
Length(PyraminxWidget w,int face,int dir,int h)2005 Length(PyraminxWidget w, int face, int dir, int h)
2006 {
2007 	if (dir == TR || dir == BL)
2008 		return ((face) ? 2 * w->pyraminx.size - 1 - h : h);
2009 	else
2010 		return ((face) ? h : w->pyraminx.size - 1 - h);
2011 }
2012 
2013 static int
CheckMoveDir(PyraminxWidget w,CRD crd1,CRD crd2,int face,int * direction)2014 CheckMoveDir(PyraminxWidget w, CRD crd1, CRD crd2, int face, int *direction)
2015 {
2016 	int         which = -1, count = 0;
2017 	int         i, *p1, *p2;
2018 
2019 	p1 = &(crd1.column);
2020 	p2 = &(crd2.column);
2021 	for (i = 0; i < 3; i++, p1++, p2++)
2022 		if (*p1 == *p2) {
2023 			which = i;
2024 			count++;
2025 		}
2026 	if (count == 1)
2027 		switch (which) {
2028 			case 0:	/* COLUMN */
2029 				*direction = (crd2.row > crd1.row) ? BOTTOM : TOP;
2030 				break;
2031 			case 1:	/* ROW */
2032 				*direction = (crd2.column > crd1.column) ? RIGHT : LEFT;
2033 				break;
2034 			case 2:	/* DIAGONAL */
2035 				*direction = (crd2.column > crd1.column) ? TR : BL;
2036 				break;
2037 		}
2038 	if (!w->pyraminx.vertical && face >= MAXSIDES && *direction > LEFT)
2039 		*direction = (*direction + MAXSIDES) % MAXFACES;
2040 	return count;
2041 }
2042 
2043 static      CRD
ToCRD(PyraminxWidget w,int face,int position)2044 ToCRD(PyraminxWidget w, int face, int position)
2045 {
2046 	CRD         crd;
2047 	int         diag, diag2;
2048 
2049 	diag = Sqrt(position);
2050 	diag2 = diag * diag;
2051 	/* Passing diagonal so there is no sqrt calculation again */
2052 	if (face % 2) {
2053 		crd.diagonal = 2 * w->pyraminx.size - 1 - diag;
2054 		crd.column = w->pyraminx.size - 1 - (position - diag2) / 2;
2055 		crd.row = w->pyraminx.size - 1 - (diag2 + 2 * diag - position) / 2;
2056 	} else {
2057 		crd.diagonal = diag;
2058 		crd.column = (position - diag2) / 2;
2059 		crd.row = (diag2 + 2 * diag - position) / 2;
2060 	}
2061 	return crd;
2062 }
2063 
2064 static int
ToPosition(PyraminxWidget w,CRD crd)2065 ToPosition(PyraminxWidget w, CRD crd)
2066 {
2067 	int         diag;
2068 
2069 	if (crd.diagonal < w->pyraminx.size)
2070 		return (crd.diagonal * crd.diagonal + crd.diagonal + crd.column - crd.row);
2071 	diag = 2 * w->pyraminx.size - 1 - crd.diagonal;
2072 	return (diag * diag + diag + crd.row - crd.column);
2073 }
2074 
2075 /* This is fast for small i, a -1 is returned for negative i. */
2076 static int
Sqrt(int i)2077 Sqrt(int i)
2078 {
2079 	int         j = 0;
2080 
2081 	while (j * j <= i)
2082 		j++;
2083 	return (j - 1);
2084 }
2085 
2086 #ifdef DEBUG
2087 
2088 static void
PrintTetra(PyraminxWidget w)2089 PrintTetra(PyraminxWidget w)
2090 {
2091 	int         face, position, square;
2092 
2093 	for (face = 0; face < MAXSIDES; face++) {
2094 		square = 1;
2095 		for (position = 0; position < w->pyraminx.sizeSize; position++) {
2096 			(void) printf("%d %d  ",
2097 				   w->pyraminx.tetraLoc[face][position].face,
2098 			      w->pyraminx.tetraLoc[face][position].rotation);
2099 			if (position == square * square - 1) {
2100 				(void) printf("\n");
2101 				++square;
2102 			}
2103 		}
2104 		(void) printf("\n");
2105 	}
2106 	(void) printf("\n");
2107 }
2108 
2109 static void
PrintFace(PyraminxWidget w)2110 PrintFace(PyraminxWidget w)
2111 {
2112 	int         g, h, side;
2113 
2114 	for (g = 0; g < w->pyraminx.size; g++) {
2115 		for (h = 0; h < w->pyraminx.size - g; h++)
2116 			for (side = 0; side < MAXSIDES; side++)
2117 				if (!side || h < w->pyraminx.size - g - 1)
2118 					(void) printf("%d %d  ", w->pyraminx.faceLoc[side][h +
2119 						  g * w->pyraminx.size].face,
2120 						      w->pyraminx.faceLoc[side][h + g * w->pyraminx.size].rotation);
2121 		(void) printf("\n");
2122 	}
2123 	(void) printf("\n");
2124 
2125 #if 0
2126 	int         position;
2127 	int         square = 1;
2128 
2129 	for (position = 0; position < w->pyraminx.sizeSize; position++) {
2130 		(void) printf("%d %d  ", w->pyraminx.faceLoc[position].face,
2131 			      w->pyraminx.faceLoc[position].rotation);
2132 		if (position == square * square - 1) {
2133 			(void) printf("\n");
2134 			++square;
2135 		}
2136 	}
2137 	(void) printf("\n");
2138 #endif
2139 }
2140 
2141 static void
PrintRow2(PyraminxWidget w,int orient)2142 PrintRow2(PyraminxWidget w, int orient)
2143 {
2144 	int         g, side;
2145 
2146 	(void) printf("Row %d:\n", orient);
2147 	for (g = 0; g < w->pyraminx.size; g++)
2148 		for (side = 0; side < MAXSIDES; side++)
2149 			(void) printf("%d %d  ", w->pyraminx.rowLoc[orient][side][g].face,
2150 			       w->pyraminx.rowLoc[orient][side][g].rotation);
2151 	(void) printf("\n");
2152 }
2153 
2154 static void
PrintRow3(PyraminxWidget w,int len,int orient)2155 PrintRow3(PyraminxWidget w, int len, int orient)
2156 {
2157 	int         g, side;
2158 
2159 	(void) printf("Row %d:\n", orient);
2160 	for (g = 0; g <= len; g++)
2161 		for (side = 0; side < MAXSIDES; side++)
2162 			if (!side || g < len)
2163 				(void) printf("%d %d  ", w->pyraminx.rowLoc[orient][side][g].face,
2164 				w->pyraminx.rowLoc[orient][side][g].rotation);
2165 	(void) printf("\n");
2166 }
2167 
2168 #endif
2169