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 */
18
19 #include <curses.h>
20 #include <stdlib.h>
21 #include <unistd.h>
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 struct Peg Pegs[NPEGS];
44 int PegPos[] = { LEFTPEG, MIDPEG, RIGHTPEG };
45 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 int NMoves = 0;
57
58 void InitTiles(int NTiles);
59 void DisplayTiles(void);
60 void MakeMove(int From, int To);
61 void AutoMove(int From, int To, int Num);
62 void Usage(void);
63 int Solved(int NumTiles);
64 int GetMove(int *From, int *To);
65 int InvalidMove(int From, int To);
66
67 int
main(int argc,char ** argv)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 exit(1);
82 }
83 break;
84 case 3:
85 if (strcmp(argv[2], "a")) {
86 Usage();
87 exit(1);
88 }
89 NTiles = atoi(argv[1]);
90 if (NTiles > MAXTILES || NTiles < MINTILES) {
91 fprintf(stderr, "Range %d to %d\n", MINTILES, MAXTILES);
92 exit(1);
93 }
94 AutoFlag = TRUE;
95 break;
96 default:
97 Usage();
98 exit(1);
99 }
100 #ifdef NCURSES_VERSION
101 trace(TRACE_MAXIMUM);
102 #endif
103 initscr();
104 if (!has_colors()) {
105 endwin();
106 puts("terminal doesn't support color.");
107 exit(1);
108 }
109 start_color();
110 {
111 int i;
112 for (i = 0; i < 9; i++)
113 init_pair(i+1, COLOR_BLACK, TileColour[i]);
114 }
115 cbreak();
116 if (LINES < 24) {
117 endwin();
118 fprintf(stderr, "Min screen length 24 lines\n");
119 exit(1);
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 for(;;) {
133 if(GetMove(&FromCol, &ToCol))
134 break;
135 if(InvalidMove(FromCol, ToCol)) {
136 mvaddstr(STATUSLINE, 0, "Invalid Move !!");
137 refresh();
138 beep();
139 continue;
140 }
141 MakeMove(FromCol, ToCol);
142 if(Solved(NTiles)) {
143 mvprintw(STATUSLINE, 0, "Well Done !! You did it in %d moves", NMoves);
144 refresh();
145 sleep(5);
146 break;
147 }
148 }
149 }
150 curs_set(1);
151 endwin();
152 exit(0);
153 }
154
155 int
InvalidMove(int From,int To)156 InvalidMove(int From, int To)
157 {
158 if(From >= NPEGS)
159 return TRUE;
160 if(From < 0)
161 return TRUE;
162 if(To >= NPEGS)
163 return TRUE;
164 if(To < 0)
165 return TRUE;
166 if(From == To)
167 return TRUE;
168 if(!Pegs[From].Count)
169 return TRUE;
170 if(Pegs[To].Count &&
171 Pegs[From].Length[Pegs[From].Count-1] >
172 Pegs[To].Length[Pegs[To].Count-1])
173 return TRUE;
174 return FALSE;
175 }
176
177 void
InitTiles(int NTiles)178 InitTiles(int NTiles)
179 {
180 int Size, SlotNo;
181
182 for(Size=NTiles*2+1, SlotNo=0; Size>=3; Size-=2)
183 Pegs[0].Length[SlotNo++] = Size;
184
185 Pegs[0].Count = NTiles;
186 Pegs[1].Count = 0;
187 Pegs[2].Count = 0;
188 }
189
190 void
DisplayTiles()191 DisplayTiles()
192 {
193 int Line, Peg, SlotNo;
194 char TileBuf[BUFSIZ];
195
196 erase();
197 mvaddstr(1, 24, "T O W E R S O F H A N O I");
198 mvaddstr(3, 34, "SJR 1990");
199 mvprintw(19, 5, "Moves : %d", NMoves);
200 attrset(A_REVERSE);
201 mvaddstr(BASELINE, 8, " ");
202
203 for(Line=TOPLINE; Line<BASELINE; Line++) {
204 mvaddch(Line, LEFTPEG, ' ');
205 mvaddch(Line, MIDPEG, ' ');
206 mvaddch(Line, RIGHTPEG, ' ');
207 }
208 mvaddch(BASELINE, LEFTPEG, '1');
209 mvaddch(BASELINE, MIDPEG, '2');
210 mvaddch(BASELINE, RIGHTPEG, '3');
211 attrset(A_NORMAL);
212
213 /* Draw tiles */
214 for(Peg=0; Peg<NPEGS; Peg++) {
215 for(SlotNo=0; SlotNo<Pegs[Peg].Count; SlotNo++) {
216 memset(TileBuf, ' ', Pegs[Peg].Length[SlotNo]);
217 TileBuf[Pegs[Peg].Length[SlotNo]] = '\0';
218 attrset(COLOR_PAIR(LENTOIND(Pegs[Peg].Length[SlotNo])));
219 mvaddstr(BASELINE-(SlotNo+1),
220 (int)(PegPos[Peg] - Pegs[Peg].Length[SlotNo]/2),
221 TileBuf);
222 }
223 }
224 attrset(A_NORMAL);
225 refresh();
226 }
227
228 int
GetMove(int * From,int * To)229 GetMove(int *From, int *To)
230 {
231 mvaddstr(STATUSLINE, 0, "Next move ('q' to quit) from ");
232 clrtoeol();
233 refresh();
234 if((*From = getch()) == 'q')
235 return TRUE;
236 addch(*From);
237 *From -= ('0'+1);
238 addstr(" to ");
239 clrtoeol();
240 refresh();
241 if((*To = getch()) == 'q')
242 return TRUE;
243 addch(*To);
244 *To -= ('0'+1);
245 move(STATUSLINE, 0);
246 clrtoeol();
247 refresh();
248 return FALSE;
249 }
250
251 void
MakeMove(int From,int To)252 MakeMove(int From, int To)
253 {
254
255 Pegs[From].Count--;
256 Pegs[To].Length[Pegs[To].Count] = Pegs[From].Length[Pegs[From].Count];
257 Pegs[To].Count++;
258 NMoves++;
259 DisplayTiles();
260 }
261
262 void
AutoMove(int From,int To,int Num)263 AutoMove(int From, int To, int Num)
264 {
265
266 if(Num == 1) {
267 MakeMove(From, To);
268 return;
269 }
270 AutoMove(From, OTHER(From, To), Num-1);
271 MakeMove(From, To);
272 AutoMove(OTHER(From, To), To, Num-1);
273 }
274
275 int
Solved(int NumTiles)276 Solved(int NumTiles)
277 {
278 int i;
279
280 for(i = 1; i < NPEGS; i++)
281 if (Pegs[i].Count == NumTiles)
282 return TRUE;
283 return FALSE;
284 }
285
286 void
Usage()287 Usage()
288 {
289 fprintf(stderr, "Usage: hanoi [<No Of Tiles>] [a]\n");
290 fprintf(stderr, "The 'a' option causes the tower to be solved automatically\n");
291 }
292
293