ncurses 5.7 - patch 20081206
[ncurses.git] / test / hanoi.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2008 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.27 2008/08/04 10:57:59 tom Exp $
45  */
46
47 #include <test.priv.h>
48
49 #define NPEGS                   3       /* This is not configurable !! */
50 #define MINTILES                3
51 #define MAXTILES                9
52 #define DEFAULTTILES            7
53 #define TOPLINE                 6
54 #define BASELINE                16
55 #define STATUSLINE              (LINES-3)
56 #define LEFTPEG                 19
57 #define MIDPEG                  39
58 #define RIGHTPEG                59
59
60 #define LENTOIND(x)             (((x)-1)/2)
61 #define OTHER(a,b)              (3-((a)+(b)))
62
63 struct Peg {
64     size_t Length[MAXTILES];
65     int Count;
66 };
67
68 static struct Peg Pegs[NPEGS];
69 static int PegPos[] =
70 {LEFTPEG, MIDPEG, RIGHTPEG};
71 static int TileColour[] =
72 {
73     COLOR_GREEN,                /* Length 3 */
74     COLOR_MAGENTA,              /* Length 5 */
75     COLOR_RED,                  /* Length 7 */
76     COLOR_BLUE,                 /* Length 9 */
77     COLOR_CYAN,                 /* Length 11 */
78     COLOR_YELLOW,               /* Length 13 */
79     COLOR_GREEN,                /* Length 15 */
80     COLOR_MAGENTA,              /* Length 17 */
81     COLOR_RED,                  /* Length 19 */
82 };
83 static int NMoves = 0;
84 static bool AutoFlag = FALSE;
85
86 static void InitTiles(int NTiles);
87 static void DisplayTiles(void);
88 static void MakeMove(int From, int To);
89 static void AutoMove(int From, int To, int Num);
90 static void Usage(void);
91 static int Solved(int NumTiles);
92 static int GetMove(int *From, int *To);
93 static int InvalidMove(int From, int To);
94
95 int
96 main(int argc, char **argv)
97 {
98     int NTiles, FromCol, ToCol;
99
100     setlocale(LC_ALL, "");
101
102     switch (argc) {
103     case 1:
104         NTiles = DEFAULTTILES;
105         break;
106     case 2:
107         NTiles = atoi(argv[1]);
108         if (NTiles > MAXTILES || NTiles < MINTILES) {
109             fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
110             ExitProgram(EXIT_FAILURE);
111         }
112         break;
113     case 3:
114         if (strcmp(argv[2], "a")) {
115             Usage();
116             ExitProgram(EXIT_FAILURE);
117         }
118         NTiles = atoi(argv[1]);
119         if (NTiles > MAXTILES || NTiles < MINTILES) {
120             fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
121             ExitProgram(EXIT_FAILURE);
122         }
123         AutoFlag = TRUE;
124         break;
125     default:
126         Usage();
127         ExitProgram(EXIT_FAILURE);
128     }
129 #ifdef TRACE
130     trace(TRACE_MAXIMUM);
131 #endif
132     initscr();
133     if (has_colors()) {
134         int i;
135         int bg = COLOR_BLACK;
136         start_color();
137 #if HAVE_USE_DEFAULT_COLORS
138         if (use_default_colors() == OK)
139             bg = -1;
140 #endif
141         for (i = 0; i < 9; i++)
142             init_pair(i + 1, bg, TileColour[i]);
143     }
144     cbreak();
145     if (LINES < 24) {
146         endwin();
147         fprintf(stderr, "Min screen length 24 lines\n");
148         ExitProgram(EXIT_FAILURE);
149     }
150     if (AutoFlag) {
151         curs_set(0);
152         leaveok(stdscr, TRUE);  /* Attempt to remove cursor */
153     }
154     InitTiles(NTiles);
155     DisplayTiles();
156     if (AutoFlag) {
157         do {
158             noecho();
159             AutoMove(0, 2, NTiles);
160         } while (!Solved(NTiles));
161         sleep(2);
162     } else {
163         echo();
164         for (;;) {
165             if (GetMove(&FromCol, &ToCol))
166                 break;
167             if (InvalidMove(FromCol, ToCol)) {
168                 mvaddstr(STATUSLINE, 0, "Invalid Move !!");
169                 refresh();
170                 beep();
171                 continue;
172             }
173             MakeMove(FromCol, ToCol);
174             if (Solved(NTiles)) {
175                 mvprintw(STATUSLINE, 0,
176                          "Well Done !! You did it in %d moves", NMoves);
177                 refresh();
178                 sleep(5);
179                 break;
180             }
181         }
182     }
183     endwin();
184     ExitProgram(EXIT_SUCCESS);
185 }
186
187 static int
188 InvalidMove(int From, int To)
189 {
190     if (From >= NPEGS)
191         return TRUE;
192     if (From < 0)
193         return TRUE;
194     if (To >= NPEGS)
195         return TRUE;
196     if (To < 0)
197         return TRUE;
198     if (From == To)
199         return TRUE;
200     if (!Pegs[From].Count)
201         return TRUE;
202     if (Pegs[To].Count &&
203         Pegs[From].Length[Pegs[From].Count - 1] >
204         Pegs[To].Length[Pegs[To].Count - 1])
205         return TRUE;
206     return FALSE;
207 }
208
209 static void
210 InitTiles(int NTiles)
211 {
212     int Size, SlotNo;
213
214     for (Size = NTiles * 2 + 1, SlotNo = 0; Size >= 3; Size -= 2)
215         Pegs[0].Length[SlotNo++] = Size;
216
217     Pegs[0].Count = NTiles;
218     Pegs[1].Count = 0;
219     Pegs[2].Count = 0;
220 }
221
222 static void
223 DisplayTiles(void)
224 {
225     int Line, peg, SlotNo;
226     char TileBuf[BUFSIZ];
227
228     erase();
229     mvaddstr(1, 24, "T O W E R S   O F   H A N O I");
230     mvaddstr(3, 34, "SJR 1990");
231     mvprintw(19, 5, "Moves : %d", NMoves);
232     attrset(A_REVERSE);
233     mvaddstr(BASELINE, 8,
234              "                                                               ");
235
236     for (Line = TOPLINE; Line < BASELINE; Line++) {
237         mvaddch(Line, LEFTPEG, ' ');
238         mvaddch(Line, MIDPEG, ' ');
239         mvaddch(Line, RIGHTPEG, ' ');
240     }
241     mvaddch(BASELINE, LEFTPEG, '1');
242     mvaddch(BASELINE, MIDPEG, '2');
243     mvaddch(BASELINE, RIGHTPEG, '3');
244     attrset(A_NORMAL);
245
246     /* Draw tiles */
247     for (peg = 0; peg < NPEGS; peg++) {
248         for (SlotNo = 0; SlotNo < Pegs[peg].Count; SlotNo++) {
249             unsigned len = Pegs[peg].Length[SlotNo];
250             if (len < sizeof(TileBuf) - 1 && len < (unsigned) PegPos[peg]) {
251                 memset(TileBuf, ' ', len);
252                 TileBuf[len] = '\0';
253                 if (has_colors())
254                     attrset(COLOR_PAIR(LENTOIND(len)));
255                 else
256                     attrset(A_REVERSE);
257                 mvaddstr(BASELINE - (SlotNo + 1),
258                          (int) (PegPos[peg] - len / 2),
259                          TileBuf);
260             }
261         }
262     }
263     attrset(A_NORMAL);
264     refresh();
265 }
266
267 static int
268 GetMove(int *From, int *To)
269 {
270     mvaddstr(STATUSLINE, 0, "Next move ('q' to quit) from ");
271     clrtoeol();
272     refresh();
273     if ((*From = getch()) == 'q')
274         return TRUE;
275     *From -= ('0' + 1);
276     addstr(" to ");
277     clrtoeol();
278     refresh();
279
280     if ((*To = getch()) == 'q')
281         return TRUE;
282     *To -= ('0' + 1);
283     refresh();
284     if (!AutoFlag)
285         napms(500);
286
287     move(STATUSLINE, 0);
288     clrtoeol();
289     refresh();
290     return FALSE;
291 }
292
293 static void
294 MakeMove(int From, int To)
295 {
296     Pegs[From].Count--;
297     Pegs[To].Length[Pegs[To].Count] = Pegs[From].Length[Pegs[From].Count];
298     Pegs[To].Count++;
299     NMoves++;
300     DisplayTiles();
301 }
302
303 static void
304 AutoMove(int From, int To, int Num)
305 {
306     if (Num == 1) {
307         MakeMove(From, To);
308         napms(500);
309         return;
310     }
311     AutoMove(From, OTHER(From, To), Num - 1);
312     MakeMove(From, To);
313     napms(500);
314     AutoMove(OTHER(From, To), To, Num - 1);
315 }
316
317 static int
318 Solved(int NumTiles)
319 {
320     int i;
321
322     for (i = 1; i < NPEGS; i++)
323         if (Pegs[i].Count == NumTiles)
324             return TRUE;
325     return FALSE;
326 }
327
328 static void
329 Usage(void)
330 {
331     fprintf(stderr, "Usage: hanoi [<No Of Tiles>] [a]\n");
332     fprintf(stderr,
333             "The 'a' option causes the tower to be solved automatically\n");
334 }