ncurses 5.0
[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.16 1999/10/23 15:01:01 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                 curs_set(0);
123                 leaveok(stdscr, TRUE);  /* Attempt to remove cursor */
124         }
125         InitTiles(NTiles);
126         DisplayTiles();
127         if(AutoFlag) {
128                 do {
129                         noecho();
130                         AutoMove(0, 2, NTiles);
131                 } while(!Solved(NTiles));
132                 sleep(2);
133         } else {
134                 echo();
135                 for(;;) {
136                         if(GetMove(&FromCol, &ToCol))
137                                 break;
138                         if(InvalidMove(FromCol, ToCol)) {
139                                 mvaddstr(STATUSLINE, 0, "Invalid Move !!");
140                                 refresh();
141                                 beep();
142                                 continue;
143                         }
144                         MakeMove(FromCol, ToCol);
145                         if(Solved(NTiles)) {
146                                 mvprintw(STATUSLINE, 0, "Well Done !! You did it in %d moves", NMoves);
147                                 refresh();
148                                 sleep(5);
149                                 break;
150                         }
151                 }
152         }
153         endwin();
154         return EXIT_SUCCESS;
155 }
156
157 static int
158 InvalidMove(int From, int To)
159 {
160         if(From >= NPEGS)
161                 return TRUE;
162         if(From < 0)
163                 return TRUE;
164         if(To >= NPEGS)
165                 return TRUE;
166         if(To < 0)
167                 return TRUE;
168         if(From == To)
169                 return TRUE;
170         if(!Pegs[From].Count)
171                 return TRUE;
172         if(Pegs[To].Count &&
173                         Pegs[From].Length[Pegs[From].Count-1] >
174                         Pegs[To].Length[Pegs[To].Count-1])
175                 return TRUE;
176         return FALSE;
177 }
178
179 static void
180 InitTiles(int NTiles)
181 {
182         int Size, SlotNo;
183
184         for(Size=NTiles*2+1, SlotNo=0; Size>=3; Size-=2)
185                 Pegs[0].Length[SlotNo++] = Size;
186
187         Pegs[0].Count = NTiles;
188         Pegs[1].Count = 0;
189         Pegs[2].Count = 0;
190 }
191
192 static void
193 DisplayTiles(void)
194 {
195         int Line, peg, SlotNo;
196         char TileBuf[BUFSIZ];
197
198         erase();
199         mvaddstr(1, 24, "T O W E R S   O F   H A N O I");
200         mvaddstr(3, 34, "SJR 1990");
201         mvprintw(19, 5, "Moves : %d", NMoves);
202         attrset(A_REVERSE);
203         mvaddstr(BASELINE, 8, "                                                               ");
204
205         for(Line=TOPLINE; Line<BASELINE; Line++) {
206                 mvaddch(Line, LEFTPEG, ' ');
207                 mvaddch(Line, MIDPEG, ' ');
208                 mvaddch(Line, RIGHTPEG, ' ');
209         }
210         mvaddch(BASELINE, LEFTPEG, '1');
211         mvaddch(BASELINE, MIDPEG, '2');
212         mvaddch(BASELINE, RIGHTPEG, '3');
213         attrset(A_NORMAL);
214
215         /* Draw tiles */
216         for(peg=0; peg<NPEGS; peg++) {
217                 for(SlotNo=0; SlotNo<Pegs[peg].Count; SlotNo++) {
218                         memset(TileBuf, ' ', Pegs[peg].Length[SlotNo]);
219                         TileBuf[Pegs[peg].Length[SlotNo]] = '\0';
220                         if (has_colors())
221                                 attrset(COLOR_PAIR(LENTOIND(Pegs[peg].Length[SlotNo])));
222                         else
223                                 attrset(A_REVERSE);
224                         mvaddstr(BASELINE-(SlotNo+1),
225                                 (int)(PegPos[peg] - Pegs[peg].Length[SlotNo]/2),
226                                 TileBuf);
227                 }
228         }
229         attrset(A_NORMAL);
230         refresh();
231 }
232
233 static int
234 GetMove(int *From, int *To)
235 {
236         mvaddstr(STATUSLINE, 0, "Next move ('q' to quit) from ");
237         clrtoeol();
238         refresh();
239         if((*From = getch()) == 'q')
240                 return TRUE;
241         *From -= ('0'+1);
242         addstr(" to ");
243         clrtoeol();
244         refresh();
245
246         if((*To = getch()) == 'q')
247                 return TRUE;
248         *To -= ('0'+1);
249         refresh();
250         napms(500);
251
252         move(STATUSLINE, 0);
253         clrtoeol();
254         refresh();
255         return FALSE;
256 }
257
258 static void
259 MakeMove(int From, int To)
260 {
261         Pegs[From].Count--;
262         Pegs[To].Length[Pegs[To].Count] = Pegs[From].Length[Pegs[From].Count];
263         Pegs[To].Count++;
264         NMoves++;
265         DisplayTiles();
266 }
267
268 static void
269 AutoMove(int From, int To, int Num)
270 {
271         if(Num == 1) {
272                 MakeMove(From, To);
273                 napms(500);
274                 return;
275         }
276         AutoMove(From, OTHER(From, To), Num-1);
277         MakeMove(From, To);
278         napms(500);
279         AutoMove(OTHER(From, To), To, Num-1);
280 }
281
282 static int
283 Solved(int NumTiles)
284 {
285         int i;
286
287         for(i = 1; i < NPEGS; i++)
288                 if (Pegs[i].Count == NumTiles)
289                         return TRUE;
290         return FALSE;
291 }
292
293 static void
294 Usage()
295 {
296         fprintf(stderr, "Usage: hanoi [<No Of Tiles>] [a]\n");
297         fprintf(stderr, "The 'a' option causes the tower to be solved automatically\n");
298 }