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