ncurses 6.2 - patch 20210306
[ncurses.git] / test / hanoi.c
1 /****************************************************************************
2  * Copyright 2019,2020 Thomas E. Dickey                                     *
3  * Copyright 1998-2014,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 /*
30  *      Name: Towers of Hanoi.
31  *
32  *      Desc:
33  *              This is a playable copy of towers of hanoi.
34  *              Its sole purpose is to demonstrate my Amiga Curses package.
35  *              This program should compile on any system that has Curses.
36  *              'hanoi'         will give a manual game with 7 playing pieces.
37  *              'hanoi n'       will give a manual game with n playing pieces.
38  *              'hanoi n a' will give an auto solved game with n playing pieces.
39  *
40  *      Author: Simon J Raybould        (sie@fulcrum.bt.co.uk).
41  *      (This version has been slightly modified by the ncurses maintainers.)
42  *
43  *      Date: 05.Nov.90
44  *
45  * $Id: hanoi.c,v 1.41 2020/02/02 23:34:34 tom Exp $
46  */
47
48 #include <test.priv.h>
49 #include <math.h>
50
51 #define NPEGS                   3       /* This is not configurable !! */
52 #define MINTILES                3
53 #define MAXTILES                9
54 #define DEFAULTTILES            7
55 #define TOPLINE                 6
56 #define BASELINE                16
57 #define STATUSLINE              (LINES-3)
58 #define LEFTPEG                 19
59 #define MIDPEG                  39
60 #define RIGHTPEG                59
61
62 #define LENTOIND(x)             (((int)(x)-1)/2)
63 #define OTHER(a,b)              (3-((a)+(b)))
64
65 struct Peg {
66     size_t Length[MAXTILES];
67     int Count;
68 };
69
70 static struct Peg Pegs[NPEGS];
71 static int PegPos[] =
72 {
73     LEFTPEG,
74     MIDPEG,
75     RIGHTPEG
76 };
77 static short TileColour[] =
78 {
79     COLOR_GREEN,                /* Length 3 */
80     COLOR_MAGENTA,              /* Length 5 */
81     COLOR_RED,                  /* Length 7 */
82     COLOR_BLUE,                 /* Length 9 */
83     COLOR_CYAN,                 /* Length 11 */
84     COLOR_YELLOW,               /* Length 13 */
85     COLOR_GREEN,                /* Length 15 */
86     COLOR_MAGENTA,              /* Length 17 */
87     COLOR_RED,                  /* Length 19 */
88 };
89 static int NTiles = 0;
90 static int NMoves = 0;
91 static bool AutoFlag = FALSE;
92
93 static int
94 InvalidMove(int From, int To)
95 {
96     if (From >= NPEGS)
97         return TRUE;
98     if (From < 0)
99         return TRUE;
100     if (To >= NPEGS)
101         return TRUE;
102     if (To < 0)
103         return TRUE;
104     if (From == To)
105         return TRUE;
106     if (!Pegs[From].Count)
107         return TRUE;
108     if (Pegs[To].Count &&
109         Pegs[From].Length[Pegs[From].Count - 1] >
110         Pegs[To].Length[Pegs[To].Count - 1])
111         return TRUE;
112     return FALSE;
113 }
114
115 static void
116 InitTiles(void)
117 {
118     int Size, SlotNo;
119
120     for (Size = NTiles * 2 + 1, SlotNo = 0; Size >= 3; Size -= 2)
121         Pegs[0].Length[SlotNo++] = (size_t) Size;
122
123     Pegs[0].Count = NTiles;
124     Pegs[1].Count = 0;
125     Pegs[2].Count = 0;
126 }
127
128 static void
129 DisplayTiles(void)
130 {
131     int Line, peg, SlotNo;
132     char TileBuf[BUFSIZ];
133
134     erase();
135     MvAddStr(1, 24, "T O W E R S   O F   H A N O I");
136     MvAddStr(3, 34, "SJR 1990");
137     MvPrintw(19, 5, "Moves : %d of %.0f", NMoves, pow(2.0, (float) NTiles) - 1);
138     (void) attrset(A_REVERSE);
139     MvAddStr(BASELINE, 8,
140              "                                                               ");
141
142     for (Line = TOPLINE; Line < BASELINE; Line++) {
143         MvAddCh(Line, LEFTPEG, ' ');
144         MvAddCh(Line, MIDPEG, ' ');
145         MvAddCh(Line, RIGHTPEG, ' ');
146     }
147     MvAddCh(BASELINE, LEFTPEG, '1');
148     MvAddCh(BASELINE, MIDPEG, '2');
149     MvAddCh(BASELINE, RIGHTPEG, '3');
150     (void) attrset(A_NORMAL);
151
152     /* Draw tiles */
153     for (peg = 0; peg < NPEGS; peg++) {
154         for (SlotNo = 0; SlotNo < Pegs[peg].Count; SlotNo++) {
155             size_t len = Pegs[peg].Length[SlotNo];
156             if (len < sizeof(TileBuf) - 1 && len < (size_t) PegPos[peg]) {
157                 memset(TileBuf, ' ', len);
158                 TileBuf[len] = '\0';
159                 if (has_colors())
160                     (void) attrset(AttrArg(COLOR_PAIR(LENTOIND(len)), 0));
161                 else
162                     (void) attrset(A_REVERSE);
163                 MvAddStr(BASELINE - (SlotNo + 1),
164                          (PegPos[peg] - (int) len / 2),
165                          TileBuf);
166             }
167         }
168     }
169     (void) attrset(A_NORMAL);
170     refresh();
171 }
172
173 static int
174 GetMove(int *From, int *To)
175 {
176     MvAddStr(STATUSLINE, 0, "Next move ('q' to quit) from ");
177     clrtoeol();
178     refresh();
179     if ((*From = getch()) == 'q')
180         return TRUE;
181     *From -= ('0' + 1);
182     addstr(" to ");
183     clrtoeol();
184     refresh();
185
186     if ((*To = getch()) == 'q')
187         return TRUE;
188     *To -= ('0' + 1);
189     refresh();
190     if (!AutoFlag)
191         napms(500);
192
193     move(STATUSLINE, 0);
194     clrtoeol();
195     refresh();
196     return FALSE;
197 }
198
199 static void
200 MakeMove(int From, int To)
201 {
202     Pegs[From].Count--;
203     Pegs[To].Length[Pegs[To].Count] = Pegs[From].Length[Pegs[From].Count];
204     Pegs[To].Count++;
205     NMoves++;
206     DisplayTiles();
207 }
208
209 static void
210 AutoMove(int From, int To, int Num)
211 {
212     if (Num == 1) {
213         MakeMove(From, To);
214         napms(500);
215     } else {
216         AutoMove(From, OTHER(From, To), Num - 1);
217         MakeMove(From, To);
218         napms(500);
219         AutoMove(OTHER(From, To), To, Num - 1);
220     }
221 }
222
223 static int
224 Solved(int NumTiles)
225 {
226     int i;
227
228     for (i = 1; i < NPEGS; i++)
229         if (Pegs[i].Count == NumTiles)
230             return TRUE;
231     return FALSE;
232 }
233
234 static void
235 usage(void)
236 {
237     static const char *msg[] =
238     {
239         "Usage: hanoi [options] [[<No Of Tiles>] [a]]"
240         ,""
241         ,"Options:"
242 #if HAVE_USE_DEFAULT_COLORS
243         ," -d       invoke use_default_colors"
244 #endif
245         ," -n NUM   set number of tiles (positional param is deprecated)"
246         ," -X       solve automatically (positional \"a\" is deprecated)"
247     };
248     size_t n;
249
250     for (n = 0; n < SIZEOF(msg); n++)
251         fprintf(stderr, "%s\n", msg[n]);
252
253     ExitProgram(EXIT_FAILURE);
254 }
255
256 int
257 main(int argc, char **argv)
258 {
259     int ch, FromCol, ToCol;
260
261 #if HAVE_USE_DEFAULT_COLORS
262     bool d_option = FALSE;
263 #endif
264
265     NTiles = DEFAULTTILES;
266     while ((ch = getopt(argc, argv, "dn:X")) != -1) {
267         switch (ch) {
268 #if HAVE_USE_DEFAULT_COLORS
269         case 'd':
270             d_option = TRUE;
271             break;
272 #endif
273         case 'n':
274             NTiles = atoi(optarg);
275             break;
276         case 'X':
277             AutoFlag = TRUE;
278             break;
279         default:
280             usage();
281             /* NOTREACHED */
282         }
283     }
284     setlocale(LC_ALL, "");
285
286     switch (ch = (argc - optind)) {
287     case 2:
288         if (strcmp(argv[optind + 1], "a")) {
289             usage();
290         }
291         AutoFlag = TRUE;
292         /* FALLTHRU */
293     case 1:
294         NTiles = atoi(argv[optind]);
295         /* FALLTHRU */
296     case 0:
297         break;
298     default:
299         usage();
300     }
301
302     if (NTiles > MAXTILES || NTiles < MINTILES) {
303         fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
304         usage();
305     }
306
307     initscr();
308     if (has_colors()) {
309         int i;
310         short bg = COLOR_BLACK;
311         start_color();
312 #if HAVE_USE_DEFAULT_COLORS
313         if (d_option && (use_default_colors() == OK))
314             bg = -1;
315 #endif
316         for (i = 0; i < 9; i++)
317             init_pair((short) (i + 1), bg, TileColour[i]);
318     }
319     cbreak();
320     if (LINES < 24) {
321         endwin();
322         fprintf(stderr, "Min screen length 24 lines\n");
323         ExitProgram(EXIT_FAILURE);
324     }
325     if (AutoFlag) {
326         curs_set(0);
327         leaveok(stdscr, TRUE);  /* Attempt to remove cursor */
328     }
329     InitTiles();
330     DisplayTiles();
331     if (AutoFlag) {
332         do {
333             noecho();
334             AutoMove(0, 2, NTiles);
335         } while (!Solved(NTiles));
336         sleep(2);
337     } else {
338         echo();
339         for (;;) {
340             if (GetMove(&FromCol, &ToCol))
341                 break;
342             if (InvalidMove(FromCol, ToCol)) {
343                 MvAddStr(STATUSLINE, 0, "Invalid Move !!");
344                 refresh();
345                 beep();
346                 continue;
347             }
348             MakeMove(FromCol, ToCol);
349             if (Solved(NTiles)) {
350                 MvPrintw(STATUSLINE, 0,
351                          "Well Done !! You did it in %d moves", NMoves);
352                 refresh();
353                 sleep(5);
354                 break;
355             }
356         }
357     }
358     stop_curses();
359     ExitProgram(EXIT_SUCCESS);
360 }