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