0b032f8318cae2d51c9c220f788bae278b122963
[ncurses.git] / test / hanoi.c
1 /*
2  *      Name: Towers of Hanoi.
3  *
4  *      Desc:
5  *              This is a playable copy of towers of hanoi.
6  *              Its sole purpose is to demonstrate my Amiga Curses package.
7  *              This program should compile on any system that has Curses.
8  *              'hanoi'         will give a manual game with 7 playing pieces.
9  *              'hanoi n'       will give a manual game with n playing pieces.
10  *              'hanoi n a' will give an auto solved game with n playing pieces.
11  *
12  *      Author: Simon J Raybould        (sie@fulcrum.bt.co.uk).
13  *      (This version has been slightly modified by the ncurses maintainers.)
14  *
15  *      Date: 05.Nov.90
16  *
17  * $Id: hanoi.c,v 1.19 2000/09/02 18:51:16 tom Exp $
18  */
19
20 #include <test.priv.h>
21
22 #include <string.h>
23
24 #define NPEGS                   3       /* This is not configurable !! */
25 #define MINTILES                3
26 #define MAXTILES                9
27 #define DEFAULTTILES    7
28 #define TOPLINE                 6
29 #define BASELINE                16
30 #define STATUSLINE              (LINES-3)
31 #define LEFTPEG                 19
32 #define MIDPEG                  39
33 #define RIGHTPEG                59
34
35 #define LENTOIND(x)             (((x)-1)/2)
36 #define OTHER(a,b)              (3-((a)+(b)))
37
38 struct Peg {
39     size_t Length[MAXTILES];
40     int Count;
41 };
42
43 static struct Peg Pegs[NPEGS];
44 static int PegPos[] =
45 {LEFTPEG, MIDPEG, RIGHTPEG};
46 static int TileColour[] =
47 {
48     COLOR_GREEN,                /* Length 3 */
49     COLOR_MAGENTA,              /* Length 5 */
50     COLOR_RED,                  /* Length 7 */
51     COLOR_BLUE,                 /* Length 9 */
52     COLOR_CYAN,                 /* Length 11 */
53     COLOR_YELLOW,               /* Length 13 */
54     COLOR_GREEN,                /* Length 15 */
55     COLOR_MAGENTA,              /* Length 17 */
56     COLOR_RED,                  /* Length 19 */
57 };
58 static int NMoves = 0;
59
60 static void InitTiles(int NTiles);
61 static void DisplayTiles(void);
62 static void MakeMove(int From, int To);
63 static void AutoMove(int From, int To, int Num);
64 static void Usage(void);
65 static int Solved(int NumTiles);
66 static int GetMove(int *From, int *To);
67 static int InvalidMove(int From, int To);
68
69 int
70 main(int argc, char **argv)
71 {
72     int NTiles, FromCol, ToCol;
73     unsigned char AutoFlag = 0;
74
75     switch (argc) {
76     case 1:
77         NTiles = DEFAULTTILES;
78         break;
79     case 2:
80         NTiles = atoi(argv[1]);
81         if (NTiles > MAXTILES || NTiles < MINTILES) {
82             fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
83             return EXIT_FAILURE;
84         }
85         break;
86     case 3:
87         if (strcmp(argv[2], "a")) {
88             Usage();
89             return EXIT_FAILURE;
90         }
91         NTiles = atoi(argv[1]);
92         if (NTiles > MAXTILES || NTiles < MINTILES) {
93             fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
94             return EXIT_FAILURE;
95         }
96         AutoFlag = TRUE;
97         break;
98     default:
99         Usage();
100         return EXIT_FAILURE;
101     }
102 #ifdef TRACE
103     trace(TRACE_MAXIMUM);
104 #endif
105     initscr();
106     if (has_colors()) {
107         int i;
108         int bg = COLOR_BLACK;
109         start_color();
110 #if HAVE_USE_DEFAULT_COLORS
111         if (use_default_colors() == OK)
112             bg = -1;
113 #endif
114         for (i = 0; i < 9; i++)
115             init_pair(i + 1, bg, TileColour[i]);
116     }
117     cbreak();
118     if (LINES < 24) {
119         endwin();
120         fprintf(stderr, "Min screen length 24 lines\n");
121         return EXIT_FAILURE;
122     }
123     if (AutoFlag) {
124         curs_set(0);
125         leaveok(stdscr, TRUE);  /* Attempt to remove cursor */
126     }
127     InitTiles(NTiles);
128     DisplayTiles();
129     if (AutoFlag) {
130         do {
131             noecho();
132             AutoMove(0, 2, NTiles);
133         } while (!Solved(NTiles));
134         sleep(2);
135     } else {
136         echo();
137         for (;;) {
138             if (GetMove(&FromCol, &ToCol))
139                 break;
140             if (InvalidMove(FromCol, ToCol)) {
141                 mvaddstr(STATUSLINE, 0, "Invalid Move !!");
142                 refresh();
143                 beep();
144                 continue;
145             }
146             MakeMove(FromCol, ToCol);
147             if (Solved(NTiles)) {
148                 mvprintw(STATUSLINE, 0,
149                          "Well Done !! You did it in %d moves", NMoves);
150                 refresh();
151                 sleep(5);
152                 break;
153             }
154         }
155     }
156     endwin();
157     return EXIT_SUCCESS;
158 }
159
160 static int
161 InvalidMove(int From, int To)
162 {
163     if (From >= NPEGS)
164         return TRUE;
165     if (From < 0)
166         return TRUE;
167     if (To >= NPEGS)
168         return TRUE;
169     if (To < 0)
170         return TRUE;
171     if (From == To)
172         return TRUE;
173     if (!Pegs[From].Count)
174         return TRUE;
175     if (Pegs[To].Count &&
176         Pegs[From].Length[Pegs[From].Count - 1] >
177         Pegs[To].Length[Pegs[To].Count - 1])
178         return TRUE;
179     return FALSE;
180 }
181
182 static void
183 InitTiles(int NTiles)
184 {
185     int Size, SlotNo;
186
187     for (Size = NTiles * 2 + 1, SlotNo = 0; Size >= 3; Size -= 2)
188         Pegs[0].Length[SlotNo++] = Size;
189
190     Pegs[0].Count = NTiles;
191     Pegs[1].Count = 0;
192     Pegs[2].Count = 0;
193 }
194
195 static void
196 DisplayTiles(void)
197 {
198     int Line, peg, SlotNo;
199     char TileBuf[BUFSIZ];
200
201     erase();
202     mvaddstr(1, 24, "T O W E R S   O F   H A N O I");
203     mvaddstr(3, 34, "SJR 1990");
204     mvprintw(19, 5, "Moves : %d", NMoves);
205     attrset(A_REVERSE);
206     mvaddstr(BASELINE, 8,
207              "                                                               ");
208
209     for (Line = TOPLINE; Line < BASELINE; Line++) {
210         mvaddch(Line, LEFTPEG, ' ');
211         mvaddch(Line, MIDPEG, ' ');
212         mvaddch(Line, RIGHTPEG, ' ');
213     }
214     mvaddch(BASELINE, LEFTPEG, '1');
215     mvaddch(BASELINE, MIDPEG, '2');
216     mvaddch(BASELINE, RIGHTPEG, '3');
217     attrset(A_NORMAL);
218
219     /* Draw tiles */
220     for (peg = 0; peg < NPEGS; peg++) {
221         for (SlotNo = 0; SlotNo < Pegs[peg].Count; SlotNo++) {
222             memset(TileBuf, ' ', Pegs[peg].Length[SlotNo]);
223             TileBuf[Pegs[peg].Length[SlotNo]] = '\0';
224             if (has_colors())
225                 attrset(COLOR_PAIR(LENTOIND(Pegs[peg].Length[SlotNo])));
226             else
227                 attrset(A_REVERSE);
228             mvaddstr(BASELINE - (SlotNo + 1),
229                      (int) (PegPos[peg] - Pegs[peg].Length[SlotNo] / 2),
230                      TileBuf);
231         }
232     }
233     attrset(A_NORMAL);
234     refresh();
235 }
236
237 static int
238 GetMove(int *From, int *To)
239 {
240     mvaddstr(STATUSLINE, 0, "Next move ('q' to quit) from ");
241     clrtoeol();
242     refresh();
243     if ((*From = getch()) == 'q')
244         return TRUE;
245     *From -= ('0' + 1);
246     addstr(" to ");
247     clrtoeol();
248     refresh();
249
250     if ((*To = getch()) == 'q')
251         return TRUE;
252     *To -= ('0' + 1);
253     refresh();
254     napms(500);
255
256     move(STATUSLINE, 0);
257     clrtoeol();
258     refresh();
259     return FALSE;
260 }
261
262 static void
263 MakeMove(int From, int To)
264 {
265     Pegs[From].Count--;
266     Pegs[To].Length[Pegs[To].Count] = Pegs[From].Length[Pegs[From].Count];
267     Pegs[To].Count++;
268     NMoves++;
269     DisplayTiles();
270 }
271
272 static void
273 AutoMove(int From, int To, int Num)
274 {
275     if (Num == 1) {
276         MakeMove(From, To);
277         napms(500);
278         return;
279     }
280     AutoMove(From, OTHER(From, To), Num - 1);
281     MakeMove(From, To);
282     napms(500);
283     AutoMove(OTHER(From, To), To, Num - 1);
284 }
285
286 static int
287 Solved(int NumTiles)
288 {
289     int i;
290
291     for (i = 1; i < NPEGS; i++)
292         if (Pegs[i].Count == NumTiles)
293             return TRUE;
294     return FALSE;
295 }
296
297 static void
298 Usage()
299 {
300     fprintf(stderr, "Usage: hanoi [<No Of Tiles>] [a]\n");
301     fprintf(stderr,
302             "The 'a' option causes the tower to be solved automatically\n");
303 }