|
|
1.1 root 1: /*************************************************************
2:
3: This program implements a tower of hanoi program. This
4: sample app was written to demonstrate the use of a multi-
5: threaded program. The main thread handles the PM interface,
6: the second thread is started to do the recursive execution
7: of the hanoi algorithm.
8:
9: Created by Microsoft, IBM Corporation 1990
10:
11: DISCLAIMER OF WARRANTIES. The following [enclosed] code is
12: sample code created by Microsoft Corporation and/or IBM
13: Corporation. This sample code is not part of any standard
14: Microsoft or IBM product and is provided to you solely for
15: the purpose of assisting you in the development of your
16: applications. The code is provided "AS IS", without
17: warranty of any kind. Neither Microsoft nor IBM shall be
18: liable for any damages arising out of your use of the sample
19: code, even if they have been advised of the possibility of
20: such damages.
21:
22: Procedures in this file:
23: main() Sets up the PM environment and heap and
24: calls the main window procedure ClientWndProc
25: ClientWndProc() Handles the main window messages
26: CalcThread() Sets up and terminates the secondary thread
27: DrawDisk() Draws or erases a disk on one of the poles
28: MoveDisk() Moves a disk from one pole to another
29: Hanoi() Recursive alogrithm for tower of hanoi prog
30: EnableMenuItem() Activates/deactivates a menu choice
31: EntryFldDlgProc() Handles the set number of disks dialog box
32: SetupTowers() Sets up the global tower data
33:
34: **************************************************************/
35:
36: #include "hanoi.h"
37:
38: /********************* GLOBALS *******************************/
39:
40: CHAR szClientClass[] = "Hanoi";
41:
42: /* Note that this use of global data precludes multiple windows
43: of hanoi running at the same time. Thus, from an object-
44: oriented perspective, this is less than desireable and the
45: data should be passed into the window, rather than used
46: explicitly. */
47:
48: BYTE abTowers[3][MAXDISKCNT]; /* Used to hold disk numbers on each post */
49: BYTE abTowersNdx[3]; /* Current number of disks on each post */
50: BYTE cTowerSize = DEFAULTSIZE; /* Holds the total number of disks */
51: USHORT ausPolePos[3]= { POSTOFFSET, /* Holds offset drawing information */
52: POSTOFFSET + POSTSPACE,
53: POSTOFFSET + 2*POSTSPACE };
54: ULONG ulIterations;
55: TID tidCalcThread;
56: BOOL fContinueCalc;
57: HWND hwndFrame, hwndClient;
58:
59:
60: /*************************************************************/
61:
62:
63: /*
64: * Function name: main()
65: *
66: * Parameters: argc, argv. If the user places a number (1 thru MAXDISKCNT)
67: * on the command line, that number will become the default
68: * number of disks on the stack. Otherwise there will be
69: * DEFUALTSIZE disks initially.
70: *
71: * Returns: Always returns 0
72: *
73: * Purpose: Parses the command line, sets up the PM environment, prepares
74: * the Tower arrays, calls the main window proc, handles the
75: * window's messages then cleans up and exits.
76: *
77: * Usage/Warnings:
78: *
79: * Calls: ClientWndProc() (thru PM)
80: */
81:
82: int main(int argc, char *argv[])
83: {
84:
85: HAB hab;
86: HMQ hmq;
87: QMSG qmsg;
88:
89: ULONG flFramefContinueCalcFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_MINBUTTON |
90: FCF_SHELLPOSITION | FCF_MENU | FCF_TASKLIST |
91: FCF_ICON | FCF_BORDER | FCF_ACCELTABLE;
92:
93: SHORT sHold;
94:
95: if(argc > 1) /* If command line arg, use as the initial number of disks */
96: {
97: sHold = atoi(argv[1]);
98: if(sHold>0 && sHold<=MAXDISKCNT)
99: cTowerSize = (BYTE) sHold;
100: }
101: SetupTowers();
102:
103: /* These PM calls should be error checked */
104: hab = WinInitialize(0);
105: hmq = WinCreateMsgQueue(hab, 0);
106:
107: if(!WinRegisterClass(hab, szClientClass,(PFNWP)ClientWndProc,0L,0))
108: {
109: WinAlarm(HWND_DESKTOP, WA_ERROR); /* Register failed */
110: DosExit(EXIT_PROCESS,1);
111: }
112:
113: hwndFrame = WinCreateStdWindow(HWND_DESKTOP, WS_VISIBLE,
114: &flFramefContinueCalcFlags, szClientClass, NULL,
115: 0L, NULL, ID_RESOURCE, &hwndClient);
116: if(!hwndFrame)
117: {
118: WinAlarm(HWND_DESKTOP, WA_ERROR); /* Window create failed */
119: DosExit(EXIT_PROCESS,1);
120: }
121:
122: while(WinGetMsg(hab,&qmsg,NULL,0,0)) /* Message loop */
123: WinDispatchMsg(hab,&qmsg);
124:
125: WinDestroyWindow(hwndFrame); /* Clean up */
126: WinDestroyMsgQueue(hmq);
127: WinTerminate(hab);
128: return 0;
129: }
130:
131:
132: /*
133: * Function name: ClientWndProc()
134: *
135: * Parameters: hwnd, msg, mp1, mp2. Standard PM Window Proc params.
136: * No user data is expected in the WM_CREATE.
137: *
138: * Returns:
139: *
140: * Purpose: Handles all the messages associated with the main window
141: * and calls the appropriate handling procedures.
142: *
143: * Usage/Warnings: Called only by main(). Note that when WM_PAINT executes,
144: * the secondary thread may change data during the update
145: * which may cause a problem. However, this is NOT a write
146: * conflict, as only 1 thread does the writing.
147: *
148: * Calls: DrawDisk(), CalcThread() (thru Thread), EntryFldDlgProc() (thru PM)
149: */
150:
151: MRESULT EXPENTRY ClientWndProc(HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
152: {
153: HPS hps; /* Handle for painting */
154: RECTL rcl; /* Rectangle struct for painting */
155: POINTL ptl; /* Point struct for painting */
156: BYTE cPole,bCnt,bHeight,cnt; /* Utility variables */
157: CHAR szMsg[MSGBUFSIZE]; /* sprintf buffer */
158:
159: switch(msg)
160: {
161: case WM_PAINT:
162: hps = WinBeginPaint(hwnd,NULL,NULL); /* Get paint handle */
163: WinQueryWindowRect(hwnd,&rcl);
164:
165: DrawRect(rcl.xLeft,rcl.yBottom, /* White out the screen */
166: rcl.xRight,rcl.yTop,CLR_WHITE);
167:
168: /* Draw the base */
169: DrawRect(BASEXOFFSET, BASEYOFFSET,
170: BASEXOFFSET+BASELEN-1, BASEYOFFSET+BASETHICK-1,
171: CLR_DARKGREEN);
172:
173: /* Draw the 3 posts */
174: bHeight = cTowerSize*DISKSPACE + POSTEXTRAHT;
175: for(cnt=0;cnt<3;cnt++)
176: {
177: DrawRect(ausPolePos[cnt]-POSTHALF, BASEYOFFSET,
178: ausPolePos[cnt]-POSTHALF+POSTWIDTH,bHeight,
179: CLR_DARKGREEN);
180: }
181:
182: /* Place the appropriate disks on each pole */
183: for(cPole=0;cPole<3;cPole++)
184: {
185: for(bCnt=0;bCnt<abTowersNdx[cPole];bCnt++)
186: {
187: DrawDisk(hps,cPole,bCnt,fDRAW);
188: }
189: }
190:
191: WinEndPaint(hps);
192: return 0L;
193:
194: case WM_COMMAND:
195: switch(SHORT1FROMMP(mp1))
196: {
197: case IDM_START:
198: /* Set continuation fContinueCalc */
199: fContinueCalc = TRUE;
200: ulIterations = 0; /* Zero iteration counter */
201:
202: if (DosCreateThread (&tidCalcThread, CalcThread,
203: (ULONG)NULL, 0L, STACK))
204: {
205: DosBeep (440, 175);
206: return 0L;
207: }
208:
209: /* Disallow menu items that could change data while the second
210: thread is running */
211: EnableMenuItem(hwnd,IDM_START,FALSE); /* Disable Start & set */
212: EnableMenuItem(hwnd,IDM_SET,FALSE);
213: EnableMenuItem(hwnd,IDM_STOP,TRUE); /* Enable Stop item */
214: return 0L;
215:
216: case IDM_STOP:
217: fContinueCalc = FALSE; /* Notify thread to quit */
218: return 0L;
219:
220: case IDM_SET:
221: if(WinDlgBox(HWND_DESKTOP, hwnd, /* Pop up the query/set box */
222: (PFNWP)EntryFldDlgProc,NULL,ID_SETCOUNT,NULL))
223: {
224: SetupTowers(); /* Reset towers */
225: WinInvalidateRect(hwnd,NULL,FALSE); /* Force a redraw */
226: }
227: return 0L;
228: break;
229:
230: case IDM_ABOUT:
231: WinDlgBox(HWND_DESKTOP,
232: hwnd,
233: (PFNWP)AboutDlgProc,
234: NULL,
235: IDD_ABOUTBOX,
236: NULL);
237: break;
238:
239:
240: default:
241: return WinDefWindowProc(hwnd, msg, mp1, mp2);
242: }
243:
244: case UM_CALC_DONE:
245: EnableMenuItem(hwnd,IDM_START,TRUE); /* Reenable Start & set */
246: EnableMenuItem(hwnd,IDM_SET,TRUE);
247: EnableMenuItem(hwnd,IDM_STOP,FALSE); /* Disable stop */
248:
249: sprintf(szMsg,"%lu disks were moved.",ulIterations); /* Print msg */
250: WinMessageBox(HWND_DESKTOP, hwnd, szMsg, "Done!", 0, MB_OK);
251:
252: SetupTowers(); /* Reset towers */
253: WinInvalidateRect(hwnd,NULL,FALSE); /* Force a screen redraw */
254: return 0L;
255:
256: default:
257: return WinDefWindowProc(hwnd, msg, mp1, mp2);
258: }
259: }
260:
261:
262: /*
263: * Function name: CalcThread()
264: *
265: * Parameters: none
266: *
267: * Returns: VOID
268: *
269: * Purpose: Calls the recursive Hanoi with initial setting of 0,2,1 meaning
270: * from pole 0, to pole 2, using pole 1 as a temporary. Hanoi
271: * returns when finished, or the user says stop. This proc then
272: * sets a critical section so the posted message won't be handled
273: * until the thread is terminated.
274: *
275: * Usage/Warnings: No DosExitCritSec() is called since _endthread() supposedly
276: * clears the critical section when the thread is
277: * terminated.
278: *
279: * Calls: Hanoi()
280: */
281:
282: VOID CalcThread ()
283: {
284: HAB hab;
285:
286: hab = WinInitialize(0); /* Called to increase Ring 2 stack size */
287: Hanoi(cTowerSize,0,2,1); /* Execute the recursive routine */
288: WinTerminate(hab);
289:
290: DosEnterCritSec(); /* Set Crit so the UM_CALC_DONE isn't processed */
291: /* until this thread has completely terminated */
292: WinPostMsg(hwndClient,UM_CALC_DONE,NULL,NULL); /* Post done */
293:
294: DosExit (EXIT_THREAD, 0); /* Terminate thread */
295: }
296:
297:
298: /*
299: * Function name: DrawDisk()
300: *
301: * Parameters: hps is a handle to the main PS space.
302: * cPole is the pole (0-2) to draw the disk on.
303: * bLevel is the number of spaces from the bottom to draw disk.
304: * fDraw if =0, erase disk, if =1 draw disk.
305: *
306: * Returns: VOID
307: *
308: * Purpose: This routine takes a PS handle, the hanoi spindle and disk level
309: * and draws an appropriately sized disk.
310: *
311: * Usage/Warnings: Does not grab exclusive access to the screen before
312: * drawing which may cause a problem.
313: *
314: * Calls:
315: */
316:
317: VOID DrawDisk(HPS hps,BYTE cPole, BYTE bLevel,BYTE fDraw)
318: {
319: USHORT usXstart,usYstart,usWidth;
320: POINTL ptl;
321:
322: usYstart = BOTDISKYPOS + DISKSPACE*bLevel; /* Calculate Bottom of disk */
323:
324: usWidth = (MAXDISKWIDTH-MINDISKWIDTH)*abTowers[cPole][bLevel]/cTowerSize
325: + MINDISKWIDTH; /* Calculate the disk's width */
326:
327: usXstart = ausPolePos[cPole] - usWidth/2; /* Center disk on pole */
328:
329: if(fDraw == fDRAW) /* If we are to draw the disk */
330: {
331: DrawRect(usXstart,usYstart,usXstart+usWidth,
332: usYstart+DISKTHICK-1,CLR_RED);
333: }
334: else /* We are to erase the disk, then redraw the pole */
335: {
336: DrawRect(usXstart,usYstart,usXstart+usWidth,
337: usYstart+DISKTHICK-1,CLR_WHITE);
338: DrawRect(ausPolePos[cPole]-POSTHALF,usYstart,
339: ausPolePos[cPole]-POSTHALF+POSTWIDTH,usYstart+DISKTHICK-1,
340: CLR_DARKGREEN);
341: }
342: }
343:
344:
345: /*
346: * Function name: MoveDisk()
347: *
348: * Parameters: hps is a handle to the main PS space.
349: * bFrom is the spindle to take the top disk from.
350: * bTo is the spindle to place the disk on.
351: *
352: * Returns: VOID
353: *
354: * Purpose: This routine moves the top disk from the bFrom spindle to the top
355: * of the bTo spindle.
356: *
357: * Usage/Warnings: Does error checking for trying to move a disk from a
358: * spindle that does not have any disks on it.
359: *
360: * Calls: MoveDisk()
361: */
362:
363: VOID MoveDisk(HPS hps,BYTE bFrom,BYTE bTo)
364: {
365: CHAR bTOSndx; /* Top of stack index */
366: BYTE bDiskNum; /* Disk number to move */
367:
368: bTOSndx = abTowersNdx[bFrom]-1;
369: if(bTOSndx < 0)
370: return;
371:
372: DrawDisk(hps,bFrom,bTOSndx,fERASE); /* Remove disk off from stack */
373:
374: bDiskNum = abTowers[bFrom][bTOSndx]; /* Get move disk number */
375: abTowersNdx[bFrom]--; /* Decrease count on from spindle */
376:
377: bTOSndx = abTowersNdx[bTo]++; /* Offset to place the disk at */
378: abTowers[bTo][bTOSndx] = bDiskNum; /* Place on stack in memory */
379:
380: DrawDisk(hps,bTo,bTOSndx,fDRAW); /* Draw disk on the to stack */
381: }
382:
383:
384: /*
385: * Function name: Hanoi()
386: *
387: * Parameters: pcp is a struct which contains the hwnd handle and the
388: * continue fContinueCalcFlag which is initially set to TRUE.
389: * bHeight is the number of disks in the from stack to move.
390: * bFrom is the from spindle number, 0-2.
391: * bTo is the to spindle number.
392: * bTemp is the temporary spindle number.
393: *
394: * Returns: VOID
395: *
396: * Purpose: This routine implements a recursive hanoi program that works as
397: * follows: By recursion, move all the disks, except for the
398: * bottom disk to the temporary stack. Then move the bottom
399: * disk to the target spindle. Now recursively move the stack
400: * on the temporary spindle to the target spindle. The limiting
401: * case is when Hanoi() is called with a bHeight of 0 in which
402: * case the depth recursion is terminated.
403: *
404: * Usage/Warnings: This routine checks the ->fContClac fContinueCalcFlag, which is set
405: * by the main thread when the user selects stop, to see if
406: * the user wishes to abort the algorithm. If so, it backs
407: * out and exits.
408: *
409: * Calls: MoveDisk()
410: */
411:
412: VOID Hanoi(BYTE bHeight, BYTE bFrom, BYTE bTo, BYTE bTemp)
413: {
414: HPS hps;
415:
416: if(bHeight<=0 || !fContinueCalc) /* Exit up if no more disks or */
417: return; /* the user said Stop */
418:
419: Hanoi(bHeight-1,bFrom,bTemp,bTo); /* Move all but bottom disk */
420:
421: if(!fContinueCalc) /* If user said to stop */
422: return;
423:
424: /* Display bFrom -> bTo */
425: hps = WinGetPS(hwndClient);
426: MoveDisk(hps,bFrom,bTo); /* Move the bottom disk */
427: WinReleasePS(hps);
428: ulIterations++;
429:
430: Hanoi(bHeight-1,bTemp,bTo,bFrom); /* Move disks over */
431: }
432:
433:
434: /*
435: * Function name: EnableMenuItem()
436: *
437: * Parameters: hwnd is a handle of the current window.
438: * sMenuItem is the ID of the item to Enable/Disable.
439: * fEnable will enable item if TRUE, otherwise disables it.
440: *
441: * Returns: VOID
442: *
443: * Purpose: This routine handles enabling/disabling of menu items. This
444: * is done by getting Parent and Menu hwnd handles then sending
445: * the appropriate message.
446: *
447: * Usage/Warnings:
448: *
449: * Calls:
450: */
451:
452: VOID EnableMenuItem(HWND hwnd, SHORT sMenuItem, BOOL fEnable)
453: {
454: HWND hwndParent = WinQueryWindow(hwnd, QW_PARENT, FALSE);
455: HWND hwndMenu = WinWindowFromID(hwndParent, FID_MENU);
456:
457: WinSendMsg(hwndMenu, MM_SETITEMATTR,
458: MPFROM2SHORT(sMenuItem, TRUE),
459: MPFROM2SHORT(MIA_DISABLED, fEnable ? 0 : MIA_DISABLED));
460: }
461:
462:
463: /*
464: * Function name: EntryFldDlgProc()
465: *
466: * Parameters: hwnd, msg, mp1, mp2. Standard PM Dialog Proc params.
467: * No user data is expected in the WM_CREATE.
468: *
469: * Returns: Terminates with a TRUE iff a new valid Tower Size has been entered.
470: *
471: * Purpose: Handles all the messages associated with the set entry field
472: * and calls the appropriate handling procedures. The purpose
473: * of this dialog box is to get a new number of disks for the
474: * hanoi routine.
475: *
476: *
477: * Usage/Warnings: If the value entered is valid, global cTowerSize is
478: * changed to the new value, and TRUE is returned.
479: *
480: * Calls:
481: */
482:
483: MRESULT EXPENTRY EntryFldDlgProc(HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
484: {
485: SHORT sNewSize; /* Holds new number of disks */
486:
487: switch(msg)
488: {
489: case WM_INITDLG:
490: WinSendDlgItemMsg(hwnd, ID_ENTRYFLD,EM_SETTEXTLIMIT, /* Limit len */
491: MPFROM2SHORT(2,0),NULL);
492: WinSetDlgItemShort(hwnd, ID_ENTRYFLD,(SHORT) cTowerSize,TRUE);
493: return 0L; /* Allow normal focus setting */
494:
495: case WM_COMMAND:
496: switch(SHORT1FROMMP(mp1))
497: {
498: case DID_OK:
499: WinQueryDlgItemShort(hwnd, ID_ENTRYFLD,
500: &sNewSize, TRUE); /* Get the short */
501: if(sNewSize>0 && sNewSize<=MAXDISKCNT) /* Set new Tower size */
502: {
503: cTowerSize = (BYTE) sNewSize;
504: WinDismissDlg(hwnd,TRUE);
505: }
506: else /* Invalid value */
507: {
508: WinMessageBox (HWND_DESKTOP, hwndFrame, "You may only select up to 16 disks",
509: "OOPS! Too many disks!", ID_MSGBOX, MB_OK |
510: MB_ICONEXCLAMATION);
511: }
512:
513: return 0L;
514:
515: case DID_CANCEL:
516: WinDismissDlg(hwnd,FALSE);
517: return 0L;
518:
519: default:
520: return WinDefDlgProc(hwnd, msg, mp1, mp2);
521: }
522:
523: default:
524: return WinDefDlgProc(hwnd, msg, mp1, mp2);
525: }
526: }
527:
528: /*
529: * Function name: AboutDlgProc()
530: *
531: * Parameters: hwnd, msg, mp1, mp2. Standard PM Dialog Proc params.
532: * No user data is expected in the WM_CREATE.
533: *
534: * Returns:
535: *
536: * Purpose: Handles all the messages associated with the About Box
537: *
538: *
539: * Usage/Warnings:
540: *
541: * Calls:
542: */
543:
544: MRESULT EXPENTRY AboutDlgProc(HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
545: {
546:
547: switch(msg)
548: {
549: case WM_COMMAND:
550: WinDismissDlg(hwnd, TRUE);
551: break;
552:
553: default:
554: return WinDefDlgProc(hwnd, msg, mp1, mp2);
555: }
556: }
557:
558: /*
559: * Function name: SetupTowers()
560: *
561: * Parameters: None
562: *
563: * Returns: VOID
564: *
565: * Purpose: This routine initializes the global arrays that represent the
566: * hanoi stacks. This involves placing all the disks on the
567: * first peg, emptying the other 2 pegs and setting the associated
568: * counts.
569: *
570: * Usage/Warnings: Calling uses the global variable cTowerSize to determine
571: * how many disks there are.
572: *
573: * Calls:
574: */
575:
576: VOID SetupTowers()
577: {
578: USHORT cnt;
579:
580: for(cnt=0;cnt<cTowerSize;cnt++) /* Setup the intial post with disks */
581: abTowers[0][cnt] = cTowerSize-cnt-1;
582:
583: abTowersNdx[0] = cTowerSize; /* Set disk count for initial post */
584: abTowersNdx[1] = abTowersNdx[2] = 0; /* Zero other post counts */
585: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.