print [[ TOWERS CREATIVE COMPUTING MORRISTOWN, NEW JERSY ]] local MAX_DISKS = 7 local MAX_DISK_SIZE = 15 local MAX_MOVES = 128 local NUM_TOWERS = 3 local towers = { { size = 0, elem = {} }, { size = 0, elem = {} }, { size = 0, elem = {} }, } local total_disks function ask_how_many_disks() local keep_asking = true local input local errors = 0 while keep_asking do io.write(string.format("HOW MANY DISKS DO YOU WANT TO MOVE (%d IS MAX)? ", MAX_DISKS) ) input = io.read("*number") io.read() --get rid of the remaining newline character if input ~= nil and input>0 and input<=MAX_DISKS then keep_asking = false else errors = errors + 1 if errors > 2 then print "ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL" print "JUST TAKE MY PUZZLE AND GO HOME. SO LONG." os.exit() end print "SORRY, BUT I CAN'T DO THAT JOB FOR YOU." end end total_disks = input end function init_game() print("TOWERS OF HANOIR PUZZLE\n") --'\n' indicates a new line print "YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT TOWER," print "ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A SMALLER DISK.\n" ask_how_many_disks() print() -- print() already creates a new line at the end, so an empty print leaves an empty line print "IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE." print "3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE, 7 THE" print "NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH 2 DISKS," print "THEIR CODE NAMES WOULD BE 13 AND 15, ETC. THE NEEDLES ARE" print "NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL START WITH THE" print "DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM TO NEEDLE 3.\n" print "GOOD LUCK!\n" local i = 1 local max = MAX_DISK_SIZE while i<= total_disks do towers[1].elem[i] = max max = max-2 i = i+1 end towers[1].size = total_disks local idx = 2 while idx <= NUM_TOWERS do towers[idx].size = 0 end end function print_towers() local line = MAX_DISKS while line > 0 do local twr = 1 while twr <=3 do local rpt = 10 local offset = 0 if line <= towers[twr].size then offset = (towers[twr].elem[line] - 1) / 2 rpt = rpt - offset end io.write( string.rep(' ', rpt) ) io.write( string.rep('*', offset) ) io.write '*' io.write( string.rep('*', offset) ) io.write( string.rep(' ', rpt) ) twr = twr + 1 end print '' line = line - 1 end end function ask_which_disk() local keep_asking = true local input local errors = 0 while keep_asking do io.write("WHICH DISK WOULD YOU LIKE TO MOVE? ") input = io.read("*number") io.read() --get rid of the remaining newline character if input==nil or input > MAX_DISK_SIZE or input%2==0 or input <= MAX_DISK_SIZE-(total_disks*2) then print "ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13 or 15." errors = errors + 1 if errors > 1 then print "STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE." os.exit() end --[[ Since there are only 3 towers, it's easier to do an 'if' with three conditions than to do a loop ]] elseif towers[1].elem[ towers[1].size ] ~= input and towers[2].elem[ towers[2].size ] ~= input and towers[3].elem[ towers[3].size ] ~= input then print "THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE." else keep_asking = false end end return input end function ask_which_needle(dsk) local keep_asking = true local input local errors = 0 while keep_asking do io.write("PLACE DISK ON WHICH NEEDLE? ") input = io.read("*number") io.read() --get rid of the remaining newline character if input~=nil and towers[input].size > 0 and towers[input].elem[ towers[input].size ] < dsk then print "YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE," print "IT MIGHT CRUSH IT!" return 0 elseif input~=nil and input>=1 and input<=3 then keep_asking = false else errors = errors + 1 if errors > 1 then print "I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN." print "BYE BYE, BIG SHOT." os.exit() --Stop program else print "I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT," print "I ONLY ALLOW ONE MISTAKE." end end end return input end function is_game_over() if towers[1].size == 0 and towers[2].size == 0 then return true else return false end end function game_loop() local moves = 0 local dsk local twr_to local twr_fr while not is_game_over() do moves = moves + 1 if moves > MAX_MOVES then print(string.format("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN %d MOVES.", MAX_MOVES)) os.exit() end repeat dsk = ask_which_disk() twr_to = ask_which_needle(dsk) until twr_to ~= 0 if towers[1].elem[ towers[1].size ] == dsk then twr_fr = 1 elseif towers[2].elem[ towers[2].size ] == dsk then twr_fr = 2 else twr_fr = 3 end towers[twr_fr].size = towers[twr_fr].size - 1 towers[twr_to].size = towers[twr_to].size + 1 towers[twr_to].elem[ towers[twr_to].size ] = dsk print_towers() end return moves end function keep_playing() while true do io.write("TRY AGAIN (YES OR NO)? ") local input = io.read("*line") if input == "YES" or input == "yes" then return true elseif input == "NO" or input == "no" then return false else print "'YES' OR 'NO' PLEASE" end end end function start_loop() while true do init_game() print_towers() local moves = game_loop() --check ideal solution if moves == (2^total_disks) - 1 then print "CONGRATULATIONS!!" end print ( string.format("YOU HAVE PERFORMED THE TASK IN %d MOVES.\n", moves) ) if not keep_playing() then break end end print "\nTHANKS FOR THE GAME!" end start_loop()