mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 12:25:10 -08:00
269 lines
6.8 KiB
Lua
269 lines
6.8 KiB
Lua
print [[
|
|
|
|
TOWERS
|
|
CREATIVE COMPUTING MORRISTOWN, NEW JERSY
|
|
|
|
|
|
]]
|
|
|
|
local MAX_DISKS = 7 --"<const>" can be used starting in Lua 5.4, as such: "local MAX_DISKS <const> = 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()
|