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