;*********************************************************************** ;* * ;* HANOI.ASP (C) 1990 DATASTORM TECHNOLOGIES, INC. * ;* * ;* ------------------------------------------------------------------- * ;* * ;* * ;* This is a 'Towers of Hanoi' program written in ASPECT to * ;* demonstrate many features of the new PROCOMM PLUS 2.0 * ;* ASPECT language. THIS SCRIPT REQUIRES PROCOMM PLUS 2.0 - * ;* DO NOT ATTEMPT TO RUN IT UNDER ANY PREVIOUS VERSION! * ;* * ;* HANOI.ASP is also a good demonstration of recursive processes. * ;* A recursive process is one that CALLs itself to divide the * ;* work to be done. Recursive processing is a useful way to * ;* solve certain types of problems where the work is repetitive, * ;* can be easily divided and intermediate results don't depend * ;* on each other. * ;* * ;* In 'Towers of Hanoi', there are 3 poles and a given number of * ;* disks; you must move each of the disks from the leftmost pole * ;* (pole 1) to the rightmost pole (pole 3). You must move one * ;* disk at a time without ever placing a larger disk on top of a * ;* smaller disk. To solve the problem, you must use pole 2 as a * ;* temporary 'holding area'. * ;* * ;* This program allows you to set the number of disks that will * ;* be used, and will allow you to either try and solve the * ;* Towers of Hanoi, or will provide a demonstration for you. * ;* * ;* Written by Rob Greenberg on 9/25/90. * ;* * ;*********************************************************************** define SOLID 7 define BLANK 0 define FALSE 0 define TRUE 1 define FROM 1 define TO 2 define NORMAL 7 define REV 112 define ESC 27 ;---------------------------------------------------- ; Store our representation of "poles" in strings. - ;---------------------------------------------------- string pole1="00000000", pole2="00000000", pole3="00000000", bar integer number_of_disks, move_number, keypress, termcolors, demo, exithanoi ;------------------------------------------------------------------------ ; The main procedure -- calls opening screen and the HANOI procedure. - ;------------------------------------------------------------------------ proc main integer diskval1, diskval2, done, counter, from_pole_num, to_pole_num integer high_disk1, high_disk2, high_pos, valid string fm_pole_string, to_pole_string call setup setjmp 0 exithanoi if exithanoi == -1 call cleanup endif call opening_screen ;------------------------------------------- ; If the user chose 'demo', do the demo. - ;------------------------------------------- if demo == TRUE call hanoi with number_of_disks, &pole1, &pole2, &pole3, 1, 2, 3 else ;-------------------------------------------- ; The user wants to play, so stay in this - ; loop until the user finishes the game. - ;-------------------------------------------- while TRUE ;-------------------------------------- ; See if poles 1 and 2 are empty... - ;-------------------------------------- done = TRUE for counter = 7 downto 0 strpeek pole1 counter diskval1 strpeek pole2 counter diskval2 if (diskval1 != 48) || (diskval2 != 48) done = FALSE exitfor endif endfor ;------------------------------ ; If we're done, then exit. - ;------------------------------ if done == TRUE exitwhile else valid = FALSE while valid == FALSE ;------------------------------------------- ; Get the poles to move 'from' and 'to', - ; and the addresses of their strings. - ;------------------------------------------- scroll 0 21 20 22 60 NORMAL call get_pole with &from_pole_num, &fm_pole_string, FROM call get_pole with &to_pole_num, &to_pole_string, TO ;-------------------------------------- ; Where are the disks on each pole? - ;-------------------------------------- call get_highest_disk with &high_disk1, high_pos, fm_pole_string call get_highest_disk with &high_disk2, high_pos, to_pole_string ;-------------------------- ; Is this a valid move? - ;-------------------------- if ((high_disk1 <= 0) || ((high_disk1 >= high_disk2) && high_disk2 > 0)) scroll 0 21 20 22 60 NORMAL sound 1000,40 atsay 22 21 NORMAL "Invalid move! Press a key to try again." keyget keypress scroll 0 21 20 22 60 NORMAL else valid = TRUE endif endwhile ;---------------------------------------------- ; Move whatever disk the user said to move. - ;---------------------------------------------- call move_disk with &fm_pole_string, &to_pole_string, from_pole_num, to_pole_num ;---------------------------------------------- ; Update our master copies of our 'poles'. - ;---------------------------------------------- call update_string with from_pole_num, fm_pole_string call update_string with to_pole_num, to_pole_string endif endwhile scroll 0 21 20 22 60 NORMAL endif call cleanup endproc ;------------------------------------------------------------------- ; The recursive function that subdivides work by calling itself. - ;------------------------------------------------------------------- proc hanoi intparm h_number_of_disks strparm h_pole1_string, h_pole2_string, h_pole3_string intparm pole1_num, pole2_num, pole3_num integer disks_to_move if h_number_of_disks == 1 call move_disk with &h_pole1_string, &h_pole3_string, pole1_num, pole3_num else disks_to_move = h_number_of_disks - 1 call hanoi with disks_to_move, &h_pole1_string, &h_pole3_string, &h_pole2_string, pole1_num, pole3_num, pole2_num call move_disk with &h_pole1_string, &h_pole3_string, pole1_num, pole3_num call hanoi with disks_to_move, &h_pole2_string, &h_pole1_string, &h_pole3_string, pole2_num, pole1_num, pole3_num endif endproc ;=========================================== ; = ; "Worker" functions to support "Hanoi". = ; = ;=========================================== ;------------------------------------------------- ; The function that will 'move a disk' for us. - ;------------------------------------------------- proc move_disk strparm fm_pole_string, to_pole_string intparm fm_pole_num, to_pole_num integer fm_high_disk, to_high_disk, fm_high_pos, to_high_pos, disk_display integer temp_disk, sound_value move_number++ ;---------------------------------------------- ; Find the highest 'disk' on 'source' pole. - ;---------------------------------------------- call get_highest_disk with &fm_high_disk, &fm_high_pos, fm_pole_string ;----------------------------------------- ; Convert character disk # to decimal. - ;----------------------------------------- disk_display = fm_high_disk - 48 if demo == TRUE fatsay 21 21 NORMAL "Move %d -- disk %d from pole %d to pole %d" move_number,disk_display,fm_pole_num,to_pole_num endif ;----------------------------------------- ; Erase highest disk on 'source' pole. - ;----------------------------------------- strpoke fm_pole_string fm_high_pos '0' temp_disk = fm_high_pos + 1 sound_value = (disk_display * 50) - 20 sound sound_value, 12 call display_disk with fm_pole_num, temp_disk, disk_display, BLANK ;--------------------------------------------------- ; Find the highest 'disk' on 'destination' pole. - ;--------------------------------------------------- call get_highest_disk with &to_high_disk, &to_high_pos, to_pole_string ;----------------------------------------------------- ; Move disk into empty slot on 'destination' pole. - ;----------------------------------------------------- to_high_pos++ strpoke to_pole_string to_high_pos fm_high_disk temp_disk = to_high_pos + 1 call display_disk with to_pole_num, temp_disk, disk_display, SOLID if demo == TRUE keyget keypress if keypress == ESC longjmp 0 -1 endif endif endproc ;---------------------------------------------------------------- ; This function will return the highest disk on a given pole. - ; If there are no disks on the pole, it will return a -1. - ;---------------------------------------------------------------- proc get_highest_disk intparm high_disk, high_pos strparm search_pole integer counter strlen search_pole counter counter-- high_pos = 99 for counter downto 0 strpeek search_pole counter high_disk if high_disk != 48 high_pos = counter exitfor endif endfor if high_pos == 99 high_pos = -1 high_disk = -1 endif endproc ;--------------------------------------------------- ; Given a pole, position, and disk size, this - ; function displays the disk. It also erases a - ; given disk if the attribute is set to 'BLANK'. - ;--------------------------------------------------- proc display_disk intparm bar_number, disk_position, disk_size, attribute integer row, column, counter, position, display_num row = 19 - (disk_position * 2) column = (bar_number * 20) - disk_size display_num = disk_size disk_size = (disk_size * 2) + 1 if attribute != BLANK attribute = display_num + 8 endif for counter = 1 upto disk_size position = column + counter - 1 atsay row position attribute "±" endfor ;----------------------------------------------- ; If we're erasing a disk, fixup the pole. - ; If we're drawing a disk, print the number. - ;----------------------------------------------- column = bar_number * 20 if attribute == BLANK atsay row column SOLID "Û" else fatsay row column SOLID "%d" display_num endif endproc ;--------------------------------------------------- ; Prompt the user for a pole to move, and return - ; a copy of the pole "string" in POLE_STRING. - ;--------------------------------------------------- proc get_pole intparm pole_num strparm pole_string intparm direction while TRUE if direction == FROM scroll 0 21 20 21 60 NORMAL atsay 21 22 NORMAL "Move a disk FROM which pole? (1-3):" atget 21 58 NORMAL 1 pole_num if failure longjmp 0 -1 endif else scroll 0 22 20 22 60 NORMAL atsay 22 22 NORMAL "Move a disk TO which pole? (1-3):" atget 22 58 NORMAL 1 pole_num if failure longjmp 0 -1 endif endif switch pole_num case 1 pole_string = pole1 exitwhile endcase case 2 pole_string = pole2 exitwhile endcase case 3 pole_string = pole3 exitwhile endcase default scroll 0 23 20 23 60 NORMAL sound 1000, 40 atsay 23 22 NORMAL "You must choose 1-3. Press a key..." keyget keypress scroll 0 23 20 23 60 NORMAL endcase endswitch endwhile endproc ;----------------------------------------- ; Given a 'local' copy of a string, - ; update the master copy of the string. - ;----------------------------------------- proc update_string intparm polenum strparm pole_string switch polenum case 1 pole1 = pole_string endcase case 2 pole2 = pole_string endcase case 3 pole3 = pole_string endcase endswitch endproc ;================================================= ; = ; Routines for the opening and closing screens = ; = ;================================================= ;------------------------------------------- ; Setup Hanoi... - ;------------------------------------------- proc setup strset bar 223 60 strpoke bar 61 0 move_number = 0 demo = TRUE set keys on fetch termnorm termcolors set statline off curoff endproc ;---------------------------------------------------- ; This function displays the opening screen, asks - ; the user how many disks to use, displays them, - ; and asks the user if they want to play or demo. - ;---------------------------------------------------- proc opening_screen integer counter1, counter2, column, disk_position, disk_size, offset, temp string choice clear NORMAL atsay 0 25 REV " T O W E R S O F H A N O I " atsay 2 8 NORMAL "*****************************************************************" atsay 3 8 NORMAL "* This is a 'Towers of Hanoi' program written in ASPECT to *" atsay 4 8 NORMAL "* demonstrate recursive processing. *" atsay 5 8 NORMAL "* *" atsay 6 8 NORMAL "* A recursive process is one that CALLs itself to divide the *" atsay 7 8 NORMAL "* work to be done. Recursive processing is a useful way to *" atsay 8 8 NORMAL "* solve certain types of problems where the work is repetitive, *" atsay 9 8 NORMAL "* can be easily divided and intermediate results don't depend *" atsay 10 8 NORMAL "* on each other. *" atsay 11 8 NORMAL "* *" atsay 12 8 NORMAL "* In 'Towers of Hanoi', there are 3 poles and a given number of *" atsay 13 8 NORMAL "* disks; you must move each of the disks from the leftmost pole *" atsay 14 8 NORMAL "* (pole 1) to the rightmost pole (pole 3). You must move one *" atsay 15 8 NORMAL "* disk at a time without ever placing a larger disk on top of a *" atsay 16 8 NORMAL "* smaller disk. To solve the problem, you must use pole 2 as a *" atsay 17 8 NORMAL "* temporary 'holding area'. *" atsay 18 8 NORMAL "* *" atsay 19 8 NORMAL "* This program allows you to set the number of disks that will *" atsay 20 8 NORMAL "* be used, and will allow you to either try and solve the *" atsay 21 8 NORMAL "* Towers of Hanoi, or will provide a demonstration for you. *" atsay 22 8 NORMAL "*****************************************************************" atsay 24 28 REV "Hit any key to continue." keyget keypress clear NORMAL atsay 1 25 REV " T O W E R S O F H A N O I " ;---------------------- ; Draw the "poles". - ;---------------------- atsay 19 10 NORMAL bar for counter1 = 1 upto 3 column = counter1 * 20 for counter2 = 3 upto 18 atsay counter2 column NORMAL "Û" endfor endfor ;----------------------------- ; Get the number of disks. - ;----------------------------- while TRUE atsay 21 20 NORMAL "Enter the number of disks to use (1-8):" atget 21 60 NORMAL 1 number_of_disks if failure longjmp 0 -1 endif if (number_of_disks < 1) || (number_of_disks > 8) sound 1000,40 atsay 22 25 NORMAL "Invalid selection. Try again!" else exitwhile endif endwhile ;----------------------------------- ; 'Place' the disks onto pole 1. - ;----------------------------------- for counter1 = number_of_disks downto 1 offset = number_of_disks - counter1 temp = counter1 + 48 strpoke pole1 offset temp endfor ;------------------------------------- ; Display the disks on the screen. - ;------------------------------------- for disk_position = 1 upto number_of_disks disk_size = number_of_disks + 1 - disk_position call display_disk with 1, disk_position, disk_size, SOLID endfor ;-------------------------------------------- ; Ask user if they want to play or watch. - ;-------------------------------------------- scroll 0 21 20 22 60 NORMAL atsay 21 22 NORMAL "Play the game or see the demo (P/D)?" atget 21 59 REV 1 choice strupr choice if failure longjmp 0 -1 endif find choice "P" if found demo = FALSE endif ;-------------------------------------------- ; Erase any text that might be left over. - ;-------------------------------------------- scroll 0 21 10 22 70 NORMAL atsay 22 17 NORMAL "Hit [ENTER] to begin the game. [ESC] will quit." keyget keypress scroll 0 22 10 23 70 NORMAL if demo == TRUE atsay 23 22 NORMAL "Hit [ENTER] to continue, [ESC] to quit" endif endproc ;---------------------------------------------- ; Prepare for the return to Procomm Plus... - ;---------------------------------------------- proc cleanup if exithanoi scroll 0 21 20 23 70 NORMAL else scroll 0 22 20 23 70 NORMAL if demo == FALSE fatsay 21 22 NORMAL "Congratulations! You won in %d moves!" move_number endif endif atsay 23 22 NORMAL "Hit [ENTER] to return to Procomm Plus" kflush keyget keypress ;---------------------------------------------------------- ; Restore status line, cursor, colors for Procomm Plus. - ;---------------------------------------------------------- set statline on curon clear termcolors exit endproc