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