Tower game in Lua

This commit is contained in:
Rafael Conceição
2023-10-06 11:50:51 +01:00
parent 5941979c35
commit 753327db44

268
90_Tower/lua/train.lua Normal file
View File

@@ -0,0 +1,268 @@
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()