1 /*-
2 # X-BASED TRIANGLES
3 #
4 #  Triangles.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 Triangles */
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 "TrianglesP.h"
42 
43 #ifndef DATAFILE
44 #define DATAFILE "/usr/games/lib/triangles.data"
45 #endif
46 
47 static void InitializeTriangles(Widget request, Widget renew);
48 static void ExposeTriangles(Widget renew, XEvent * event, Region region);
49 static void ResizeTriangles(TrianglesWidget w);
50 static void DestroyTriangles(Widget old);
51 static Boolean SetValuesTriangles(Widget current, Widget request, Widget renew);
52 static void QuitTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
53 static void MoveTrianglesTl(TrianglesWidget w, XEvent * event, char **args, int nArgs);
54 static void MoveTrianglesTr(TrianglesWidget w, XEvent * event, char **args, int nArgs);
55 static void MoveTrianglesLeft(TrianglesWidget w, XEvent * event, char **args, int nArgs);
56 static void MoveRightTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
57 static void MoveTrianglesBl(TrianglesWidget w, XEvent * event, char **args, int nArgs);
58 static void MoveTrianglesBr(TrianglesWidget w, XEvent * event, char **args, int nArgs);
59 static void SelectTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
60 static void ReleaseTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
61 static void RandomizeTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
62 static void RandomizeTrianglesMaybe(TrianglesWidget w, XEvent * event, char **args, int nArgs);
63 static void GetTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
64 static void WriteTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
65 static void UndoTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
66 static void SolveTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
67 static void IncrementTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
68 static void DecrementTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs);
69 static int  MoveTriangles(TrianglesWidget w, int direction);
70 
71 static int  PositionToTile(TrianglesWidget w, int x, int y, int *row, int *trbl, int *tlbr, int *orient);
72 static int  MovableTile(TrianglesWidget w);
73 static void SelectTiles(TrianglesWidget w);
74 static void SetAllColors(TrianglesWidget w, Boolean init);
75 
76 static void CheckTiles(TrianglesWidget w);
77 static void ResetTiles(TrianglesWidget w);
78 static void ResizeTiles(TrianglesWidget w);
79 static void MoveNoTiles(TrianglesWidget w);
80 static int  MoveTilesDir(TrianglesWidget w, int direction);
81 static void RandomizeTiles(TrianglesWidget w);
82 static void MoveTiles(TrianglesWidget w, int from, int orient);
83 static int  ExchangeTiles(TrianglesWidget w, int pos1, int pos2);
84 static int  TileNextToSpace(TrianglesWidget w, int rowType, int orient, int direction);
85 static void DrawFrame(TrianglesWidget w, GC gc);
86 static void DrawTile(TrianglesWidget w, int pos, int orient, Boolean blank, Boolean erase, int offset);
87 static int  ToOrient(int row, int trbl, int tlbr);
88 static int  ToPosition(int row, int trbl, int tlbr);
89 static int  Sqrt(int i);
90 static int  int2String(char *buf, int number, int base);
91 
92 static char defaultTranslationsTriangles[] =
93 "<KeyPress>q: Quit()\n\
94    Ctrl<KeyPress>C: Quit()\n\
95    <KeyPress>Home: MoveTl()\n\
96    <KeyPress>KP_7: MoveTl()\n\
97    <KeyPress>R7: MoveTl()\n\
98    <KeyPress>Prior: MoveTr()\n\
99    <KeyPress>KP_9: MoveTr()\n\
100    <KeyPress>R9: MoveTr()\n\
101    <KeyPress>Left: MoveLeft()\n\
102    <KeyPress>KP_4: MoveLeft()\n\
103    <KeyPress>R10: MoveLeft()\n\
104    <KeyPress>Right: MoveRight()\n\
105    <KeyPress>KP_6: MoveRight()\n\
106    <KeyPress>R12: MoveRight()\n\
107    <KeyPress>End: MoveBl()\n\
108    <KeyPress>KP_1: MoveBl()\n\
109    <KeyPress>R13: MoveBl()\n\
110    <KeyPress>Next: MoveBr()\n\
111    <KeyPress>KP_3: MoveBr()\n\
112    <KeyPress>R15: MoveBr()\n\
113    <Btn1Down>: Select()\n\
114    <Btn1Up>: Release()\n\
115    <KeyPress>r: Randomize()\n\
116    <Btn3Down>(2+): Randomize()\n\
117    <Btn3Down>: RandomizeMaybe()\n\
118    <KeyPress>g: Get()\n\
119    <KeyPress>w: Write()\n\
120    <KeyPress>u: Undo()\n\
121    <KeyPress>s: Solve()\n\
122    <KeyPress>i: Increment()\n\
123    <KeyPress>d: Decrement()";
124 
125 static XtActionsRec actionsListTriangles[] =
126 {
127 	{"Quit", (XtActionProc) QuitTriangles},
128 	{"MoveTl", (XtActionProc) MoveTrianglesTl},
129 	{"MoveTr", (XtActionProc) MoveTrianglesTr},
130 	{"MoveLeft", (XtActionProc) MoveTrianglesLeft},
131 	{"MoveRight", (XtActionProc) MoveRightTriangles},
132 	{"MoveBl", (XtActionProc) MoveTrianglesBl},
133 	{"MoveBr", (XtActionProc) MoveTrianglesBr},
134 	{"Select", (XtActionProc) SelectTriangles},
135 	{"Release", (XtActionProc) ReleaseTriangles},
136 	{"Randomize", (XtActionProc) RandomizeTriangles},
137 	{"RandomizeMaybe", (XtActionProc) RandomizeTrianglesMaybe},
138 	{"Get", (XtActionProc) GetTriangles},
139 	{"Write", (XtActionProc) WriteTriangles},
140 	{"Undo", (XtActionProc) UndoTriangles},
141 	{"Solve", (XtActionProc) SolveTriangles},
142 	{"Increment", (XtActionProc) IncrementTriangles},
143 	{"Decrement", (XtActionProc) DecrementTriangles}
144 };
145 
146 static XtResource resourcesTriangles[] =
147 {
148 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
149 	 XtOffset(TrianglesWidget, triangles.username), XtRString, "nobody"},
150 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
151 	 XtOffset(TrianglesWidget, triangles.foreground), XtRString, XtDefaultForeground},
152 	{XtNtileColor, XtCColor, XtRPixel, sizeof (Pixel),
153 	 XtOffset(TrianglesWidget, triangles.tileColor), XtRString, XtDefaultForeground},
154 	{XtNtileBorder, XtCColor, XtRPixel, sizeof (Pixel),
155 	 XtOffset(TrianglesWidget, triangles.borderColor), XtRString, XtDefaultBackground},
156 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
157 	 XtOffset(TrianglesWidget, core.width), XtRString, "200"},
158 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
159 	 XtOffset(TrianglesWidget, core.height), XtRString, "173"},
160 	{XtNsize, XtCSize, XtRInt, sizeof (int),
161 	 XtOffset(TrianglesWidget, triangles.size), XtRString, "4"},	/* DEFAULTTRIS */
162 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
163 	 XtOffset(TrianglesWidget, triangles.mono), XtRString, "FALSE"},
164 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
165 	 XtOffset(TrianglesWidget, triangles.reverse), XtRString, "FALSE"},
166 	{XtNbase, XtCBase, XtRInt, sizeof (int),
167 	 XtOffset(TrianglesWidget, triangles.base), XtRString, "10"},
168 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
169 	 XtOffset(TrianglesWidget, triangles.started), XtRString, "FALSE"},
170 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
171 	 XtOffset(TrianglesWidget, triangles.select), XtRCallback, NULL}
172 };
173 
174 TrianglesClassRec trianglesClassRec =
175 {
176 	{
177 		(WidgetClass) & widgetClassRec,		/* superclass */
178 		"Triangles",	/* class name */
179 		sizeof (TrianglesRec),	/* widget size */
180 		NULL,		/* class initialize */
181 		NULL,		/* class part initialize */
182 		FALSE,		/* class inited */
183 		(XtInitProc) InitializeTriangles,	/* initialize */
184 		NULL,		/* initialize hook */
185 		XtInheritRealize,	/* realize */
186 		actionsListTriangles,	/* actions */
187 		XtNumber(actionsListTriangles),		/* num actions */
188 		resourcesTriangles,	/* resources */
189 		XtNumber(resourcesTriangles),	/* num resources */
190 		NULLQUARK,	/* xrm class */
191 		TRUE,		/* compress motion */
192 		TRUE,		/* compress exposure */
193 		TRUE,		/* compress enterleave */
194 		TRUE,		/* visible interest */
195 		(XtWidgetProc) DestroyTriangles,	/* destroy */
196 		(XtWidgetProc) ResizeTriangles,		/* resize */
197 		(XtExposeProc) ExposeTriangles,		/* expose */
198 		(XtSetValuesFunc) SetValuesTriangles,	/* set values */
199 		NULL,		/* set values hook */
200 		XtInheritSetValuesAlmost,	/* set values almost */
201 		NULL,		/* get values hook */
202 		NULL,		/* accept focus */
203 		XtVersion,	/* version */
204 		NULL,		/* callback private */
205 		defaultTranslationsTriangles,	/* tm table */
206 		NULL,		/* query geometry */
207 		NULL,		/* display accelerator */
208 		NULL		/* extension */
209 	},
210 	{
211 		0		/* ignore */
212 	}
213 };
214 
215 WidgetClass trianglesWidgetClass = (WidgetClass) & trianglesClassRec;
216 
217 static XPoint triangleUnit[MAXORIENT][ROWTYPES + 1] =
218 {
219 	{
220 		{0, 0},
221 		{-1, -1},
222 		{2, 0},
223 		{-1, 1}},
224 	{
225 		{0, 0},
226 		{1, 1},
227 		{-2, 0},
228 		{1, -1}}
229 };
230 static XPoint triangleList[MAXORIENT][ROWTYPES + 1];
231 
232 #ifndef HAVE_USLEEP
233 #if !defined( VMS ) || defined( XVMSUTILS ) ||  ( __VMS_VER >= 70000000 )
234 #ifdef USE_XVMSUTILS
235 #include <X11/unix_time.h>
236 #endif
237 #if HAVE_SYS_TIME_H
238 #include <sys/time.h>
239 #else
240 #if HAVE_SYS_SELECT_H
241 #include <sys/select.h>
242 #endif
243 #endif
244 #endif
245 #if defined(SYSV) || defined(SVR4)
246 #ifdef LESS_THAN_AIX3_2
247 #include <sys/poll.h>
248 #else /* !LESS_THAN_AIX3_2 */
249 #include <poll.h>
250 #endif /* !LESS_THAN_AIX3_2 */
251 #endif /* defined(SYSV) || defined(SVR4) */
252 
253 static int
usleep(unsigned int usec)254 usleep(unsigned int usec)
255 {
256 #if (defined (SYSV) || defined(SVR4)) && !defined(__hpux)
257 #if defined(HAVE_NANOSLEEP)
258 	{
259 		struct timespec rqt;
260 
261 		rqt.tv_nsec = 1000 * (usec % (unsigned int) 1000000);
262 		rqt.tv_sec = usec / (unsigned int) 1000000;
263 		return nanosleep(&rqt, NULL);
264 	}
265 #else
266 	(void) poll((void *) 0, (int) 0, usec / 1000);	/* ms resolution */
267 #endif
268 #else
269 #ifdef VMS
270 	long        timadr[2];
271 
272 	if (usec != 0) {
273 		timadr[0] = -usec * 10;
274 		timadr[1] = -1;
275 
276 		sys$setimr(4, &timadr, 0, 0, 0);
277 		sys$waitfr(4);
278 	}
279 #else
280 	struct timeval time_out;
281 
282 #if  0
283 	/* (!defined(AIXV3) && !defined(__hpux)) */
284 	extern int  select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
285 
286 #endif
287 
288 	time_out.tv_usec = usec % (unsigned int) 1000000;
289 	time_out.tv_sec = usec / (unsigned int) 1000000;
290 	(void) select(0, (void *) 0, (void *) 0, (void *) 0, &time_out);
291 #endif
292 #endif
293 	return 0;
294 }
295 #endif
296 
297 static void
Sleep(unsigned int cMilliseconds)298 Sleep(unsigned int cMilliseconds)
299 {
300 	(void) usleep(cMilliseconds * 1000);
301 }
302 
303 static void
InitializeTriangles(Widget request,Widget renew)304 InitializeTriangles(Widget request, Widget renew)
305 {
306 	TrianglesWidget w = (TrianglesWidget) renew;
307 
308 	w->triangles.tileOfPosition = NULL;
309 	CheckTiles(w);
310 	InitMoves();
311 	ResetTiles(w);
312 	(void) SRAND(getpid());
313 	w->triangles.depth = DefaultDepthOfScreen(XtScreen(w));
314 	SetAllColors(w, True);
315 	ResizeTriangles(w);
316 }
317 
318 static void
DestroyTriangles(Widget old)319 DestroyTriangles(Widget old)
320 {
321 	TrianglesWidget w = (TrianglesWidget) old;
322 
323 	XtReleaseGC(old, w->triangles.tileGC);
324 	XtReleaseGC(old, w->triangles.borderGC);
325 	XtReleaseGC(old, w->triangles.puzzleGC);
326 	XtReleaseGC(old, w->triangles.inverseGC);
327 	XtRemoveCallbacks(old, XtNselectCallback, w->triangles.select);
328 }
329 
330 static void
ResizeTriangles(TrianglesWidget w)331 ResizeTriangles(TrianglesWidget w)
332 {
333 	double      sqrt_3 = 1.73205080756887729352744634150587237;
334 	XPoint      tempSize;
335 
336 	w->triangles.delta.x = 5;
337 	w->triangles.delta.y = 3;
338 	w->triangles.offset.x = MAX(((int) w->core.width -
339 			       w->triangles.delta.x) / w->triangles.size, 0);
340 	w->triangles.offset.y = MAX(((int) w->core.height -
341 			   2 * w->triangles.delta.y) / w->triangles.size, 0);
342 	tempSize.y = (int) ((double) w->triangles.offset.x * sqrt_3 / 2.0);
343 	tempSize.x = (int) ((double) w->triangles.offset.y * 2.0 / sqrt_3);
344 	if (tempSize.y < w->triangles.offset.y)
345 		w->triangles.offset.y = tempSize.y;
346 	else			/* tempSize.x <=  w->triangles.wid */
347 		w->triangles.offset.x = tempSize.x;
348 	w->triangles.puzzleSize.x = w->triangles.offset.x * w->triangles.size +
349 		w->triangles.delta.x + 2;
350 	w->triangles.puzzleSize.y = w->triangles.offset.y * w->triangles.size +
351 		w->triangles.delta.y + 2;
352 	w->triangles.puzzleOffset.x = ((int) w->core.width -
353 				       w->triangles.puzzleSize.x + 2) / 2;
354 	w->triangles.puzzleOffset.y = ((int) w->core.height -
355 				       w->triangles.puzzleSize.y + 2) / 2;
356 	w->triangles.tileSize.x = MAX(w->triangles.offset.x - w->triangles.delta.x,
357 				      0);
358 	w->triangles.tileSize.y = MAX(w->triangles.offset.y - w->triangles.delta.y,
359 				      0);
360 	ResizeTiles(w);
361 }
362 
363 static void
ExposeTriangles(Widget renew,XEvent * event,Region region)364 ExposeTriangles(Widget renew, XEvent * event, Region region)
365 {
366 	TrianglesWidget w = (TrianglesWidget) renew;
367 
368 	if (w->core.visible) {
369 		if (w->triangles.reverse)
370 			XFillRectangle(XtDisplay(w), XtWindow(w),
371 				       w->triangles.inverseGC, 0, 0, w->core.width, w->core.height);
372 		DrawFrame(w, w->triangles.puzzleGC);
373 		DrawAllTiles(w);
374 	}
375 }
376 
377 static      Boolean
SetValuesTriangles(Widget current,Widget request,Widget renew)378 SetValuesTriangles(Widget current, Widget request, Widget renew)
379 {
380 	TrianglesWidget c = (TrianglesWidget) current, w = (TrianglesWidget) renew;
381 	Boolean     redraw = FALSE;
382 	Boolean     redrawTiles = FALSE;
383 
384 	CheckTiles(w);
385 	if (w->core.background_pixel != c->core.background_pixel ||
386 	    w->triangles.foreground != c->triangles.foreground ||
387 	    w->triangles.borderColor != c->triangles.borderColor ||
388 	    w->triangles.tileColor != c->triangles.tileColor ||
389 	    w->triangles.reverse != c->triangles.reverse ||
390 	    w->triangles.mono != c->triangles.mono) {
391 		SetAllColors(w, False);
392 		redrawTiles = TRUE;
393 	}
394 	if (w->triangles.size != c->triangles.size ||
395 	    w->triangles.base != c->triangles.base) {
396 		ResetTiles(w);
397 		ResizeTriangles(w);
398 		redraw = TRUE;
399 	} else if (w->triangles.offset.x != c->triangles.offset.x ||
400 		   w->triangles.offset.y != c->triangles.offset.y) {
401 		ResizeTriangles(w);
402 		redraw = TRUE;
403 	}
404 	if (redrawTiles && !redraw && XtIsRealized(renew) && renew->core.visible) {
405 		DrawFrame(c, c->triangles.inverseGC);
406 		DrawFrame(w, w->triangles.puzzleGC);
407 		DrawAllTiles(w);
408 	}
409 	return (redraw);
410 }
411 
412 static void
QuitTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)413 QuitTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
414 {
415 	XtCloseDisplay(XtDisplay(w));
416 	exit(0);
417 }
418 
419 static void
SelectTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)420 SelectTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
421 {
422 	int         pos, orient, row, trbl, tlbr, rowType;
423 
424 	pos = PositionToTile(w, event->xbutton.x, event->xbutton.y,
425 			     &row, &trbl, &tlbr, &orient);
426 	if (pos >= 0) {
427 		if (CheckSolved(w)) {
428 			MoveNoTiles(w);
429 			w->triangles.currentPosition = -1;
430 			return;
431 		}
432 		w->triangles.currentPosition = pos;
433 		w->triangles.currentPositionOrient = orient;
434 		w->triangles.currentRow[TRBL] = trbl;
435 		w->triangles.currentRow[TLBR] = tlbr;
436 		w->triangles.currentRow[ROW] = row;
437 		rowType = MovableTile(w);
438 		if (rowType < 0) {
439 			trianglesCallbackStruct cb;
440 
441 			DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, rowType == TRIANGLES_SPACE, False, TRUE);
442 			XFlush(XtDisplay(w));
443 			Sleep(100);
444 			DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, True, True, TRUE);
445 			if (rowType != TRIANGLES_SPACE)
446 				DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, False, False, FALSE);
447 			cb.reason = rowType;
448 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
449 			w->triangles.currentPosition = -1;
450 			return;
451 		}
452 		DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, False, False, TRUE);
453 	} else
454 		w->triangles.currentPosition = -1;
455 }
456 
457 static void
ReleaseTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)458 ReleaseTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
459 {
460 	if (w->triangles.currentPosition == -1)
461 		return;
462 	DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, True, True, TRUE);
463 	DrawTile(w, w->triangles.currentPosition, w->triangles.currentPositionOrient, False, False, FALSE);
464 	SelectTiles(w);
465 	w->triangles.currentPosition = -1;
466 }
467 
468 static void
RandomizeTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)469 RandomizeTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
470 {
471 	RandomizeTiles(w);
472 }
473 
474 static void
RandomizeTrianglesMaybe(TrianglesWidget w,XEvent * event,char ** args,int nArgs)475 RandomizeTrianglesMaybe(TrianglesWidget w, XEvent * event, char **args, int nArgs)
476 {
477 	if (!w->triangles.started)
478 		RandomizeTiles(w);
479 }
480 
481 static void
GetTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)482 GetTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
483 {
484 	FILE       *fp;
485 	char        c;
486 	int         i, size, moves;
487 	trianglesCallbackStruct cb;
488 
489 	if ((fp = fopen(DATAFILE, "r")) == NULL)
490 		(void) printf("Can not read %s for get.\n", DATAFILE);
491 	else {
492 		FlushMoves(w);
493 		while ((c = getc(fp)) != EOF && c != SYMBOL);
494 		(void) fscanf(fp, "%d", &size);
495 		if (size >= MINTRIANGLES) {
496 			for (i = w->triangles.size; i < size; i++) {
497 				cb.reason = TRIANGLES_INC;
498 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
499 			}
500 			for (i = w->triangles.size; i > size; i--) {
501 				cb.reason = TRIANGLES_DEC;
502 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
503 			}
504 		} else
505 			(void) printf("%s corrupted: size %d should be between %d and MAXINT\n",
506 				      DATAFILE, size, MINTRIANGLES);
507 		while ((c = getc(fp)) != EOF && c != SYMBOL);
508 		(void) fscanf(fp, "%d", &moves);
509 		ScanStartPosition(fp, w);
510 		cb.reason = TRIANGLES_RESTORE;
511 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
512 		SetStartPosition(w);
513 		ScanMoves(fp, w, moves);
514 		(void) fclose(fp);
515 		(void) printf("%s: size %d, moves %d.\n", DATAFILE, size, moves);
516 	}
517 }
518 
519 static void
WriteTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)520 WriteTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
521 {
522 	FILE       *fp;
523 
524 	if ((fp = fopen(DATAFILE, "w")) == NULL)
525 		(void) printf("Can not write to %s.\n", DATAFILE);
526 	else {
527 		(void) fprintf(fp, "size%c %d\n", SYMBOL, w->triangles.size);
528 		(void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
529 		PrintStartPosition(fp, w);
530 		PrintMoves(fp);
531 		(void) fclose(fp);
532 		(void) printf("Saved to %s.\n", DATAFILE);
533 	}
534 }
535 
536 static void
UndoTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)537 UndoTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
538 {
539 	if (MadeMoves()) {
540 		int         direction;
541 
542 		GetMove(&direction);
543 		direction = (direction + (COORD / 2)) % COORD;
544 		if (MoveTilesDir(w, direction)) {
545 			trianglesCallbackStruct cb;
546 
547 			cb.reason = TRIANGLES_UNDO;
548 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
549 		}
550 	}
551 }
552 
553 static void
SolveTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)554 SolveTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
555 {
556 #if 0
557 	SolveTiles(w);		/* Sorry, this is not implemented */
558 #endif
559 }
560 
561 static void
IncrementTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)562 IncrementTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
563 {
564 	trianglesCallbackStruct cb;
565 
566 	cb.reason = TRIANGLES_INC;
567 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
568 }
569 
570 static void
DecrementTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)571 DecrementTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
572 {
573 	trianglesCallbackStruct cb;
574 
575 	if (w->triangles.size <= MINTRIANGLES)
576 		return;
577 	cb.reason = TRIANGLES_DEC;
578 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
579 }
580 
581 static void
MoveTrianglesTl(TrianglesWidget w,XEvent * event,char ** args,int nArgs)582 MoveTrianglesTl(TrianglesWidget w, XEvent * event, char **args, int nArgs)
583 {
584 	(void) MoveTriangles(w, TL);
585 }
586 
587 static void
MoveTrianglesTr(TrianglesWidget w,XEvent * event,char ** args,int nArgs)588 MoveTrianglesTr(TrianglesWidget w, XEvent * event, char **args, int nArgs)
589 {
590 	(void) MoveTriangles(w, TR);
591 }
592 
593 static void
MoveTrianglesLeft(TrianglesWidget w,XEvent * event,char ** args,int nArgs)594 MoveTrianglesLeft(TrianglesWidget w, XEvent * event, char **args, int nArgs)
595 {
596 	(void) MoveTriangles(w, LEFT);
597 }
598 
599 static void
MoveRightTriangles(TrianglesWidget w,XEvent * event,char ** args,int nArgs)600 MoveRightTriangles(TrianglesWidget w, XEvent * event, char **args, int nArgs)
601 {
602 	(void) MoveTriangles(w, RIGHT);
603 }
604 
605 static void
MoveTrianglesBl(TrianglesWidget w,XEvent * event,char ** args,int nArgs)606 MoveTrianglesBl(TrianglesWidget w, XEvent * event, char **args, int nArgs)
607 {
608 	(void) MoveTriangles(w, BL);
609 }
610 
611 static void
MoveTrianglesBr(TrianglesWidget w,XEvent * event,char ** args,int nArgs)612 MoveTrianglesBr(TrianglesWidget w, XEvent * event, char **args, int nArgs)
613 {
614 	(void) MoveTriangles(w, BR);
615 }
616 
617 static int
MoveTriangles(TrianglesWidget w,int direction)618 MoveTriangles(TrianglesWidget w, int direction)
619 {
620 	trianglesCallbackStruct cb;
621 
622 	if (CheckSolved(w)) {
623 		MoveNoTiles(w);
624 		return FALSE;
625 	}
626 	if (!MoveTrianglesDir(w, direction)) {
627 		cb.reason = TRIANGLES_BLOCKED;
628 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
629 		return FALSE;
630 	}
631 	if (CheckSolved(w)) {
632 		cb.reason = TRIANGLES_SOLVED;
633 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
634 	}
635 	return TRUE;
636 }
637 
638 int
MoveTrianglesDir(TrianglesWidget w,int direction)639 MoveTrianglesDir(TrianglesWidget w, int direction)
640 {
641 	trianglesCallbackStruct cb;
642 
643 	if (MoveTilesDir(w, direction)) {
644 		cb.reason = TRIANGLES_MOVED;
645 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
646 		PutMove(direction);
647 		return TRUE;
648 	}
649 	return FALSE;
650 }
651 
652 static int
PositionToTile(TrianglesWidget w,int x,int y,int * row,int * trbl,int * tlbr,int * orient)653 PositionToTile(TrianglesWidget w, int x, int y, int *row, int *trbl, int *tlbr, int *orient)
654 {
655 	int         sumX, sumY, sumX2;
656 
657 	sumX = w->triangles.size * w->triangles.offset.x + w->triangles.delta.x;
658 	sumY = w->triangles.size * w->triangles.offset.y + w->triangles.delta.y + 1;
659 	sumX2 = sumX / 2;
660 	x -= w->triangles.puzzleOffset.x;
661 	y -= w->triangles.puzzleOffset.y;
662 	if (x * (sumY + w->triangles.delta.y) + y * (sumX2 + 1) <
663 	    (sumX2 + 1) * (sumY + w->triangles.delta.y) ||
664 	    x * (sumY + w->triangles.delta.y) - y * (sumX2 - 1) >
665 	    (sumX2 - 1) * (sumY + w->triangles.delta.y) ||
666 	    y > sumY - w->triangles.delta.y)
667 		return -1;
668 	*row = (y - w->triangles.delta.y) / w->triangles.offset.y;
669 	*trbl = (x - sumX2 - 1 + *row * w->triangles.offset.x / 2) /
670 		w->triangles.offset.x;
671 	*trbl += ((x - (*trbl + 1) * w->triangles.offset.x) *
672 		  (sumY + w->triangles.delta.y) + y * (sumX2 + 1)) /
673 		((sumX2 + 1) * (sumY + w->triangles.delta.y));
674 	*tlbr = (-x + sumX2 - 1 + *row * w->triangles.offset.x / 2) /
675 		w->triangles.offset.x;
676 	*tlbr += 1 + ((-x - (*tlbr + 1) * w->triangles.offset.x) *
677 		      (sumY + w->triangles.delta.y) + y * (sumX2 - 1)) /
678 		((sumX2 - 1) * (sumY + w->triangles.delta.y));
679 	if (*row >= 0 && *trbl >= 0 && *tlbr >= 0 && *row < w->triangles.size &&
680 	    *trbl < w->triangles.size && *tlbr < w->triangles.size) {
681 		*orient = ToOrient(*row, *trbl, *tlbr);
682 		return ToPosition(*row, *trbl, *tlbr);
683 	} else
684 		return -1;
685 }
686 
687 static int
MovableTile(TrianglesWidget w)688 MovableTile(TrianglesWidget w)
689 {
690 	int         rowType = TRIANGLES_BLOCKED, l;
691 
692 	/* Are the spaces in a "row" with the mouse click?
693 	   (If two, then one clicked on a space). */
694 	for (l = 0; l < ROWTYPES; l++) {
695 		if (w->triangles.currentRow[l] == w->triangles.spaceRow[DOWN][l] &&
696 		w->triangles.currentRow[l] == w->triangles.spaceRow[UP][l]) {
697 			if (rowType == TRIANGLES_BLOCKED) {
698 				rowType = l;
699 			} else {
700 				return TRIANGLES_SPACE;
701 			}
702 		}
703 	}
704 	return rowType;
705 }
706 
707 static void
SelectTiles(TrianglesWidget w)708 SelectTiles(TrianglesWidget w)
709 {
710 	trianglesCallbackStruct cb;
711 	int         rowType, orient, next;
712 
713 	rowType = MovableTile(w);
714 	if (rowType < 0) {
715 		(void) printf("SelectTiles: rowType %d\n", rowType);
716 		return;
717 	}
718 	if (w->triangles.currentPosition <
719 	    w->triangles.spacePosition[w->triangles.currentPositionOrient]) {
720 		orient = (w->triangles.spacePosition[UP] + (rowType == TRBL) <
721 			  w->triangles.spacePosition[DOWN]) ? UP : DOWN;
722 		while (w->triangles.currentPosition <
723 		       w->triangles.spacePosition[w->triangles.currentPositionOrient]) {
724 			next = TileNextToSpace(w, rowType, orient, UP);
725 			orient = !orient;
726 			MoveTiles(w, next, orient);
727 			cb.reason = TRIANGLES_MOVED;
728 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
729 			switch (rowType) {
730 				case TLBR:
731 					PutMove(BR);
732 					break;
733 				case TRBL:
734 					PutMove(BL);
735 					break;
736 				case ROW:
737 					PutMove(RIGHT);
738 					break;
739 				default:
740 					(void) printf("SelectTiles: rowType %d\n", rowType);
741 			}
742 		}
743 	} else {		/*w->triangles.currentPosition >
744 				   w->triangles.spacePosition[w->triangles.currentPositionOrient] */
745 		orient = (w->triangles.spacePosition[UP] + 2 * (rowType == TRBL) >
746 			  w->triangles.spacePosition[DOWN]) ? UP : DOWN;
747 		while (w->triangles.currentPosition >
748 		       w->triangles.spacePosition[w->triangles.currentPositionOrient]) {
749 			next = TileNextToSpace(w, rowType, orient, DOWN);
750 			orient = !orient;
751 			MoveTiles(w, next, orient);
752 			cb.reason = TRIANGLES_MOVED;
753 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
754 			switch (rowType) {
755 				case TLBR:
756 					PutMove(TL);
757 					break;
758 				case TRBL:
759 					PutMove(TR);
760 					break;
761 				case ROW:
762 					PutMove(LEFT);
763 					break;
764 				default:
765 					(void) printf("SelectTiles: rowType %d\n", rowType);
766 			}
767 		}
768 	}
769 	if (CheckSolved(w)) {
770 		cb.reason = TRIANGLES_SOLVED;
771 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
772 	}
773 }
774 
775 static void
SetAllColors(TrianglesWidget w,Boolean init)776 SetAllColors(TrianglesWidget w, Boolean init)
777 {
778 	XGCValues   values;
779 	XtGCMask    valueMask;
780 
781 	valueMask = GCForeground | GCBackground;
782 	if (w->triangles.reverse) {
783 		values.foreground = w->triangles.foreground;
784 		values.background = w->core.background_pixel;
785 	} else {
786 		values.foreground = w->core.background_pixel;
787 		values.background = w->triangles.foreground;
788 	}
789 	if (!init)
790 		XtReleaseGC((Widget) w, w->triangles.inverseGC);
791 	w->triangles.inverseGC = XtGetGC((Widget) w, valueMask, &values);
792 	if (w->triangles.reverse) {
793 		values.foreground = w->core.background_pixel;
794 		values.background = w->triangles.foreground;
795 	} else {
796 		values.foreground = w->triangles.foreground;
797 		values.background = w->core.background_pixel;
798 	}
799 	if (!init)
800 		XtReleaseGC((Widget) w, w->triangles.puzzleGC);
801 	w->triangles.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
802 	if (w->triangles.depth < 2 || w->triangles.mono) {
803 		if (w->triangles.reverse) {
804 			values.foreground = w->core.background_pixel;
805 			values.background = w->triangles.foreground;
806 		} else {
807 			values.foreground = w->triangles.foreground;
808 			values.background = w->core.background_pixel;
809 		}
810 	} else {
811 		values.foreground = w->triangles.tileColor;
812 		values.background = w->triangles.borderColor;
813 	}
814 	if (!init)
815 		XtReleaseGC((Widget) w, w->triangles.tileGC);
816 	w->triangles.tileGC = XtGetGC((Widget) w, valueMask, &values);
817 	if (w->triangles.depth < 2 || w->triangles.mono) {
818 		if (w->triangles.reverse) {
819 			values.foreground = w->triangles.foreground;
820 			values.background = w->core.background_pixel;
821 		} else {
822 			values.foreground = w->core.background_pixel;
823 			values.background = w->triangles.foreground;
824 		}
825 	} else {
826 		values.foreground = w->triangles.borderColor;
827 		values.background = w->triangles.tileColor;
828 	}
829 	if (!init)
830 		XtReleaseGC((Widget) w, w->triangles.borderGC);
831 	w->triangles.borderGC = XtGetGC((Widget) w, valueMask, &values);
832 }
833 
834 static void
CheckTiles(TrianglesWidget w)835 CheckTiles(TrianglesWidget w)
836 {
837 	char        buf[121];
838 
839 	if (w->triangles.size < MINTRIANGLES) {
840 		(void) sprintf(buf,
841 		"Number of Triangles on a edge out of bounds, use %d..MAXINT",
842 			       MINTRIANGLES);
843 		XtWarning(buf);
844 		w->triangles.size = DEFAULTTRIANGLES;
845 	}
846 	if (w->triangles.base > 36) {
847 		/* 10 numbers + 26 letters (ASCII or EBCDIC) */
848 		XtWarning("Base must be less than or equal to 36");
849 		w->triangles.base = 10;
850 	} else if (w->triangles.base <= 1) {	/* Base 1 is rediculous :) */
851 		XtWarning("Base must be greater than 1");
852 		w->triangles.base = 10;
853 	}
854 }
855 
856 static void
ResetTiles(TrianglesWidget w)857 ResetTiles(TrianglesWidget w)
858 {
859 	int         i;
860 
861 	w->triangles.sizeSize = w->triangles.size * w->triangles.size;
862 	if (w->triangles.tileOfPosition)
863 		(void) free((void *) w->triangles.tileOfPosition);
864 	if (!(w->triangles.tileOfPosition = (int *)
865 	      malloc(sizeof (int) * w->triangles.sizeSize)))
866 		            XtError("Not enough memory, exiting.");
867 
868 	if (startPosition)
869 		(void) free((void *) startPosition);
870 	if (!(startPosition = (int *)
871 	      malloc(sizeof (int) * w->triangles.sizeSize)))
872 		            XtError("Not enough memory, exiting.");
873 
874 	w->triangles.spacePosition[UP] = w->triangles.sizeSize - 1;
875 	w->triangles.spaceRow[UP][TRBL] = w->triangles.size - 1;	/*i */
876 	w->triangles.spaceRow[UP][TLBR] = 0;	/*j */
877 	w->triangles.spaceRow[UP][ROW] = w->triangles.size - 1;		/*k */
878 	if (w->triangles.size > 1) {
879 		w->triangles.spacePosition[DOWN] = w->triangles.sizeSize - 2;
880 		w->triangles.spaceRow[DOWN][TRBL] = w->triangles.size - 2;	/*i */
881 		w->triangles.spaceRow[DOWN][TLBR] = 0;	/*j */
882 		w->triangles.spaceRow[DOWN][ROW] = w->triangles.size - 1;	/*k */
883 		w->triangles.tileOfPosition[w->triangles.sizeSize - 2] = -1;
884 	}
885 	w->triangles.tileOfPosition[w->triangles.sizeSize - 1] = 0;
886 	for (i = 1; i < w->triangles.sizeSize - 1; i++)
887 		w->triangles.tileOfPosition[i - 1] = i;
888 	FlushMoves(w);
889 	w->triangles.currentPosition = -1;
890 	w->triangles.started = FALSE;
891 }
892 
893 static void
ResizeTiles(TrianglesWidget w)894 ResizeTiles(TrianglesWidget w)
895 {
896 	int         i, j;
897 
898 	for (j = 0; j < MAXORIENT; j++)
899 		for (i = 0; i <= ROWTYPES; i++) {
900 			triangleList[j][i].x = (w->triangles.tileSize.x / 2) *
901 				triangleUnit[j][i].x;
902 			triangleList[j][i].y = w->triangles.tileSize.y *
903 				triangleUnit[j][i].y;
904 		}
905 	w->triangles.digitOffset.x = 3;
906 	w->triangles.digitOffset.y = 2;
907 }
908 
909 static void
MoveNoTiles(TrianglesWidget w)910 MoveNoTiles(TrianglesWidget w)
911 {
912 	trianglesCallbackStruct cb;
913 
914 	cb.reason = TRIANGLES_IGNORE;
915 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
916 }
917 
918 static int
MoveTilesDir(TrianglesWidget w,int direction)919 MoveTilesDir(TrianglesWidget w, int direction)
920 {
921 	int         orient;
922 
923 	switch (direction) {
924 		case TR:
925 			if (w->triangles.spaceRow[UP][TRBL] == w->triangles.spaceRow[DOWN][TRBL]
926 			    && w->triangles.spaceRow[UP][ROW] != w->triangles.size - 1) {
927 				orient = (w->triangles.spacePosition[UP] + 2 >
928 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
929 				MoveTiles(w, TileNextToSpace(w, TRBL, orient, DOWN), !orient);
930 				return TRUE;
931 			}
932 			break;
933 		case RIGHT:
934 			if (w->triangles.spaceRow[UP][ROW] == w->triangles.spaceRow[DOWN][ROW]
935 			    && w->triangles.spaceRow[UP][TRBL] != 0) {
936 				orient = (w->triangles.spacePosition[UP] <
937 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
938 				MoveTiles(w, TileNextToSpace(w, ROW, orient, UP), !orient);
939 				return TRUE;
940 			}
941 			break;
942 		case BR:
943 			if (w->triangles.spaceRow[UP][TLBR] == w->triangles.spaceRow[DOWN][TLBR]
944 			    && w->triangles.spaceRow[UP][TRBL] != 0) {
945 				orient = (w->triangles.spacePosition[UP] <
946 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
947 				MoveTiles(w, TileNextToSpace(w, TLBR, orient, UP), !orient);
948 				return TRUE;
949 			}
950 			break;
951 		case BL:
952 			if (w->triangles.spaceRow[UP][TRBL] == w->triangles.spaceRow[DOWN][TRBL]
953 			    && w->triangles.spaceRow[UP][TLBR] != 0) {
954 				orient = (w->triangles.spacePosition[UP] + 1 <
955 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
956 				MoveTiles(w, TileNextToSpace(w, TRBL, orient, UP), !orient);
957 				return TRUE;
958 			}
959 			break;
960 		case LEFT:
961 			if (w->triangles.spaceRow[UP][ROW] == w->triangles.spaceRow[DOWN][ROW]
962 			    && w->triangles.spaceRow[UP][TLBR] != 0) {
963 				orient = (w->triangles.spacePosition[UP] >
964 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
965 				MoveTiles(w, TileNextToSpace(w, ROW, orient, DOWN), !orient);
966 				return TRUE;
967 			}
968 			break;
969 		case TL:
970 			if (w->triangles.spaceRow[UP][TLBR] == w->triangles.spaceRow[DOWN][TLBR]
971 			    && w->triangles.spaceRow[UP][ROW] != w->triangles.size - 1) {
972 				orient = (w->triangles.spacePosition[UP] >
973 				w->triangles.spacePosition[DOWN]) ? UP : DOWN;
974 				MoveTiles(w, TileNextToSpace(w, TLBR, orient, DOWN), !orient);
975 				return TRUE;
976 			}
977 			break;
978 		default:
979 			(void) printf("MoveTilesDir: direction %d\n", direction);
980 	}
981 	return FALSE;
982 }
983 
984 static void
RandomizeTiles(TrianglesWidget w)985 RandomizeTiles(TrianglesWidget w)
986 {
987 	trianglesCallbackStruct cb;
988 
989 	/* First interchange tiles but only with other tiles of the same
990 	   orientation */
991 	if (w->triangles.size > 2) {
992 		int         currentPos, randomPos, randomRow;
993 		int         currentOrient = UP, randomOrient = DOWN;
994 		int         step = 1, fin = 1;
995 
996 		for (currentPos = 0; currentPos < w->triangles.sizeSize; currentPos++) {
997 			randomPos = currentPos;
998 			while (currentPos == randomPos || currentOrient != randomOrient) {
999 				randomPos = NRAND(w->triangles.sizeSize);
1000 				randomRow = Row(randomPos);
1001 				randomOrient = !((randomRow + TrBl(randomPos, randomRow) / 2 +
1002 					TlBr(randomPos, randomRow) / 2) % 2);
1003 			}
1004 			(void) ExchangeTiles(w, currentPos, randomPos);
1005 			if (fin == currentPos + 1) {
1006 				currentOrient = UP;
1007 				step += 2;
1008 				fin += step;
1009 			} else
1010 				currentOrient = !currentOrient;
1011 		}
1012 		DrawAllTiles(w);
1013 	}
1014 	/* Now move the spaces around randomly */
1015 	if (w->triangles.size > 1) {
1016 		int         big = w->triangles.sizeSize + NRAND(2);
1017 		int         lastDirection = 0;
1018 		int         randomDirection;
1019 
1020 		cb.reason = TRIANGLES_RESET;
1021 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1022 
1023 		if (w->triangles.size == 2)
1024 			big *= big;
1025 
1026 #ifdef DEBUG
1027 		big = 3;
1028 #endif
1029 
1030 		if (big > 1000)
1031 			big = 1000;
1032 		while (big--) {
1033 			randomDirection = NRAND(COORD);
1034 
1035 #ifdef DEBUG
1036 			sleep(1);
1037 #endif
1038 
1039 			if ((randomDirection + COORD / 2) % COORD != lastDirection) {
1040 				if (MoveTrianglesDir(w, randomDirection))
1041 					lastDirection = randomDirection;
1042 				else
1043 					big++;
1044 			}
1045 		}
1046 		FlushMoves(w);
1047 		cb.reason = TRIANGLES_RANDOMIZE;
1048 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1049 	}
1050 	if (CheckSolved(w)) {
1051 		cb.reason = TRIANGLES_SOLVED;
1052 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1053 	}
1054 }
1055 
1056 static void
MoveTiles(TrianglesWidget w,int from,int orient)1057 MoveTiles(TrianglesWidget w, int from, int orient)
1058 {
1059 	int         tempTile;
1060 
1061 	tempTile = w->triangles.tileOfPosition[from];
1062 	w->triangles.tileOfPosition[from] =
1063 		w->triangles.tileOfPosition[w->triangles.spacePosition[orient]];
1064 	w->triangles.tileOfPosition[w->triangles.spacePosition[orient]] =
1065 		tempTile;
1066 	DrawTile(w, w->triangles.spacePosition[orient], orient, False, False, FALSE);
1067 	w->triangles.spacePosition[orient] = from;
1068 	w->triangles.spaceRow[orient][ROW] = Row(from);
1069 	w->triangles.spaceRow[orient][TRBL] =
1070 		TrBl(from, w->triangles.spaceRow[orient][ROW]) / 2;
1071 	w->triangles.spaceRow[orient][TLBR] =
1072 		TlBr(from, w->triangles.spaceRow[orient][ROW]) / 2;
1073 	DrawTile(w, w->triangles.spacePosition[orient], orient, True, True, FALSE);
1074 }
1075 
1076 static int
ExchangeTiles(TrianglesWidget w,int pos1,int pos2)1077 ExchangeTiles(TrianglesWidget w, int pos1, int pos2)
1078 {
1079 	int         tempTile;
1080 
1081 	if (w->triangles.tileOfPosition[pos1] <= 0)
1082 		return FALSE;
1083 	else if (w->triangles.tileOfPosition[pos2] <= 0)
1084 		return FALSE;
1085 	tempTile = w->triangles.tileOfPosition[pos1];
1086 	w->triangles.tileOfPosition[pos1] = w->triangles.tileOfPosition[pos2];
1087 	w->triangles.tileOfPosition[pos2] = tempTile;
1088 	return TRUE;
1089 }
1090 
1091 static int
TileNextToSpace(TrianglesWidget w,int rowType,int orient,int direction)1092 TileNextToSpace(TrianglesWidget w, int rowType, int orient, int direction)
1093 {
1094 	if (direction == UP) {
1095 		if (rowType == TRBL)
1096 			return ((orient == UP) ? w->triangles.spacePosition[orient] + 1 :
1097 				w->triangles.spacePosition[orient] -
1098 				2 * w->triangles.spaceRow[orient][ROW]);
1099 		else if (rowType == TLBR)
1100 			return ((orient == UP) ? w->triangles.spacePosition[orient] - 1 :
1101 				w->triangles.spacePosition[orient] -
1102 				2 * w->triangles.spaceRow[orient][ROW]);
1103 		else		/* rowType == ROW */
1104 			return (w->triangles.spacePosition[orient] - 1);
1105 	} else {		/* direction == DOWN */
1106 		if (rowType == TRBL)
1107 			return ((orient == DOWN) ? w->triangles.spacePosition[orient] - 1 :
1108 				w->triangles.spacePosition[orient] +
1109 				2 * (w->triangles.spaceRow[orient][ROW] + 1));
1110 		else if (rowType == TLBR)
1111 			return ((orient == DOWN) ? w->triangles.spacePosition[orient] + 1 :
1112 				w->triangles.spacePosition[orient] +
1113 				2 * (w->triangles.spaceRow[orient][ROW] + 1));
1114 		else		/* rowType == ROW */
1115 			return (w->triangles.spacePosition[orient] + 1);
1116 	}
1117 }
1118 
1119 static void
DrawFrame(TrianglesWidget w,GC gc)1120 DrawFrame(TrianglesWidget w, GC gc)
1121 {
1122 	int         sumX, sumY, sumX2, offsetX, offsetY;
1123 
1124 	sumX = w->triangles.size * w->triangles.offset.x + w->triangles.delta.x + 1;
1125 	sumY = w->triangles.size * w->triangles.offset.y + w->triangles.delta.y + 1;
1126 	offsetX = w->triangles.puzzleOffset.x;
1127 	offsetY = w->triangles.puzzleOffset.y;
1128 	sumX2 = sumX / 2 + offsetX;
1129 	sumX += offsetX;
1130 	sumY += offsetY;
1131 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1132 		  sumX2 - 1, offsetY, offsetX, sumY);
1133 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1134 		  sumX2 - 2, offsetY, sumX - 2, sumY);
1135 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1136 		  offsetX, sumY, sumX - 2, sumY);
1137 }
1138 
1139 void
DrawAllTiles(TrianglesWidget w)1140 DrawAllTiles(TrianglesWidget w)
1141 {
1142 	int         k, side = UP, fin = 1, step = 1;
1143 
1144 	for (k = 0; k < w->triangles.sizeSize; k++) {
1145 		DrawTile(w, k, side, (w->triangles.tileOfPosition[k] <= 0), (w->triangles.tileOfPosition[k] <= 0), FALSE);
1146 		if (fin == k + 1) {
1147 			side = UP;
1148 			step += 2;
1149 			fin += step;
1150 		} else
1151 			side = !side;
1152 	}
1153 }
1154 
1155 static void
DrawTile(TrianglesWidget w,int pos,int orient,Boolean blank,Boolean erase,int offset)1156 DrawTile(TrianglesWidget w, int pos, int orient, Boolean blank, Boolean erase, int offset)
1157 {
1158 	int         dx, dy, k = Row(pos);
1159 	GC          tileGC, borderGC;
1160 
1161 	if (erase) {
1162 		tileGC = w->triangles.inverseGC;
1163 		borderGC = w->triangles.inverseGC;
1164 	} else if (offset) {
1165 		tileGC = w->triangles.borderGC;
1166 		borderGC = w->triangles.tileGC;
1167 	} else {
1168 		tileGC = w->triangles.tileGC;
1169 		borderGC = w->triangles.borderGC;
1170 	}
1171 	dy = (orient == UP) ? 0 : w->triangles.tileSize.y;
1172 	dy += k * w->triangles.offset.y + w->triangles.delta.y + 2 +
1173 		w->triangles.puzzleOffset.y + offset;
1174 	dx = (TrBl(pos, k) - k + w->triangles.size) * w->triangles.offset.x / 2 +
1175 		w->triangles.delta.x / 2 + w->triangles.puzzleOffset.x + offset;
1176 	triangleList[orient][0].x = dx;
1177 	triangleList[orient][0].y = dy;
1178 	XFillPolygon(XtDisplay(w), XtWindow(w), tileGC, triangleList[orient], 3,
1179 		     Convex, CoordModePrevious);
1180 	XDrawLines(XtDisplay(w), XtWindow(w), borderGC, triangleList[orient], 4,
1181 		   CoordModePrevious);
1182 	if (!blank) {
1183 		int         i = 0, offsetX = 0, offsetY = 0;
1184 		int         tile = w->triangles.tileOfPosition[pos];
1185 		char        buf[5];
1186 
1187 		(void) int2String(buf, tile, w->triangles.base);
1188 		while (tile >= 1) {
1189 			tile /= w->triangles.base;
1190 			offsetX += w->triangles.digitOffset.x;
1191 			i++;
1192 		}
1193 		offsetY = (orient == UP) ? w->triangles.digitOffset.y +
1194 			2 * w->triangles.delta.y + 2 :
1195 			-w->triangles.digitOffset.y - w->triangles.tileSize.y +
1196 			w->triangles.delta.y - 2;
1197 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1198 			    dx - offsetX, dy + w->triangles.tileSize.y / 2 + offsetY, buf, i);
1199 	}
1200 }
1201 
1202 int
Row(int pos)1203 Row(int pos)
1204 {
1205 	return Sqrt(pos);
1206 }
1207 
1208 /* Passing row so there is no sqrt calculation again */
1209 int
TrBl(int pos,int posRow)1210 TrBl(int pos, int posRow)
1211 {
1212 	return (pos - posRow * posRow);
1213 }
1214 
1215 int
TlBr(int pos,int posRow)1216 TlBr(int pos, int posRow)
1217 {
1218 	return (posRow * posRow + 2 * posRow - pos);
1219 }
1220 
1221 static int
ToOrient(int row,int trbl,int tlbr)1222 ToOrient(int row, int trbl, int tlbr)
1223 {
1224 	return (trbl + tlbr == row);
1225 }
1226 
1227 static int
ToPosition(int row,int trbl,int tlbr)1228 ToPosition(int row, int trbl, int tlbr)
1229 {
1230 	return (row * row + row + trbl - tlbr);
1231 }
1232 
1233 /* This is fast for small i, a -1 is returned for negative i. */
1234 static int
Sqrt(int i)1235 Sqrt(int i)
1236 {
1237 	int         j = 0;
1238 
1239 	while (j * j <= i)
1240 		j++;
1241 	return (j - 1);
1242 }
1243 
1244 static int
int2String(char * buf,int number,int base)1245 int2String(char *buf, int number, int base)
1246 {
1247 	int         digit, mult = base, last, position;
1248 
1249 	if (number < 0) {
1250 		(void) printf("number %d < 0\n", number);
1251 		return 0;
1252 	}
1253 	last = 1;
1254 	while (number >= mult) {
1255 		last++;
1256 		mult *= base;
1257 	}
1258 	for (position = 0; position < last; position++) {
1259 		mult /= base;
1260 		digit = number / mult;
1261 		number -= digit * mult;
1262 		buf[position] = digit + '0';
1263 		if (buf[position] > '9') {	/* ASCII */
1264 			buf[position] += ('A' - '9' - 1);
1265 		} else if (buf[position] < '0') {	/* EBCDIC */
1266 			buf[position] += ('A' - '9' - 1);
1267 			if (buf[position] > 'I')
1268 				buf[position] += ('J' - 'I' - 1);
1269 			if (buf[position] > 'R')
1270 				buf[position] += ('S' - 'R' - 1);
1271 		}
1272 	}
1273 	buf[last] = '\0';
1274 	return last;
1275 }
1276 
1277 Boolean
CheckSolved(TrianglesWidget w)1278 CheckSolved(TrianglesWidget w)
1279 {
1280 	int         i;
1281 
1282 	for (i = 1; i < w->triangles.sizeSize - 1; i++)
1283 		if (w->triangles.tileOfPosition[i - 1] != i)
1284 			return FALSE;
1285 	return TRUE;
1286 }
1287