mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 12:25:10 -08:00
Tower game in Lua
This commit is contained in:
268
90_Tower/lua/train.lua
Normal file
268
90_Tower/lua/train.lua
Normal 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()
|
||||
Reference in New Issue
Block a user