1 /*-
2 # X-BASED HEXAGONS
3 #
4 #  Hexagons.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 Hexagons */
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 "HexagonsP.h"
42 
43 #ifndef DATAFILE
44 #define DATAFILE "/usr/games/lib/hexagons.data"
45 #endif
46 
47 static void InitializeHexagons(Widget request, Widget renew);
48 static void ExposeHexagons(Widget renew, XEvent * event, Region region);
49 static void ResizeHexagons(HexagonsWidget w);
50 static void DestroyHexagons(Widget old);
51 static Boolean SetValuesHexagons(Widget current, Widget request, Widget renew);
52 static void QuitHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
53 static void MoveHexagonsTl(HexagonsWidget w, XEvent * event, char **args, int nArgs);
54 static void MoveHexagonsTr(HexagonsWidget w, XEvent * event, char **args, int nArgs);
55 static void MoveHexagonsLeft(HexagonsWidget w, XEvent * event, char **args, int nArgs);
56 static void MoveHexagonsRight(HexagonsWidget w, XEvent * event, char **args, int nArgs);
57 static void MoveHexagonsBl(HexagonsWidget w, XEvent * event, char **args, int nArgs);
58 static void MoveHexagonsBr(HexagonsWidget w, XEvent * event, char **args, int nArgs);
59 static void SelectHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
60 static void ReleaseHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
61 static void RandomizeHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
62 static void RandomizeHexagonsMaybe(HexagonsWidget w, XEvent * event, char **args, int nArgs);
63 static void GetHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
64 static void WriteHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
65 static void UndoHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
66 static void SolveHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
67 static void IncrementHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
68 static void DecrementHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
69 static void CornerHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs);
70 static int  MoveHexagons(HexagonsWidget w, int direction);
71 
72 static int  PositionToTile(HexagonsWidget w, int x, int y, int *row);
73 static int  MovableCornerTile(HexagonsWidget w);
74 static int  MovableNoCornTile(HexagonsWidget w);
75 static void SelectCornerTiles(HexagonsWidget w, int space);
76 static void SelectNoCornTiles(HexagonsWidget w);
77 static void SetAllColors(HexagonsWidget w, Boolean init);
78 static void CheckTiles(HexagonsWidget w);
79 static void ResetTiles(HexagonsWidget w);
80 static void ResizeTiles(HexagonsWidget w);
81 static int  MovableCornerTiles(HexagonsWidget w, int direction, int *position, int *posRow, int *space);
82 static void MoveNoTiles(HexagonsWidget w);
83 static int  MoveTilesDir(HexagonsWidget w, int direction);
84 static int  MoveCornerTilesDir(HexagonsWidget w, int direction);
85 static int  MoveNoCornTilesDir(HexagonsWidget w, int direction);
86 static void RandomizeTiles(HexagonsWidget w);
87 static void MoveCornerTiles(HexagonsWidget w, int from, int posRow, int space);
88 static void MoveNoCornTiles(HexagonsWidget w, int from, int posRow);
89 static int  ExchangeTiles(HexagonsWidget w, int pos1, int pos2);
90 
91 #ifdef DEBUG
92 static int  WithinFrame(HexagonsWidget w, int x, int y, int dx, int dy);
93 
94 #endif
95 static int  NextToWall(HexagonsWidget w, int pos, int posRow, int spaceType);
96 static int  TileNextToSpace(HexagonsWidget w, int rowType, int direction);
97 static int  FindTileTriangle(HexagonsWidget w, int pI, int pJ, int pK, int rI, int rJ, int rK);
98 static int  FindDir(HexagonsWidget w, int posTile, int posSpace, int rowTile, int rowSpace);
99 static int  FindSpaceType(HexagonsWidget w, int pos1, int pos2, int row1, int row2);
100 static void FindMovableTile(HexagonsWidget w, int pos, int posRow, int spaceType, int side, int *tilePos, int *tileRow);
101 static void DrawFrame(HexagonsWidget w, GC gc);
102 static void DrawTile(HexagonsWidget w, int pos, Boolean blank, Boolean erase, int offset);
103 
104 #ifdef DEBUG
105 static int  PositionInRow();
106 
107 #endif
108 static int  PositionFromRow(HexagonsWidget w, int rowPosition, int posRow);
109 static int  Sqrt(int i);
110 static int  int2String(char *buf, int number, int base);
111 static void swap(int *a, int *b);
112 
113 static char defaultTranslationsHexagons[] =
114 "<KeyPress>q: Quit()\n\
115    Ctrl<KeyPress>C: Quit()\n\
116    <KeyPress>Home: MoveTl()\n\
117    <KeyPress>KP_7: MoveTl()\n\
118    <KeyPress>R7: MoveTl()\n\
119    <KeyPress>Prior: MoveTr()\n\
120    <KeyPress>KP_9: MoveTr()\n\
121    <KeyPress>R9: MoveTr()\n\
122    <KeyPress>Left: MoveLeft()\n\
123    <KeyPress>KP_4: MoveLeft()\n\
124    <KeyPress>R10: MoveLeft()\n\
125    <KeyPress>Right: MoveRight()\n\
126    <KeyPress>KP_6: MoveRight()\n\
127    <KeyPress>R12: MoveRight()\n\
128    <KeyPress>End: MoveBl()\n\
129    <KeyPress>KP_1: MoveBl()\n\
130    <KeyPress>R13: MoveBl()\n\
131    <KeyPress>Next: MoveBr()\n\
132    <KeyPress>KP_3: MoveBr()\n\
133    <KeyPress>R15: MoveBr()\n\
134    <Btn1Down>: Select()\n\
135    <Btn1Up>: Release()\n\
136    <KeyPress>r: Randomize()\n\
137    <Btn3Down>(2+): Randomize()\n\
138    <Btn3Down>: RandomizeMaybe()\n\
139    <KeyPress>g: Get()\n\
140    <KeyPress>w: Write()\n\
141    <KeyPress>u: Undo()\n\
142    <KeyPress>s: Solve()\n\
143    <KeyPress>i: Increment()\n\
144    <KeyPress>d: Decrement()\n\
145    <KeyPress>c: Corner()";
146 
147 static XtActionsRec actionsListHexagons[] =
148 {
149 	{"Quit", (XtActionProc) QuitHexagons},
150 	{"MoveTl", (XtActionProc) MoveHexagonsTl},
151 	{"MoveTr", (XtActionProc) MoveHexagonsTr},
152 	{"MoveLeft", (XtActionProc) MoveHexagonsLeft},
153 	{"MoveRight", (XtActionProc) MoveHexagonsRight},
154 	{"MoveBl", (XtActionProc) MoveHexagonsBl},
155 	{"MoveBr", (XtActionProc) MoveHexagonsBr},
156 	{"Select", (XtActionProc) SelectHexagons},
157 	{"Release", (XtActionProc) ReleaseHexagons},
158 	{"Randomize", (XtActionProc) RandomizeHexagons},
159 	{"RandomizeMaybe", (XtActionProc) RandomizeHexagonsMaybe},
160 	{"Get", (XtActionProc) GetHexagons},
161 	{"Write", (XtActionProc) WriteHexagons},
162 	{"Undo", (XtActionProc) UndoHexagons},
163 	{"Solve", (XtActionProc) SolveHexagons},
164 	{"Increment", (XtActionProc) IncrementHexagons},
165 	{"Decrement", (XtActionProc) DecrementHexagons},
166 	{"Corner", (XtActionProc) CornerHexagons}
167 };
168 
169 static XtResource resourcesHexagons[] =
170 {
171 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
172 	 XtOffset(HexagonsWidget, hexagons.username), XtRString, "nobody"},
173 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
174 	 XtOffset(HexagonsWidget, hexagons.foreground), XtRString, XtDefaultForeground},
175 	{XtNtileColor, XtCColor, XtRPixel, sizeof (Pixel),
176 XtOffset(HexagonsWidget, hexagons.tileColor), XtRString, XtDefaultForeground},
177 	{XtNtileBorder, XtCColor, XtRPixel, sizeof (Pixel),
178 	 XtOffset(HexagonsWidget, hexagons.borderColor), XtRString, XtDefaultBackground},
179 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
180 	 XtOffset(HexagonsWidget, core.width), XtRString, "259"},
181 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
182 	 XtOffset(HexagonsWidget, core.height), XtRString, "200"},
183 	{XtNsize, XtCSize, XtRInt, sizeof (int),
184 	 XtOffset(HexagonsWidget, hexagons.size), XtRString, "3"},	/* DEFAULTHEXS */
185 	{XtNcorners, XtCCorners, XtRBoolean, sizeof (Boolean),
186 	 XtOffset(HexagonsWidget, hexagons.corners), XtRString, "TRUE"},	/*DEFAULTCORN */
187 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
188 	 XtOffset(HexagonsWidget, hexagons.mono), XtRString, "FALSE"},
189 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
190 	 XtOffset(HexagonsWidget, hexagons.reverse), XtRString, "FALSE"},
191 	{XtNbase, XtCBase, XtRInt, sizeof (int),
192 	 XtOffset(HexagonsWidget, hexagons.base), XtRString, "10"},
193 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
194 	 XtOffset(HexagonsWidget, hexagons.started), XtRString, "FALSE"},
195 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
196 	 XtOffset(HexagonsWidget, hexagons.select), XtRCallback, NULL}
197 };
198 
199 HexagonsClassRec hexagonsClassRec =
200 {
201 	{
202 		(WidgetClass) & widgetClassRec,		/* superclass */
203 		"Hexagons",	/* class name */
204 		sizeof (HexagonsRec),	/* widget size */
205 		NULL,		/* class initialize */
206 		NULL,		/* class part initialize */
207 		FALSE,		/* class inited */
208 		(XtInitProc) InitializeHexagons,	/* initialize */
209 		NULL,		/* initialize hook */
210 		XtInheritRealize,	/* realize */
211 		actionsListHexagons,	/* actions */
212 		XtNumber(actionsListHexagons),	/* num actions */
213 		resourcesHexagons,	/* resources */
214 		XtNumber(resourcesHexagons),	/* num resources */
215 		NULLQUARK,	/* xrm class */
216 		TRUE,		/* compress motion */
217 		TRUE,		/* compress exposure */
218 		TRUE,		/* compress enterleave */
219 		TRUE,		/* visible interest */
220 		(XtWidgetProc) DestroyHexagons,		/* destroy */
221 		(XtWidgetProc) ResizeHexagons,	/* resize */
222 		(XtExposeProc) ExposeHexagons,	/* expose */
223 		(XtSetValuesFunc) SetValuesHexagons,	/* set values */
224 		NULL,		/* set values hook */
225 		XtInheritSetValuesAlmost,	/* set values almost */
226 		NULL,		/* get values hook */
227 		NULL,		/* accept focus */
228 		XtVersion,	/* version */
229 		NULL,		/* callback private */
230 		defaultTranslationsHexagons,	/* tm table */
231 		NULL,		/* query geometry */
232 		NULL,		/* display accelerator */
233 		NULL		/* extension */
234 	},
235 	{
236 		0		/* ignore */
237 	}
238 };
239 
240 WidgetClass hexagonsWidgetClass = (WidgetClass) & hexagonsClassRec;
241 
242 static XPoint hexagonUnit[MAXORIENT][7] =
243 {
244 	{
245 		{0, 0},
246 		{2, 0},
247 		{1, 1},
248 		{-1, 1},
249 		{-2, 0},
250 		{-1, -1},
251 		{1, -1}},
252 	{
253 		{0, 0},
254 		{1, 1},
255 		{0, 2},
256 		{-1, 1},
257 		{-1, -1},
258 		{0, -2},
259 		{1, -1}}
260 };
261 static XPoint hexagonList[MAXORIENT][7];
262 
263 #ifndef HAVE_USLEEP
264 #if !defined( VMS ) || defined( XVMSUTILS ) ||  ( __VMS_VER >= 70000000 )
265 #ifdef USE_XVMSUTILS
266 #include <X11/unix_time.h>
267 #endif
268 #if HAVE_SYS_TIME_H
269 #include <sys/time.h>
270 #else
271 #if HAVE_SYS_SELECT_H
272 #include <sys/select.h>
273 #endif
274 #endif
275 #endif
276 #if defined(SYSV) || defined(SVR4)
277 #ifdef LESS_THAN_AIX3_2
278 #include <sys/poll.h>
279 #else /* !LESS_THAN_AIX3_2 */
280 #include <poll.h>
281 #endif /* !LESS_THAN_AIX3_2 */
282 #endif /* defined(SYSV) || defined(SVR4) */
283 
284 static int
usleep(unsigned int usec)285 usleep(unsigned int usec)
286 {
287 #if (defined (SYSV) || defined(SVR4)) && !defined(__hpux)
288 #if defined(HAVE_NANOSLEEP)
289 	{
290 		struct timespec rqt;
291 
292 		rqt.tv_nsec = 1000 * (usec % (unsigned int) 1000000);
293 		rqt.tv_sec = usec / (unsigned int) 1000000;
294 		return nanosleep(&rqt, NULL);
295 	}
296 #else
297 	(void) poll((void *) 0, (int) 0, usec / 1000);	/* ms resolution */
298 #endif
299 #else
300 #ifdef VMS
301 	long        timadr[2];
302 
303 	if (usec != 0) {
304 		timadr[0] = -usec * 10;
305 		timadr[1] = -1;
306 
307 		sys$setimr(4, &timadr, 0, 0, 0);
308 		sys$waitfr(4);
309 	}
310 #else
311 	struct timeval time_out;
312 
313 #if  0
314 	/* (!defined(AIXV3) && !defined(__hpux)) */
315 	extern int  select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
316 
317 #endif
318 
319 	time_out.tv_usec = usec % (unsigned int) 1000000;
320 	time_out.tv_sec = usec / (unsigned int) 1000000;
321 	(void) select(0, (void *) 0, (void *) 0, (void *) 0, &time_out);
322 #endif
323 #endif
324 	return 0;
325 }
326 #endif
327 
328 static void
Sleep(unsigned int cMilliseconds)329 Sleep(unsigned int cMilliseconds)
330 {
331 	(void) usleep(cMilliseconds * 1000);
332 }
333 
334 static void
InitializeHexagons(Widget request,Widget renew)335 InitializeHexagons(Widget request, Widget renew)
336 {
337 	HexagonsWidget w = (HexagonsWidget) renew;
338 
339 	w->hexagons.tileOfPosition = NULL;
340 	CheckTiles(w);
341 	InitMoves();
342 	ResetTiles(w);
343 	(void) SRAND(getpid());
344 	w->hexagons.depth = DefaultDepthOfScreen(XtScreen(w));
345 	SetAllColors(w, True);
346 	ResizeHexagons(w);
347 }
348 
349 static void
DestroyHexagons(Widget old)350 DestroyHexagons(Widget old)
351 {
352 	HexagonsWidget w = (HexagonsWidget) old;
353 
354 	XtReleaseGC(old, w->hexagons.tileGC);
355 	XtReleaseGC(old, w->hexagons.borderGC);
356 	XtReleaseGC(old, w->hexagons.puzzleGC);
357 	XtReleaseGC(old, w->hexagons.inverseGC);
358 	XtRemoveCallbacks(old, XtNselectCallback, w->hexagons.select);
359 }
360 
361 static void
ResizeHexagons(HexagonsWidget w)362 ResizeHexagons(HexagonsWidget w)
363 {
364 	double      sqrt_3 = 1.73205080756887729352744634150587237;
365 	XPoint      tempSize;
366 
367 	w->hexagons.delta.x = 2;
368 	w->hexagons.delta.y = 2;
369 	w->hexagons.tileSize.x = MAX((2 * ((int) w->core.width +
370 			2 * w->hexagons.delta.x - 1) - 4 * w->hexagons.size *
371 		       w->hexagons.delta.x) / (4 * w->hexagons.size - 1), 0);
372 	w->hexagons.tileSize.y = MAX(((int) w->core.height - 1 -
373 				2 * w->hexagons.size * w->hexagons.delta.y) /
374 				     (3 * w->hexagons.size - 1), 0);
375 	w->hexagons.offset.x = w->hexagons.tileSize.x + w->hexagons.delta.x;
376 	w->hexagons.offset.y = w->hexagons.tileSize.y + 2 * w->hexagons.delta.y;
377 	tempSize.y = (int) ((double) w->hexagons.offset.x / sqrt_3);
378 	tempSize.x = (int) ((double) w->hexagons.offset.y * sqrt_3);
379 	if (tempSize.y < w->hexagons.offset.y) {
380 		w->hexagons.offset.x = w->hexagons.tileSize.x + w->hexagons.delta.x;
381 		w->hexagons.offset.y = tempSize.y;
382 	} else {		/* tempSize.x <=  w->hexagons.offset.x */
383 		w->hexagons.offset.x = tempSize.x;
384 		w->hexagons.offset.y = w->hexagons.tileSize.y + 2 * w->hexagons.delta.y;
385 	}
386 	w->hexagons.tileSize.x = MAX(w->hexagons.offset.x - w->hexagons.delta.x, 0);
387 	w->hexagons.tileSize.y = MAX(w->hexagons.offset.y - 2 * w->hexagons.delta.y,
388 				     0);
389 	w->hexagons.puzzleSize.x = w->hexagons.size * 2 * w->hexagons.offset.x -
390 		w->hexagons.tileSize.x / 2 - 2 * w->hexagons.delta.x + 1;
391 	w->hexagons.puzzleSize.y = w->hexagons.size * (3 * w->hexagons.tileSize.y +
392 		       2 * w->hexagons.delta.y) - w->hexagons.tileSize.y + 2;
393 	w->hexagons.puzzleOffset.x =
394 		((int) w->core.width - w->hexagons.puzzleSize.x + 2) / 2;
395 	w->hexagons.puzzleOffset.y =
396 		((int) w->core.height - w->hexagons.puzzleSize.y + 2) / 2;
397 	ResizeTiles(w);
398 }
399 
400 static void
ExposeHexagons(Widget renew,XEvent * event,Region region)401 ExposeHexagons(Widget renew, XEvent * event, Region region)
402 {
403 	HexagonsWidget w = (HexagonsWidget) renew;
404 
405 	if (w->core.visible) {
406 		if (w->hexagons.reverse)
407 			XFillRectangle(XtDisplay(w), XtWindow(w),
408 				       w->hexagons.inverseGC, 0, 0, w->core.width, w->core.height);
409 
410 		DrawFrame(w, w->hexagons.puzzleGC);
411 		DrawAllTiles(w);
412 	}
413 }
414 
415 static      Boolean
SetValuesHexagons(Widget current,Widget request,Widget renew)416 SetValuesHexagons(Widget current, Widget request, Widget renew)
417 {
418 	HexagonsWidget c = (HexagonsWidget) current, w = (HexagonsWidget) renew;
419 	Boolean     redraw = FALSE;
420 	Boolean     redrawTiles = FALSE;
421 
422 	CheckTiles(w);
423 	if (w->core.background_pixel != c->core.background_pixel ||
424 	    w->hexagons.foreground != c->hexagons.foreground ||
425 	    w->hexagons.borderColor != c->hexagons.borderColor ||
426 	    w->hexagons.tileColor != c->hexagons.tileColor ||
427 	    w->hexagons.reverse != c->hexagons.reverse ||
428 	    w->hexagons.mono != c->hexagons.mono) {
429 		SetAllColors(w, False);
430 		redrawTiles = TRUE;
431 	}
432 	if (w->hexagons.size != c->hexagons.size ||
433 	    w->hexagons.corners != c->hexagons.corners ||
434 	    w->hexagons.base != c->hexagons.base) {
435 		ResetTiles(w);
436 		ResizeHexagons(w);
437 		redraw = TRUE;
438 	} else if (w->hexagons.offset.x != c->hexagons.offset.x ||
439 		   w->hexagons.offset.y != c->hexagons.offset.y) {
440 		ResizeHexagons(w);
441 		redraw = TRUE;
442 	}
443 	if (redrawTiles && !redraw && XtIsRealized(renew) && renew->core.visible) {
444 		DrawFrame(c, c->hexagons.inverseGC);
445 		DrawFrame(w, w->hexagons.puzzleGC);
446 		DrawAllTiles(w);
447 	}
448 	return (redraw);
449 }
450 
451 static void
QuitHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)452 QuitHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
453 {
454 	XtCloseDisplay(XtDisplay(w));
455 	exit(0);
456 }
457 
458 static void
SelectHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)459 SelectHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
460 {
461 	int         pos, row, rowType;
462 
463 	pos = PositionToTile(w, event->xbutton.x, event->xbutton.y, &row);
464 	if (pos >= 0) {
465 		if (CheckSolved(w)) {
466 			MoveNoTiles(w);
467 			w->hexagons.currentPosition = -1;
468 			return;
469 		}
470 		w->hexagons.currentPosition = PositionFromRow(w, pos, row);
471 		w->hexagons.currentRow[ROW] = row;
472 		w->hexagons.currentRow[TRBL] = TrBl(w, w->hexagons.currentPosition, row);
473 		w->hexagons.currentRow[TLBR] = TlBr(w, w->hexagons.currentPosition, row);
474 		if (w->hexagons.corners)
475 			rowType = MovableCornerTile(w);
476 		else
477 			rowType = MovableNoCornTile(w);
478 		if (rowType < 0) {
479 			hexagonsCallbackStruct cb;
480 
481 			DrawTile(w, w->hexagons.currentPosition, rowType == HEXAGONS_SPACE, False, TRUE);
482 			XFlush(XtDisplay(w));
483 			Sleep(100);
484 			DrawTile(w, w->hexagons.currentPosition, True, True, TRUE);
485 			if (rowType != HEXAGONS_SPACE)
486 				DrawTile(w, w->hexagons.currentPosition, False, False, FALSE);
487 			cb.reason = rowType;
488 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
489 			w->hexagons.currentPosition = -1;
490 			return;
491 		}
492 		DrawTile(w, w->hexagons.currentPosition, False, False, TRUE);
493 	} else
494 		w->hexagons.currentPosition = -1;
495 }
496 
497 static void
ReleaseHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)498 ReleaseHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
499 {
500 	int         pos, row, space;
501 
502 	if (w->hexagons.currentPosition == -1)
503 		return;
504 	DrawTile(w, w->hexagons.currentPosition, True, True, TRUE);
505 	DrawTile(w, w->hexagons.currentPosition, False, False, FALSE);
506 	if (!w->hexagons.corners) {
507 		SelectNoCornTiles(w);
508 		w->hexagons.currentPosition = -1;
509 		return;
510 	}
511 	pos = PositionToTile(w, event->xbutton.x, event->xbutton.y, &row);
512 	if (pos >= 0) {
513 		pos = PositionFromRow(w, pos, row);
514 		if (w->hexagons.spacePosition[HIGH] == pos)
515 			space = (w->hexagons.spacePosition[HIGH] >
516 				 w->hexagons.spacePosition[LOW]);
517 		else if (w->hexagons.spacePosition[LOW] == pos)
518 			space = (w->hexagons.spacePosition[HIGH] <
519 				 w->hexagons.spacePosition[LOW]);
520 		else {
521 			w->hexagons.currentPosition = -1;
522 			return;
523 		}
524 		SelectCornerTiles(w, space);
525 	}
526 	w->hexagons.currentPosition = -1;
527 }
528 
529 static void
RandomizeHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)530 RandomizeHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
531 {
532 	RandomizeTiles(w);
533 }
534 
535 static void
RandomizeHexagonsMaybe(HexagonsWidget w,XEvent * event,char ** args,int nArgs)536 RandomizeHexagonsMaybe(HexagonsWidget w, XEvent * event, char **args, int nArgs)
537 {
538 	if (!w->hexagons.started)
539 		RandomizeTiles(w);
540 }
541 
542 static void
GetHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)543 GetHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
544 {
545 	FILE       *fp;
546 	char        c;
547 	int         i, size, corners, moves;
548 	hexagonsCallbackStruct cb;
549 
550 	if ((fp = fopen(DATAFILE, "r")) == NULL)
551 		(void) printf("Can not read %s for get.\n", DATAFILE);
552 	else {
553 		FlushMoves(w);
554 		while ((c = getc(fp)) != EOF && c != SYMBOL);
555 		(void) fscanf(fp, "%d", &size);
556 		if (size >= MINHEXAGONS) {
557 			for (i = w->hexagons.size; i < size; i++) {
558 				cb.reason = HEXAGONS_INC;
559 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
560 			}
561 			for (i = w->hexagons.size; i > size; i--) {
562 				cb.reason = HEXAGONS_DEC;
563 				XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
564 			}
565 		} else
566 			(void) printf("%s corrupted: size %d should be between %d and MAXINT\n",
567 				      DATAFILE, size, MINHEXAGONS);
568 		while ((c = getc(fp)) != EOF && c != SYMBOL);
569 		(void) fscanf(fp, "%d", &corners);
570 		if (w->hexagons.corners != (Boolean) corners) {
571 			cb.reason = HEXAGONS_CORNERS;
572 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
573 		}
574 		while ((c = getc(fp)) != EOF && c != SYMBOL);
575 		(void) fscanf(fp, "%d", &moves);
576 		ScanStartPosition(fp, w);
577 		cb.reason = HEXAGONS_RESTORE;
578 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
579 		SetStartPosition(w);
580 		ScanMoves(fp, w, moves);
581 		(void) fclose(fp);
582 		(void) printf("%s: size %d, corners %d, moves %d.\n",
583 			      DATAFILE, size, corners, moves);
584 	}
585 }
586 
587 static void
WriteHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)588 WriteHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
589 {
590 	FILE       *fp;
591 
592 	if ((fp = fopen(DATAFILE, "w")) == NULL)
593 		(void) printf("Can not write to %s.\n", DATAFILE);
594 	else {
595 		(void) fprintf(fp, "size%c %d\n", SYMBOL, w->hexagons.size);
596 		(void) fprintf(fp, "corners%c %d\n", SYMBOL, (w->hexagons.corners) ? 1 : 0);
597 		(void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
598 		PrintStartPosition(fp, w);
599 		PrintMoves(fp);
600 		(void) fclose(fp);
601 		(void) printf("Saved to %s.\n", DATAFILE);
602 	}
603 }
604 
605 static void
UndoHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)606 UndoHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
607 {
608 	if (MadeMoves()) {
609 		int         direction;
610 
611 		GetMove(&direction);
612 		direction = (direction + (COORD / 2)) % COORD;
613 		if (MoveTilesDir(w, direction)) {
614 			hexagonsCallbackStruct cb;
615 
616 			cb.reason = HEXAGONS_UNDO;
617 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
618 		}
619 	}
620 }
621 
622 static void
SolveHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)623 SolveHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
624 {
625 #if 0
626 	SolveTiles(w);		/* Sorry, this is not implemented */
627 #endif
628 }
629 
630 static void
IncrementHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)631 IncrementHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
632 {
633 	hexagonsCallbackStruct cb;
634 
635 	cb.reason = HEXAGONS_INC;
636 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
637 }
638 
639 static void
DecrementHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)640 DecrementHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
641 {
642 	hexagonsCallbackStruct cb;
643 
644 	if (w->hexagons.size <= MINHEXAGONS)
645 		return;
646 	cb.reason = HEXAGONS_DEC;
647 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
648 }
649 
650 static void
CornerHexagons(HexagonsWidget w,XEvent * event,char ** args,int nArgs)651 CornerHexagons(HexagonsWidget w, XEvent * event, char **args, int nArgs)
652 {
653 	hexagonsCallbackStruct cb;
654 
655 	cb.reason = HEXAGONS_CORNERS;
656 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
657 }
658 
659 static void
MoveHexagonsTl(HexagonsWidget w,XEvent * event,char ** args,int nArgs)660 MoveHexagonsTl(HexagonsWidget w, XEvent * event, char **args, int nArgs)
661 {
662 	(void) MoveHexagons(w, TL);
663 }
664 
665 static void
MoveHexagonsTr(HexagonsWidget w,XEvent * event,char ** args,int nArgs)666 MoveHexagonsTr(HexagonsWidget w, XEvent * event, char **args, int nArgs)
667 {
668 	(void) MoveHexagons(w, TR);
669 }
670 
671 static void
MoveHexagonsLeft(HexagonsWidget w,XEvent * event,char ** args,int nArgs)672 MoveHexagonsLeft(HexagonsWidget w, XEvent * event, char **args, int nArgs)
673 {
674 	(void) MoveHexagons(w, LEFT);
675 }
676 
677 static void
MoveHexagonsRight(HexagonsWidget w,XEvent * event,char ** args,int nArgs)678 MoveHexagonsRight(HexagonsWidget w, XEvent * event, char **args, int nArgs)
679 {
680 	(void) MoveHexagons(w, RIGHT);
681 }
682 
683 static void
MoveHexagonsBl(HexagonsWidget w,XEvent * event,char ** args,int nArgs)684 MoveHexagonsBl(HexagonsWidget w, XEvent * event, char **args, int nArgs)
685 {
686 	(void) MoveHexagons(w, BL);
687 }
688 
689 static void
MoveHexagonsBr(HexagonsWidget w,XEvent * event,char ** args,int nArgs)690 MoveHexagonsBr(HexagonsWidget w, XEvent * event, char **args, int nArgs)
691 {
692 	(void) MoveHexagons(w, BR);
693 }
694 
695 static int
MoveHexagons(HexagonsWidget w,int direction)696 MoveHexagons(HexagonsWidget w, int direction)
697 {
698 	hexagonsCallbackStruct cb;
699 
700 	if (CheckSolved(w)) {
701 		MoveNoTiles(w);
702 		return FALSE;
703 	}
704 	if (!MoveHexagonsDir(w, direction)) {
705 		cb.reason = HEXAGONS_BLOCKED;
706 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
707 		return FALSE;
708 	}
709 	if (CheckSolved(w)) {
710 		cb.reason = HEXAGONS_SOLVED;
711 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
712 	}
713 	return TRUE;
714 }
715 
716 int
MoveHexagonsDir(HexagonsWidget w,int direction)717 MoveHexagonsDir(HexagonsWidget w, int direction)
718 {
719 	hexagonsCallbackStruct cb;
720 
721 	if (MoveTilesDir(w, direction)) {
722 		cb.reason = HEXAGONS_MOVED;
723 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
724 		PutMove(direction);
725 		return TRUE;
726 	}
727 	return FALSE;
728 }
729 
730 static int
PositionToTile(HexagonsWidget w,int x,int y,int * row)731 PositionToTile(HexagonsWidget w, int x, int y, int *row)
732 {
733 	int         i, j, k, modI, modJ;
734 
735 	x -= w->hexagons.puzzleOffset.x;
736 	y -= w->hexagons.puzzleOffset.y;
737 	/* First convert x and y coordinates to hexagon grid.  Keep in mind that
738 	   the starting hexagon x position changes with "w->hexagons.size % 2". */
739 	if (x < w->hexagons.tileSize.x / 4)
740 		return -1;
741 	i = 2 * (x - w->hexagons.tileSize.x / 4) / w->hexagons.offset.x;
742 	j = 3 * (y - w->hexagons.delta.y) /
743 		(3 * w->hexagons.tileSize.y / 2 + w->hexagons.delta.y);
744 	modI = 2 * (x - w->hexagons.tileSize.x / 4) % w->hexagons.offset.x;
745 	modJ = 3 * (y - w->hexagons.delta.y) %
746 		(3 * w->hexagons.tileSize.y / 2 + w->hexagons.delta.y);
747 	*row = j / 3;		/* Approximate to a rectangle just for now */
748 	if (j % 3 == 0) {	/* Then it is the triangle near bottom or top point */
749 		if ((w->hexagons.size - 1 + *row + i) % 2)	/* \ */
750 			*row -= (modJ * w->hexagons.offset.x < modI *
751 				 (3 * w->hexagons.tileSize.y / 2 + w->hexagons.delta.y));
752 		else		/* / */
753 			*row -= (modJ * w->hexagons.offset.x < (w->hexagons.offset.x - modI) *
754 				 (3 * w->hexagons.tileSize.y / 2 + w->hexagons.delta.y));
755 	}
756 	if (i < (w->hexagons.size - 1 + *row) % 2 || *row < 0 ||
757 	    *row > 2 * (w->hexagons.size - 1))
758 		return -1;
759 	k = (i - ((w->hexagons.size - 1 + *row) % 2)) / 2;
760 	/* Map the hexagon grid to hexagon position in puzzle. */
761 	i = (*row < w->hexagons.size) ?
762 		k - (w->hexagons.size - 1 - *row) / 2 :
763 		k + (w->hexagons.size - 1 - *row) / 2;
764 	j = (*row < w->hexagons.size) ?
765 		w->hexagons.size - 1 + *row : 3 * (w->hexagons.size - 1) - *row;
766 	if (i < 0 || i > j)
767 		return -1;
768 	return i;
769 }
770 
771 static int
MovableCornerTile(HexagonsWidget w)772 MovableCornerTile(HexagonsWidget w)
773 {
774 	/*   (Clicked on a space?). */
775 	if (w->hexagons.currentPosition == w->hexagons.spacePosition[LOW] ||
776 	    w->hexagons.currentPosition == w->hexagons.spacePosition[HIGH]) {
777 		return HEXAGONS_SPACE;
778 	}
779 	if (FindTileTriangle(w,
780 		w->hexagons.currentPosition, w->hexagons.spacePosition[HIGH],
781 		 w->hexagons.spacePosition[LOW], w->hexagons.currentRow[ROW],
782 		    w->hexagons.spaceRow[HIGH], w->hexagons.spaceRow[LOW])) {
783 		return 0;
784 	}
785 	return HEXAGONS_BLOCKED;
786 }
787 
788 static int
MovableNoCornTile(HexagonsWidget w)789 MovableNoCornTile(HexagonsWidget w)
790 {
791 	int         rowType = HEXAGONS_BLOCKED, l;
792 
793 	/* Are the spaces in a "row" with the mouse click?
794 	   (If two, then one clicked on a space). */
795 	for (l = 0; l < ROWTYPES; l++) {
796 		if (w->hexagons.currentRow[l] == w->hexagons.spaceRow[l]) {
797 			if (rowType == HEXAGONS_BLOCKED) {
798 				rowType = l;
799 			} else {
800 				return HEXAGONS_SPACE;
801 			}
802 		}
803 	}
804 	return rowType;
805 }
806 
807 static void
SelectCornerTiles(HexagonsWidget w,int space)808 SelectCornerTiles(HexagonsWidget w, int space)
809 {
810 	int         rowType, orient;
811 	hexagonsCallbackStruct cb;
812 
813 	rowType = MovableCornerTile(w);
814 	if (rowType < 0) {
815 		(void) printf("SelectCornerTiles: rowType %d\n", rowType);
816 		return;
817 	}
818 	orient = (w->hexagons.spacePosition[HIGH] <
819 		  w->hexagons.spacePosition[LOW]) ? !space : space;
820 	PutMove(FindDir(w,
821 	      w->hexagons.currentPosition, w->hexagons.spacePosition[orient],
822 		 w->hexagons.currentRow[ROW], w->hexagons.spaceRow[orient]));
823 	MoveCornerTiles(w, w->hexagons.currentPosition,
824 			w->hexagons.currentRow[ROW], orient);
825 	cb.reason = HEXAGONS_MOVED;
826 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
827 	if (CheckSolved(w)) {
828 		cb.reason = HEXAGONS_SOLVED;
829 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
830 	}
831 }
832 
833 static void
SelectNoCornTiles(HexagonsWidget w)834 SelectNoCornTiles(HexagonsWidget w)
835 {
836 	hexagonsCallbackStruct cb;
837 	int         rowType, orient;
838 
839 	rowType = MovableNoCornTile(w);
840 	if (rowType < 0) {
841 		(void) printf("SelectNoCornTiles: rowType %d\n", rowType);
842 		return;
843 	}
844 	if (w->hexagons.currentPosition < w->hexagons.spacePosition[HIGH]) {
845 		while (w->hexagons.currentPosition < w->hexagons.spacePosition[HIGH]) {
846 			orient = (rowType == ROW) ?
847 				w->hexagons.spaceRow[ROW] : w->hexagons.spaceRow[ROW] - 1;
848 			MoveNoCornTiles(w, TileNextToSpace(w, rowType, HIGH), orient);
849 			cb.reason = HEXAGONS_MOVED;
850 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
851 			switch (rowType) {
852 				case TLBR:
853 					PutMove(BR);
854 					break;
855 				case TRBL:
856 					PutMove(BL);
857 					break;
858 				case ROW:
859 					PutMove(RIGHT);
860 					break;
861 				default:
862 					(void) printf("SelectNoCornTiles: rowType %d\n", rowType);
863 			}
864 		}
865 	} else {		/*w->hexagons.currentPosition > w->hexagons.spacePosition[HIGH] */
866 		while (w->hexagons.currentPosition > w->hexagons.spacePosition[HIGH]) {
867 			orient = (rowType == ROW) ?
868 				w->hexagons.spaceRow[ROW] : w->hexagons.spaceRow[ROW] + 1;
869 			MoveNoCornTiles(w, TileNextToSpace(w, rowType, LOW), orient);
870 			cb.reason = HEXAGONS_MOVED;
871 			XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
872 			switch (rowType) {
873 				case TLBR:
874 					PutMove(TL);
875 					break;
876 				case TRBL:
877 					PutMove(TR);
878 					break;
879 				case ROW:
880 					PutMove(LEFT);
881 					break;
882 				default:
883 					(void) printf("SelectNoCornTiles: rowType %d\n", rowType);
884 			}
885 		}
886 	}
887 	if (CheckSolved(w)) {
888 		cb.reason = HEXAGONS_SOLVED;
889 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
890 	}
891 }
892 
893 static void
SetAllColors(HexagonsWidget w,Boolean init)894 SetAllColors(HexagonsWidget w, Boolean init)
895 {
896 	XGCValues   values;
897 	XtGCMask    valueMask;
898 
899 	valueMask = GCForeground | GCBackground;
900 	if (w->hexagons.reverse) {
901 		values.foreground = w->hexagons.foreground;
902 		values.background = w->core.background_pixel;
903 	} else {
904 		values.foreground = w->core.background_pixel;
905 		values.background = w->hexagons.foreground;
906 	}
907 	if (!init)
908 		XtReleaseGC((Widget) w, w->hexagons.inverseGC);
909 	w->hexagons.inverseGC = XtGetGC((Widget) w, valueMask, &values);
910 	if (w->hexagons.reverse) {
911 		values.foreground = w->core.background_pixel;
912 		values.background = w->hexagons.foreground;
913 	} else {
914 		values.foreground = w->hexagons.foreground;
915 		values.background = w->core.background_pixel;
916 	}
917 	if (!init)
918 		XtReleaseGC((Widget) w, w->hexagons.puzzleGC);
919 	w->hexagons.puzzleGC = XtGetGC((Widget) w, valueMask, &values);
920 	if (w->hexagons.depth < 2 || w->hexagons.mono) {
921 		if (w->hexagons.reverse) {
922 			values.foreground = w->core.background_pixel;
923 			values.background = w->hexagons.foreground;
924 		} else {
925 			values.foreground = w->hexagons.foreground;
926 			values.background = w->core.background_pixel;
927 		}
928 	} else {
929 		values.foreground = w->hexagons.tileColor;
930 		values.background = w->hexagons.borderColor;
931 	}
932 	if (!init)
933 		XtReleaseGC((Widget) w, w->hexagons.tileGC);
934 	w->hexagons.tileGC = XtGetGC((Widget) w, valueMask, &values);
935 	if (w->hexagons.depth < 2 || w->hexagons.mono) {
936 		if (w->hexagons.reverse) {
937 			values.foreground = w->hexagons.foreground;
938 			values.background = w->core.background_pixel;
939 		} else {
940 			values.foreground = w->core.background_pixel;
941 			values.background = w->hexagons.foreground;
942 		}
943 	} else {
944 		values.foreground = w->hexagons.borderColor;
945 		values.background = w->hexagons.tileColor;
946 	}
947 	if (!init)
948 		XtReleaseGC((Widget) w, w->hexagons.borderGC);
949 	w->hexagons.borderGC = XtGetGC((Widget) w, valueMask, &values);
950 }
951 
952 static void
CheckTiles(HexagonsWidget w)953 CheckTiles(HexagonsWidget w)
954 {
955 	char        buf[121];
956 
957 	if (w->hexagons.size < MINHEXAGONS) {
958 		(void) sprintf(buf,
959 		"Number of Hexagons on a edge out of bounds, use %d..MAXINT",
960 			       MINHEXAGONS);
961 		XtWarning(buf);
962 		w->hexagons.size = DEFAULTHEXAGONS;
963 	}
964 	if (w->hexagons.base > 36) {
965 		/* 10 numbers + 26 letters (ASCII or EBCDIC) */
966 		XtWarning("Base must be less than or equal to 36");
967 		w->hexagons.base = 10;
968 	} else if (w->hexagons.base <= 1) {	/* Base 1 is rediculous :) */
969 		XtWarning("Base must be greater than 1");
970 		w->hexagons.base = 10;
971 	}
972 }
973 
974 static void
ResetTiles(HexagonsWidget w)975 ResetTiles(HexagonsWidget w)
976 {
977 	int         i;
978 
979 	w->hexagons.sizeSize = 3 * w->hexagons.size * (w->hexagons.size - 1) + 1;
980 	w->hexagons.sizeCenter = (w->hexagons.sizeSize - 1) / 2;
981 	if (w->hexagons.tileOfPosition)
982 		(void) free((void *) w->hexagons.tileOfPosition);
983 	if (!(w->hexagons.tileOfPosition = (int *)
984 	      malloc(sizeof (int) * w->hexagons.sizeSize)))
985 		            XtError("Not enough memory, exiting.");
986 
987 	if (startPosition)
988 		(void) free((void *) startPosition);
989 	if (!(startPosition = (int *)
990 	      malloc(sizeof (int) * w->hexagons.sizeSize)))
991 		            XtError("Not enough memory, exiting.");
992 
993 	w->hexagons.spacePosition[HIGH] = w->hexagons.sizeSize - 1;
994 	if (w->hexagons.corners) {
995 		w->hexagons.spaceRow[HIGH] = 2 * w->hexagons.size - 2;
996 		if (w->hexagons.size > 1) {
997 			w->hexagons.spacePosition[LOW] = w->hexagons.sizeSize - 2;
998 			w->hexagons.spaceRow[LOW] = 2 * w->hexagons.size - 2;
999 			w->hexagons.tileOfPosition[w->hexagons.sizeSize - 2] = -1;
1000 		}
1001 	} else {
1002 		w->hexagons.spaceRow[ROW] = w->hexagons.spaceRow[TRBL] =
1003 			2 * w->hexagons.size - 2;
1004 		w->hexagons.spaceRow[TLBR] = w->hexagons.size - 1;
1005 	}
1006 	w->hexagons.tileOfPosition[w->hexagons.sizeSize - 1] = 0;
1007 	for (i = 1; i < w->hexagons.sizeSize - ((w->hexagons.corners) ? 1 : 0);
1008 	     i++)
1009 		w->hexagons.tileOfPosition[i - 1] = i;
1010 	FlushMoves(w);
1011 	w->hexagons.currentPosition = -1;
1012 	w->hexagons.started = FALSE;
1013 }
1014 
1015 static void
ResizeTiles(HexagonsWidget w)1016 ResizeTiles(HexagonsWidget w)
1017 {
1018 	int         i;
1019 
1020 	for (i = 0; i <= 6; i++) {
1021 		hexagonList[NOCORN][i].x = w->hexagons.tileSize.x *
1022 			hexagonUnit[NOCORN][i].x / 4;
1023 		hexagonList[NOCORN][i].y = 3 * w->hexagons.tileSize.y *
1024 			hexagonUnit[NOCORN][i].y / 4;
1025 		hexagonList[CORNERS][i].x = w->hexagons.tileSize.x *
1026 			hexagonUnit[CORNERS][i].x / 2;
1027 		hexagonList[CORNERS][i].y = w->hexagons.tileSize.y *
1028 			hexagonUnit[CORNERS][i].y / 2;
1029 	}
1030 	w->hexagons.digitOffset.x = 3;
1031 	w->hexagons.digitOffset.y = 4;
1032 }
1033 
1034 static int
MovableCornerTiles(HexagonsWidget w,int direction,int * position,int * posRow,int * space)1035 MovableCornerTiles(HexagonsWidget w, int direction, int *position, int *posRow, int *space)
1036 {
1037 	int         spaceType, highest, side = -1;
1038 
1039 	highest = (w->hexagons.spacePosition[HIGH] >
1040 		   w->hexagons.spacePosition[LOW]) ? HIGH : LOW;
1041 	spaceType = FindSpaceType(w,
1042 	     w->hexagons.spacePosition[HIGH], w->hexagons.spacePosition[LOW],
1043 		      w->hexagons.spaceRow[HIGH], w->hexagons.spaceRow[LOW]);
1044 	switch (spaceType) {
1045 		case TRBL:
1046 			if (direction == TR || direction == BL)
1047 				return FALSE;
1048 			side = NextToWall(w, w->hexagons.spacePosition[highest],
1049 				   w->hexagons.spaceRow[highest], spaceType);
1050 			if (side != -1) {
1051 				if ((side == HIGH && direction == RIGHT) ||
1052 				    (side == HIGH && direction == BR) ||
1053 				    (side == LOW && direction == LEFT) ||
1054 				    (side == LOW && direction == TL))
1055 					return FALSE;
1056 			} else
1057 				side = (direction == TL || direction == LEFT);
1058 			*space = (direction == BR || direction == LEFT);
1059 			break;
1060 		case TLBR:
1061 			if (direction == TL || direction == BR)
1062 				return FALSE;
1063 			side = NextToWall(w, w->hexagons.spacePosition[highest],
1064 				   w->hexagons.spaceRow[highest], spaceType);
1065 			if (side != -1) {
1066 				if ((side == LOW && direction == TR) ||
1067 				    (side == LOW && direction == RIGHT) ||
1068 				    (side == HIGH && direction == BL) ||
1069 				    (side == HIGH && direction == LEFT))
1070 					return FALSE;
1071 			} else
1072 				side = (direction == TR || direction == RIGHT);
1073 			*space = (direction == RIGHT || direction == BL);
1074 			break;
1075 		case ROW:
1076 			if (direction == LEFT || direction == RIGHT)
1077 				return FALSE;
1078 			side = NextToWall(w, w->hexagons.spacePosition[highest],
1079 				   w->hexagons.spaceRow[highest], spaceType);
1080 			if (side != -1) {
1081 				if ((side == LOW && direction == TR) ||
1082 				    (side == HIGH && direction == BR) ||
1083 				    (side == HIGH && direction == BL) ||
1084 				    (side == LOW && direction == TL))
1085 					return FALSE;
1086 			} else
1087 				side = (direction == TR || direction == TL);
1088 			*space = (direction == TR || direction == BR);
1089 			break;
1090 	}
1091 	FindMovableTile(w, w->hexagons.spacePosition[highest],
1092 	   w->hexagons.spaceRow[highest], spaceType, side, position, posRow);
1093 	return TRUE;
1094 }
1095 
1096 static void
MoveNoTiles(HexagonsWidget w)1097 MoveNoTiles(HexagonsWidget w)
1098 {
1099 	hexagonsCallbackStruct cb;
1100 
1101 	cb.reason = HEXAGONS_IGNORE;
1102 	XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1103 }
1104 
1105 static int
MoveTilesDir(HexagonsWidget w,int direction)1106 MoveTilesDir(HexagonsWidget w, int direction)
1107 {
1108 	if (w->hexagons.corners)
1109 		return MoveCornerTilesDir(w, direction);
1110 	else
1111 		return MoveNoCornTilesDir(w, direction);
1112 }
1113 
1114 
1115 static int
MoveCornerTilesDir(HexagonsWidget w,int direction)1116 MoveCornerTilesDir(HexagonsWidget w, int direction)
1117 {
1118 	int         position, posRow, space;
1119 
1120 	if (MovableCornerTiles(w, direction, &position, &posRow, &space)) {
1121 		MoveCornerTiles(w, position, posRow, (w->hexagons.spacePosition[HIGH] <
1122 			   w->hexagons.spacePosition[LOW]) ? !space : space);
1123 		return TRUE;
1124 	}
1125 	return FALSE;
1126 }
1127 
1128 static int
MoveNoCornTilesDir(HexagonsWidget w,int direction)1129 MoveNoCornTilesDir(HexagonsWidget w, int direction)
1130 {
1131 	switch (direction) {
1132 		case TR:
1133 			if (w->hexagons.spaceRow[ROW] != 2 * w->hexagons.size - 2 &&
1134 			    w->hexagons.spaceRow[TLBR] != 2 * w->hexagons.size - 2) {
1135 				MoveNoCornTiles(w, TileNextToSpace(w, TRBL, LOW),
1136 					      w->hexagons.spaceRow[ROW] + 1);
1137 				return TRUE;
1138 			}
1139 			break;
1140 		case RIGHT:
1141 			if (w->hexagons.spaceRow[TRBL] != 0 &&
1142 			    w->hexagons.spaceRow[TLBR] != 2 * w->hexagons.size - 2) {
1143 				MoveNoCornTiles(w, TileNextToSpace(w, ROW, HIGH),
1144 						w->hexagons.spaceRow[ROW]);
1145 				return TRUE;
1146 			}
1147 			break;
1148 		case BR:
1149 			if (w->hexagons.spaceRow[ROW] != 0 &&
1150 			    w->hexagons.spaceRow[TRBL] != 0) {
1151 				MoveNoCornTiles(w, TileNextToSpace(w, TLBR, HIGH),
1152 					      w->hexagons.spaceRow[ROW] - 1);
1153 				return TRUE;
1154 			}
1155 			break;
1156 		case BL:
1157 			if (w->hexagons.spaceRow[ROW] != 0 &&
1158 			    w->hexagons.spaceRow[TLBR] != 0) {
1159 				MoveNoCornTiles(w, TileNextToSpace(w, TRBL, HIGH),
1160 					      w->hexagons.spaceRow[ROW] - 1);
1161 				return TRUE;
1162 			}
1163 			break;
1164 		case LEFT:
1165 			if (w->hexagons.spaceRow[TLBR] != 0 &&
1166 			    w->hexagons.spaceRow[TRBL] != 2 * w->hexagons.size - 2) {
1167 				MoveNoCornTiles(w, TileNextToSpace(w, ROW, LOW),
1168 						w->hexagons.spaceRow[ROW]);
1169 				return TRUE;
1170 			}
1171 			break;
1172 		case TL:
1173 			if (w->hexagons.spaceRow[ROW] != 2 * w->hexagons.size - 2 &&
1174 			    w->hexagons.spaceRow[TRBL] != 2 * w->hexagons.size - 2) {
1175 				MoveNoCornTiles(w, TileNextToSpace(w, TLBR, LOW),
1176 					      w->hexagons.spaceRow[ROW] + 1);
1177 				return TRUE;
1178 			}
1179 			break;
1180 		default:
1181 			(void) printf("MoveNoCornTilesDir: direction %d\n", direction);
1182 	}
1183 	return FALSE;
1184 }
1185 
1186 static void
RandomizeTiles(HexagonsWidget w)1187 RandomizeTiles(HexagonsWidget w)
1188 {
1189 	hexagonsCallbackStruct cb;
1190 
1191 	/* First interchange tiles an even number of times */
1192 	if (w->hexagons.size > 1 + ((w->hexagons.corners) ? 1 : 0)) {
1193 		int         currentPos, randomPos;
1194 		int         count = 0;
1195 
1196 		for (currentPos = 0; currentPos < w->hexagons.sizeSize; currentPos++) {
1197 			randomPos = currentPos;
1198 			while (currentPos == randomPos)
1199 				randomPos = NRAND(w->hexagons.sizeSize);
1200 			count += ExchangeTiles(w, currentPos, randomPos);
1201 		}
1202 		if (count % 2 && w->hexagons.corners)
1203 			if (!ExchangeTiles(w, 0, 1))
1204 				if (!ExchangeTiles(w,
1205 						   w->hexagons.sizeSize - 2, w->hexagons.sizeSize - 1))
1206 					(void) printf("RandomizeTiles: should not get here\n");
1207 		DrawAllTiles(w);
1208 	}
1209 	/* Now move the spaces around randomly */
1210 	if (w->hexagons.size > 1) {
1211 		int         big = w->hexagons.sizeSize + NRAND(2);
1212 		int         lastDirection = 0;
1213 		int         randomDirection;
1214 
1215 		cb.reason = HEXAGONS_RESET;
1216 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1217 
1218 		if (w->hexagons.corners && w->hexagons.size == 2)
1219 			big *= big;
1220 
1221 #ifdef DEBUG
1222 		big = 3;
1223 #endif
1224 
1225 		if (big > 1000)
1226 			big = 1000;
1227 		while (big--) {
1228 			randomDirection = NRAND(COORD);
1229 
1230 #ifdef DEBUG
1231 			sleep(1);
1232 #endif
1233 
1234 			if ((randomDirection + COORD / 2) % COORD != lastDirection) {
1235 				if (MoveHexagonsDir(w, randomDirection))
1236 					lastDirection = randomDirection;
1237 				else
1238 					big++;
1239 			}
1240 		}
1241 		FlushMoves(w);
1242 		cb.reason = HEXAGONS_RANDOMIZE;
1243 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1244 	}
1245 	if (CheckSolved(w)) {
1246 		cb.reason = HEXAGONS_SOLVED;
1247 		XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
1248 	}
1249 }
1250 
1251 static void
MoveCornerTiles(HexagonsWidget w,int from,int posRow,int space)1252 MoveCornerTiles(HexagonsWidget w, int from, int posRow, int space)
1253 {
1254 	int         tempTile;
1255 
1256 	tempTile = w->hexagons.tileOfPosition[from];
1257 	w->hexagons.tileOfPosition[from] =
1258 		w->hexagons.tileOfPosition[w->hexagons.spacePosition[space]];
1259 	w->hexagons.tileOfPosition[w->hexagons.spacePosition[space]] =
1260 		tempTile;
1261 	DrawTile(w, w->hexagons.spacePosition[space], False, False, FALSE);
1262 	w->hexagons.spacePosition[space] = from;
1263 	w->hexagons.spaceRow[space] = posRow;
1264 	DrawTile(w, w->hexagons.spacePosition[space], True, True, FALSE);
1265 }
1266 
1267 static void
MoveNoCornTiles(HexagonsWidget w,int from,int posRow)1268 MoveNoCornTiles(HexagonsWidget w, int from, int posRow)
1269 {
1270 	int         tempTile;
1271 
1272 	tempTile = w->hexagons.tileOfPosition[from];
1273 	w->hexagons.tileOfPosition[from] =
1274 		w->hexagons.tileOfPosition[w->hexagons.spacePosition[HIGH]];
1275 	w->hexagons.tileOfPosition[w->hexagons.spacePosition[HIGH]] = tempTile;
1276 	DrawTile(w, w->hexagons.spacePosition[HIGH], False, False, FALSE);
1277 	w->hexagons.spacePosition[HIGH] = from;
1278 	w->hexagons.spaceRow[ROW] = posRow;
1279 	w->hexagons.spaceRow[TRBL] = TrBl(w, from, posRow);
1280 	w->hexagons.spaceRow[TLBR] = TlBr(w, from, posRow);
1281 	DrawTile(w, w->hexagons.spacePosition[HIGH], True, True, FALSE);
1282 }
1283 
1284 static int
ExchangeTiles(HexagonsWidget w,int pos1,int pos2)1285 ExchangeTiles(HexagonsWidget w, int pos1, int pos2)
1286 {
1287 	int         tempTile;
1288 
1289 	if (w->hexagons.tileOfPosition[pos1] <= 0 ||
1290 	    w->hexagons.tileOfPosition[pos2] <= 0)
1291 		return FALSE;
1292 	tempTile = w->hexagons.tileOfPosition[pos1];
1293 	w->hexagons.tileOfPosition[pos1] = w->hexagons.tileOfPosition[pos2];
1294 	w->hexagons.tileOfPosition[pos2] = tempTile;
1295 	return TRUE;
1296 }
1297 
1298 #ifdef DEBUG
1299 static int
WithinFrame(HexagonsWidget w,int x,int y,int dx,int dy)1300 WithinFrame(HexagonsWidget w, int x, int y, int dx, int dy)
1301 {
1302 	return
1303 		(x < dx + w->hexagons.tileSize.x / 2 &&
1304 		 x > dx - w->hexagons.tileSize.x / 2 &&
1305 		 w->hexagons.tileSize.y * (x - dx) < w->hexagons.tileSize.x * (y - dy) &&
1306 		 w->hexagons.tileSize.y * (dx - x) < w->hexagons.tileSize.x * (y - dy) &&
1307 	     w->hexagons.tileSize.y * (x - dx + 2 * w->hexagons.tileSize.x) >
1308 		 w->hexagons.tileSize.x * (y - dy) &&
1309 	     w->hexagons.tileSize.y * (dx - x + 2 * w->hexagons.tileSize.x) >
1310 		 w->hexagons.tileSize.x * (y - dy));
1311 }
1312 #endif
1313 
1314 static int
NextToWall(HexagonsWidget w,int pos,int posRow,int spaceType)1315 NextToWall(HexagonsWidget w, int pos, int posRow, int spaceType)
1316 {
1317 	switch (spaceType) {
1318 		case TRBL:
1319 			if (posRow < w->hexagons.size && pos ==
1320 			w->hexagons.size * posRow + posRow * (posRow - 1) / 2)
1321 				return (HIGH);
1322 			else if (posRow >= w->hexagons.size && pos == w->hexagons.size *
1323 				 (posRow - w->hexagons.size) + 3 * w->hexagons.size - posRow -
1324 				 2 + 4 * w->hexagons.size * (w->hexagons.size - 1) / 2 -
1325 				 (2 * w->hexagons.size - posRow - 2) *
1326 				 (2 * w->hexagons.size - posRow - 1) / 2)
1327 				return (LOW);
1328 			else
1329 				return (-1);
1330 		case TLBR:
1331 			if (posRow < w->hexagons.size && pos ==
1332 			    w->hexagons.size * (posRow + 1) + posRow * (posRow + 1) / 2 - 1)
1333 				return (HIGH);
1334 			else if (posRow >= w->hexagons.size && pos == w->hexagons.size *
1335 				 (posRow - w->hexagons.size) + 1 + 4 * w->hexagons.size *
1336 				 (w->hexagons.size - 1) / 2 - (2 * w->hexagons.size - posRow - 2) *
1337 				 (2 * w->hexagons.size - posRow - 1) / 2)
1338 				return (LOW);
1339 			else
1340 				return (-1);
1341 		case ROW:
1342 			if (posRow == 0)
1343 				return (HIGH);
1344 			else if (posRow == 2 * (w->hexagons.size - 1))
1345 				return (LOW);
1346 			else
1347 				return (-1);
1348 	}
1349 	return (-2);		/*Unknown space formation. */
1350 }
1351 
1352 static int
TileNextToSpace(HexagonsWidget w,int rowType,int direction)1353 TileNextToSpace(HexagonsWidget w, int rowType, int direction)
1354 {
1355 	if (direction == HIGH) {
1356 		if (rowType == TRBL)
1357 			return ((w->hexagons.spaceRow[ROW] < w->hexagons.size) ?
1358 			 w->hexagons.spacePosition[HIGH] - w->hexagons.size -
1359 				w->hexagons.spaceRow[ROW] + 1 :
1360 				w->hexagons.spacePosition[HIGH] - 3 * w->hexagons.size +
1361 				w->hexagons.spaceRow[ROW] + 2);
1362 		else if (rowType == TLBR)
1363 			return ((w->hexagons.spaceRow[ROW] < w->hexagons.size) ?
1364 			 w->hexagons.spacePosition[HIGH] - w->hexagons.size -
1365 				w->hexagons.spaceRow[ROW] :
1366 				w->hexagons.spacePosition[HIGH] - 3 * w->hexagons.size +
1367 				w->hexagons.spaceRow[ROW] + 1);
1368 		else		/* rowType == ROW */
1369 			return (w->hexagons.spacePosition[HIGH] - 1);
1370 	} else {		/* direction == LOW */
1371 		if (rowType == TRBL)
1372 			return ((w->hexagons.spaceRow[ROW] < w->hexagons.size - 1) ?
1373 			 w->hexagons.spacePosition[HIGH] + w->hexagons.size +
1374 				w->hexagons.spaceRow[ROW] :
1375 				w->hexagons.spacePosition[HIGH] + 3 * w->hexagons.size -
1376 				w->hexagons.spaceRow[ROW] - 3);
1377 		else if (rowType == TLBR)
1378 			return ((w->hexagons.spaceRow[ROW] < w->hexagons.size - 1) ?
1379 			 w->hexagons.spacePosition[HIGH] + w->hexagons.size +
1380 				w->hexagons.spaceRow[ROW] + 1 :
1381 				w->hexagons.spacePosition[HIGH] + 3 * w->hexagons.size -
1382 				w->hexagons.spaceRow[ROW] - 2);
1383 		else		/* rowType == ROW */
1384 			return (w->hexagons.spacePosition[HIGH] + 1);
1385 	}
1386 }
1387 
1388 static int
FindTileTriangle(HexagonsWidget w,int pI,int pJ,int pK,int rI,int rJ,int rK)1389 FindTileTriangle(HexagonsWidget w, int pI, int pJ, int pK, int rI, int rJ, int rK)
1390 {
1391 	int         found = TRUE, temp = 0, k = 0, row1 = 0, row2 = 0, pos;
1392 
1393 	if (rI == rJ) {
1394 		if (pI == pJ - 1)
1395 			temp = pJ;
1396 		else if (pI == pJ + 1)
1397 			temp = pI;
1398 		else
1399 			found = FALSE;
1400 		k = pK;
1401 		row1 = rI;
1402 		row2 = rK;
1403 	} else if (rJ == rK) {
1404 		if (pJ == pK - 1)
1405 			temp = pK;
1406 		else if (pJ == pK + 1)
1407 			temp = pJ;
1408 		else
1409 			found = FALSE;
1410 		k = pI;
1411 		row1 = rJ;
1412 		row2 = rI;
1413 	} else if (rK == rI) {
1414 		if (pK == pI - 1)
1415 			temp = pI;
1416 		else if (pK == pI + 1)
1417 			temp = pK;
1418 		else
1419 			found = FALSE;
1420 		k = pJ;
1421 		row1 = rK;
1422 		row2 = rJ;
1423 	}
1424 	if (found == FALSE)
1425 		return (0);
1426 	pos = -1;
1427 	if (row1 == row2 + 1) {
1428 		if (row1 <= w->hexagons.size - 1)
1429 			pos = temp - w->hexagons.size - row1;
1430 		else		/* row1 > w->hexagons.size - 1 */
1431 			pos = temp - 3 * w->hexagons.size + row1 + 1;
1432 	} else if (row1 == row2 - 1) {
1433 		if (row1 < w->hexagons.size - 1)
1434 			pos = temp + w->hexagons.size + row1;
1435 		else		/* row1 >= w->hexagons.size - 1 */
1436 			pos = temp + 3 * (w->hexagons.size - 1) - row1;
1437 	}
1438 	if (k == pos)
1439 		return (1);
1440 	return (0);
1441 }
1442 
1443 static int
FindDir(HexagonsWidget w,int posTile,int posSpace,int rowTile,int rowSpace)1444 FindDir(HexagonsWidget w, int posTile, int posSpace, int rowTile, int rowSpace)
1445 {
1446 	if (rowTile == rowSpace) {
1447 		if (posTile > posSpace)
1448 			return LEFT;
1449 		else
1450 			return RIGHT;
1451 	} else if (TrBl(w, posTile, rowTile) == TrBl(w, posSpace, rowSpace)) {
1452 		if (posTile > posSpace)
1453 			return TR;
1454 		else
1455 			return BL;
1456 	} else {
1457 		/* if (TlBr(w, posTile, rowTile) == TlBr(w, posSpace, rowSpace)) */
1458 		if (posTile > posSpace)
1459 			return TL;
1460 		else
1461 			return BR;
1462 	}
1463 }
1464 
1465 static int
FindSpaceType(HexagonsWidget w,int pos1,int pos2,int row1,int row2)1466 FindSpaceType(HexagonsWidget w, int pos1, int pos2, int row1, int row2)
1467 {
1468 	if (row1 == row2 && (pos1 == pos2 + 1 || pos1 == pos2 - 1))
1469 		return (ROW);
1470 	else if (row1 == row2 - 1) {
1471 		swap(&row1, &row2);
1472 		swap(&pos1, &pos2);
1473 	}
1474 	if (row1 == row2 + 1) {
1475 		if (row1 <= w->hexagons.size - 1) {
1476 			if (pos2 == pos1 - w->hexagons.size - row1)
1477 				return (TLBR);
1478 			else if (pos2 == pos1 - w->hexagons.size - row1 + 1)
1479 				return (TRBL);
1480 		} else {	/* row1 > w->hexagons.size - 1 */
1481 			if (pos2 == pos1 - 3 * w->hexagons.size + row1 + 1)
1482 				return (TLBR);
1483 			else if (pos2 == pos1 - 3 * w->hexagons.size + row1 + 2)
1484 				return (TRBL);
1485 		}
1486 	}
1487 	return (-1);
1488 }
1489 
1490 static void
FindMovableTile(HexagonsWidget w,int pos,int posRow,int spaceType,int side,int * tilePos,int * tileRow)1491 FindMovableTile(HexagonsWidget w, int pos, int posRow, int spaceType, int side, int *tilePos, int *tileRow)
1492 {
1493 	switch (spaceType) {
1494 		case TRBL:
1495 			if (side == HIGH) {
1496 				*tileRow = posRow;
1497 				*tilePos = pos + 1;
1498 			} else {	/* side == LOW */
1499 				*tileRow = posRow - 1;
1500 				*tilePos = (posRow <= w->hexagons.size - 1) ?
1501 					pos - w->hexagons.size - posRow :
1502 					pos - 3 * w->hexagons.size + posRow + 1;
1503 			}
1504 			break;
1505 		case TLBR:
1506 			if (side == HIGH) {
1507 				*tileRow = posRow;
1508 				*tilePos = pos - 1;
1509 			} else {	/* side == LOW */
1510 				*tileRow = posRow - 1;
1511 				*tilePos = (posRow <= w->hexagons.size - 1) ?
1512 					pos - w->hexagons.size - posRow + 1 :
1513 					pos - 3 * w->hexagons.size + posRow + 2;
1514 			}
1515 			break;
1516 		case ROW:
1517 			if (side == HIGH) {
1518 				*tileRow = posRow + 1;
1519 				*tilePos = (posRow < w->hexagons.size - 1) ?
1520 					pos + w->hexagons.size + posRow :
1521 					pos + 3 * w->hexagons.size - posRow - 3;
1522 			} else {	/* side == LOW */
1523 				*tileRow = posRow - 1;
1524 				*tilePos = (posRow <= w->hexagons.size - 1) ?
1525 					pos - w->hexagons.size - posRow :
1526 					pos - 3 * w->hexagons.size + posRow + 1;
1527 			}
1528 			break;
1529 		default:
1530 			(void) printf("FindMovableTile: spaceType %d.\n", spaceType);
1531 	}
1532 }
1533 
1534 static void
DrawFrame(HexagonsWidget w,GC gc)1535 DrawFrame(HexagonsWidget w, GC gc)
1536 {
1537 	int         sumX, sumY, sumX4, sum3X4, sumY2, offsetX, offsetY;
1538 
1539 	sumX = w->hexagons.size * (2 * w->hexagons.offset.x) -
1540 		w->hexagons.tileSize.x / 2 - 2 * w->hexagons.delta.x - 1;
1541 	sumY = w->hexagons.size * (3 * w->hexagons.tileSize.y + 2 *
1542 			   w->hexagons.delta.y) - w->hexagons.tileSize.y - 1;
1543 	offsetX = w->hexagons.puzzleOffset.x - 1;
1544 	offsetY = w->hexagons.puzzleOffset.y;
1545 	sumX4 = sumX / 4 + offsetX;
1546 	sum3X4 = 3 * sumX / 4 + offsetX + 2;
1547 	sumY2 = sumY / 2 + offsetY;
1548 	sumX += offsetX + 1 + w->hexagons.size / 2;
1549 	sumY += offsetY;
1550 	offsetX += 1 - w->hexagons.size / 2;
1551 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1552 		  sumX4, offsetY, sum3X4, offsetY);
1553 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1554 		  sum3X4, offsetY, sumX, sumY2);
1555 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1556 		  sumX, sumY2, sum3X4, sumY);
1557 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1558 		  sum3X4, sumY, sumX4, sumY);
1559 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1560 		  sumX4, sumY, offsetX, sumY2);
1561 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
1562 		  offsetX, sumY2, sumX4, offsetY);
1563 }
1564 
1565 void
DrawAllTiles(HexagonsWidget w)1566 DrawAllTiles(HexagonsWidget w)
1567 {
1568 	int         k;
1569 
1570 	for (k = 0; k < w->hexagons.sizeSize; k++)
1571 		DrawTile(w, k, (w->hexagons.tileOfPosition[k] <= 0), (w->hexagons.tileOfPosition[k] <= 0), False);
1572 }
1573 
1574 static void
DrawTile(HexagonsWidget w,int pos,Boolean blank,Boolean erase,int offset)1575 DrawTile(HexagonsWidget w, int pos, Boolean blank, Boolean erase, int offset)
1576 {
1577 	int         dx, dy, k = Row(w, pos), orient = (w->hexagons.corners) ? 1 : 0;
1578 	GC          tileGC, borderGC;
1579 
1580 	if (erase) {
1581 		tileGC = w->hexagons.inverseGC;
1582 		borderGC = w->hexagons.inverseGC;
1583 	} else if (offset) {
1584 		tileGC = w->hexagons.borderGC;
1585 		borderGC = w->hexagons.tileGC;
1586 	} else {
1587 		tileGC = w->hexagons.tileGC;
1588 		borderGC = w->hexagons.borderGC;
1589 	}
1590 	dx = w->hexagons.tileSize.x / 4 - 1 + (2 * TrBl(w, pos, k) +
1591 			   w->hexagons.size - k) * w->hexagons.offset.x / 2 +
1592 		w->hexagons.puzzleOffset.x + offset;
1593 	dy = k * (3 * w->hexagons.tileSize.y / 2 + w->hexagons.delta.y) +
1594 		w->hexagons.delta.y - 1 + w->hexagons.puzzleOffset.y + offset;
1595 	if (orient) {
1596 		hexagonList[orient][0].x = dx;
1597 		hexagonList[orient][0].y = dy;
1598 	} else {
1599 		hexagonList[orient][0].x = dx - w->hexagons.offset.x / 4;
1600 		hexagonList[orient][0].y = dy +
1601 			(w->hexagons.tileSize.y + w->hexagons.delta.y) / 4;
1602 	}
1603 	XFillPolygon(XtDisplay(w), XtWindow(w), tileGC, hexagonList[orient], 6,
1604 		     Convex, CoordModePrevious);
1605 	XDrawLines(XtDisplay(w), XtWindow(w), borderGC, hexagonList[orient], 7,
1606 		   CoordModePrevious);
1607 	if (!blank) {
1608 		int         i = 0, offsetX = 0;
1609 		int         tile = w->hexagons.tileOfPosition[pos];
1610 		char        buf[5];
1611 
1612 		(void) int2String(buf, tile, w->hexagons.base);
1613 		while (tile >= 1) {
1614 			tile /= w->hexagons.base;
1615 			offsetX += w->hexagons.digitOffset.x;
1616 			i++;
1617 		}
1618 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
1619 			    dx - offsetX,
1620 			    dy + w->hexagons.tileSize.y + w->hexagons.digitOffset.y, buf, i);
1621 	}
1622 }
1623 
1624 #ifdef DEBUG
1625 static int
PositionInRow(w,position,posRow)1626 PositionInRow(w, position, posRow)
1627 	HexagonsWidget w;
1628 	int         position, posRow;
1629 {
1630 	return (posRow <= w->hexagons.size - 1) ?
1631 		(position - w->hexagons.size * posRow - posRow * (posRow - 1) / 2) :
1632 		(position - w->hexagons.size * (posRow - w->hexagons.size) -
1633 		 4 * w->hexagons.size * (w->hexagons.size - 1) / 2 + (2 *
1634 	w->hexagons.size - posRow - 2) * (2 * w->hexagons.size - posRow - 1) /
1635 		 2 - 1);
1636 }
1637 #endif
1638 
1639 static int
PositionFromRow(HexagonsWidget w,int rowPosition,int posRow)1640 PositionFromRow(HexagonsWidget w, int rowPosition, int posRow)
1641 {
1642 	return (posRow <= w->hexagons.size - 1) ?
1643 		(w->hexagons.size * posRow + posRow * (posRow - 1) / 2 + rowPosition) :
1644 		(w->hexagons.size * (posRow - w->hexagons.size) + 4 *
1645 		 w->hexagons.size * (w->hexagons.size - 1) / 2 - (2 * w->hexagons.size -
1646 								  posRow - 2) * (2 * w->hexagons.size - posRow - 1) / 2 + 1 + rowPosition);
1647 }
1648 
1649 int
Row(HexagonsWidget w,int pos)1650 Row(HexagonsWidget w, int pos)
1651 {
1652 	return (pos <= w->hexagons.sizeCenter) ?
1653 		(1 + Sqrt(1 + 8 * (pos + w->hexagons.size *
1654 		       (w->hexagons.size - 1) / 2))) / 2 - w->hexagons.size :
1655 		3 * w->hexagons.size - 2 - (1 + Sqrt(1 + 8 * (w->hexagons.sizeSize - 1 +
1656 		  w->hexagons.size * (w->hexagons.size - 1) / 2 - pos))) / 2;
1657 }
1658 
1659 /* Passing row so there is no sqrt calculation again */
1660 int
TrBl(HexagonsWidget w,int pos,int posRow)1661 TrBl(HexagonsWidget w, int pos, int posRow)
1662 {
1663 	return (pos <= w->hexagons.sizeCenter) ?
1664 		(pos + w->hexagons.size * (w->hexagons.size - 1) / 2) -
1665 		(posRow + w->hexagons.size) * (posRow + w->hexagons.size - 1) / 2 :
1666 		2 * w->hexagons.size - 2 - (w->hexagons.sizeSize - 1 +
1667 			w->hexagons.size * (w->hexagons.size - 1) / 2 - pos -
1668 					    (3 * w->hexagons.size - posRow - 2) * (3 * w->hexagons.size - posRow - 3) /
1669 					    2);
1670 }
1671 
1672 int
TlBr(HexagonsWidget w,int pos,int posRow)1673 TlBr(HexagonsWidget w, int pos, int posRow)
1674 {
1675 	return (pos <= w->hexagons.sizeCenter) ?
1676 		-1 - ((pos + w->hexagons.size * (w->hexagons.size - 1) / 2) -
1677 	 (posRow + w->hexagons.size + 1) * (posRow + w->hexagons.size) / 2) :
1678 		2 * w->hexagons.size - 1 + (w->hexagons.sizeSize - 1 +
1679 			w->hexagons.size * (w->hexagons.size - 1) / 2 - pos -
1680 					    (3 * w->hexagons.size - posRow - 1) * (3 * w->hexagons.size - posRow - 2) /
1681 					    2);
1682 }
1683 
1684 /* This is fast for small i, a -1 is returned for negative i. */
1685 static int
Sqrt(int i)1686 Sqrt(int i)
1687 {
1688 	int         j = 0;
1689 
1690 	while (j * j <= i)
1691 		j++;
1692 	return (j - 1);
1693 }
1694 
1695 static int
int2String(char * buf,int number,int base)1696 int2String(char *buf, int number, int base)
1697 {
1698 	int         digit, mult = base, last, position;
1699 
1700 	if (number < 0) {
1701 		(void) printf("number %d < 0\n", number);
1702 		return 0;
1703 	}
1704 	last = 1;
1705 	while (number >= mult) {
1706 		last++;
1707 		mult *= base;
1708 	}
1709 	for (position = 0; position < last; position++) {
1710 		mult /= base;
1711 		digit = number / mult;
1712 		number -= digit * mult;
1713 		buf[position] = digit + '0';
1714 		if (buf[position] > '9') {	/* ASCII */
1715 			buf[position] += ('A' - '9' - 1);
1716 		} else if (buf[position] < '0') {	/* EBCDIC */
1717 			buf[position] += ('A' - '9' - 1);
1718 			if (buf[position] > 'I')
1719 				buf[position] += ('J' - 'I' - 1);
1720 			if (buf[position] > 'R')
1721 				buf[position] += ('S' - 'R' - 1);
1722 		}
1723 	}
1724 	buf[last] = '\0';
1725 	return last;
1726 }
1727 
1728 static void
swap(int * a,int * b)1729 swap(int *a, int *b)
1730 {
1731 	int         c;
1732 
1733 	c = *b;
1734 	*b = *a;
1735 	*a = c;
1736 }
1737 
1738 Boolean
CheckSolved(HexagonsWidget w)1739 CheckSolved(HexagonsWidget w)
1740 {
1741 	int         i;
1742 
1743 	for (i = 1; i < w->hexagons.sizeSize - ((w->hexagons.corners) ? 1 : 0); i++)
1744 		if (w->hexagons.tileOfPosition[i - 1] != i)
1745 			return FALSE;
1746 	return TRUE;
1747 }
1748