Variables Resetting When Using Parallel Function in Lua with ComputerCraft - function

I am a beginner designing a Lua program in ComputerCraft (Minecraft) that asks the player for their name the first time they use it, and records it. For now, I want a program that detects if the variable firstnameis equal to nil and asks for the name if it is so. If the variable is not equal to nil, it says that you do not need to register. I currently have register()called only after pressing 5from the main menu.
The problem is that each time I assign a string to firstnameupon being prompted, firstnamereturns to nil when the main menu comes up. I even put print(firstname)at the end of the menu to test this.
I have assumed this is due to the parallel function that this all runs in. I run MainMenu() and Controls()in parallel so that I can listen for keyboard input and redstone input at the same time.
How can I keep both functions listening and the menu working, while preserving variables?
Here is the full code:
rednet.open("back") --Opens rednet on the back side of computer
local innerdooropen = false --Stuff to do with the airlock
local outerdooropen = false
function reset() --A handy function for screen reset
term.clear()
term.setCursorPos(1,1)
end
local function register() --This is the registration menu system
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
local firstname = read()
if firstname ~= "" then
print("Enter your last name")
local lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)
rednet.broadcast(firstname.." "..lastname)
elseif firstname == "" then
print "You must register to enter"
shell.run("startup")
end
end
if firstname ~= nil then
print("Registration Not Needed")
sleep(2)
end
end
--Beginning of Section You Don't Have to Read
local function MainMenu()
while true do
term.clear()
term.setCursorPos(1, 1)
if innerdooropen == true then
rs.setOutput("left", false)
end
if outerdooropen == true then
rs.setOutput("right", false)
end
if innerdooropen == false then
rs.setOutput("left", true)
end
if outerdooropen == false then
rs.setOutput("right", true)
end
print "Safety Airlock Control"
print "~~~~~~~~~~~~~~~~~~~~~~"
print "[1] Open Outer Door"
print "[2] Open Inner Door"
print "[3] Close Both Doors"
print ""
print "[4] Open Both Doors - WARNING! DANGEROUS!"
print ""
print "[5] Register"
print(firstname)
input = read()
if input == "2" then
print "Inner Door Open"
outerdooropen = false
sleep "1"
innerdooropen = true
end
if input == "1" then
print "Outer Door Open"
innerdooropen = false
sleep "1"
outerdooropen = true
end
if input == "3" then
print "Both Doors Closed"
innerdooropen = false
outerdooropen = false
end
if input == "5" then
reset()
register()
end
if input == "6" then
print("firstname: "..firstname)
sleep(3)
end
if input == "4" then
term.clear()
term.setCursorPos(1, 1)
print "CONFIRM BOTH DOORS OPEN? [y] [n]"
input = read()
if input == "y" then
print "OPENING AIRLOCK DOORS IN"
sleep "1"
print "10"
sleep "1"
print "9"
sleep "1"
print "8"
sleep "1"
print "7"
sleep "1"
print "6"
sleep "1"
print "5"
sleep "1"
print "4"
sleep "1"
print "3"
sleep "1"
print "2"
sleep "1"
print "1"
sleep "1"
innerdooropen = true
outerdooropen = true
print "DOORS OPEN"
sleep "1"
end
elseif input == "n" then
term.clear()
term.setCursorPos(1, 1)
shell.run("startup")
end
end
end
--end of section you don't have to read
local function Controls()
while true do
local e = os.pullEvent()
if e == "redstone" and rs.getInput("bottom") then
redstone.setOutput ("left", true)
sleep "1"
redstone.setOutput ("right", false)
innerdooropen = true
outerdooropen = false
end
end
end
while true do
parallel.waitForAll(MainMenu,Controls)
end

Initialize firstname outside of the parallel. Put local firstname up at the top of your code, and change local firstname = read() to just firstname = read(), and do the same for last name.
You are creating the variable after you check whether or not it is nil, which is why it always returns nil. Similarly, when the function ends, firstname no longer exists, since it is called and created inside of the function. So it will always return nil.
The top of your code should look like so
rednet.open("back") --Opens rednet on the back side of computer
local innerdooropen = false --Stuff to do with the airlock
local outerdooropen = false
local firstname = ""
local lastname = ""
And the other section should be as follows:
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
firstname = read()
if firstname ~= "" then
print("Enter your last name")
lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)

You should initialize firstname before you check if it is equal to nil.
local firstname = nil; --Move this up here so that you can access it elsewhere.
local lastname = nil; --This is also needed out here.
local function register() --This is the registration menu system
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
firstname = read() --Change this to just setting firstname to the read name.
if firstname ~= "" then
print("Enter your last name")
lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)
rednet.broadcast(firstname.." "..lastname)
elseif firstname == "" then
print "You must register to enter"
shell.run("startup")
end
end
if firstname ~= nil then --Now this will not error as well.
print("Registration Not Needed")
sleep(2)
end
end
This allows for firstname to be accessed elsewhere and not just within that first if statement. You were checking if an uninitialized variable was equal to nil or not.
EDIT: Saw purxiz's answer and realized that you also probably want lastname to be outside of the loop. I fixed it in my code above now.

Related

I've stumped myself on my login project and suggestions with the order of my code! it is a login project

So I have the code written I think and it works but I am not sure how to order it properly so for example in my menu function when they enter 2 to end the program it will end completely as after I call this function I need to call the others but at the moment it while just run the next part of the code
print("----------------------Welcome User----------------------\n\n")
# this function is used for apperance in the program
def linebreak():
print("---------------------------------------------------------")
# Menu for user to choose what they would like to do
# this functuon gives the user a choice from the menu above if they want to log in it will break out of
# the loop and go onto the next block of code below
def menu():
while True:
print("-----------------------Main Menu------------------------\n")
print("To login enter 1:")
print("To end the program enter 2:")
print("")
try:
sUserSelection = int(input("What would you like to do? \n"))
# make a conditional statement to see if the user wants to start the login program or exit
if sUserSelection == 1:
break
elif sUserSelection == 2:
print("Ending program, Goodbye!")
break
except:
print("ERROR! Invalid choice please choose from the menu to continue or exit the program.\n")
menu()
menu()
# This is a function that will return two print statements one to tell the user that their login was successful
# And it will then print greet with that users inputted name
def welcome_user():
print("Login successful. ")
print("Welcome ")
# Create a function for the admin login
sAdmin = "admin"
sAdminPassWord = "aDmin002"
def admin_login():
print("Please login to the administrator account to create your new personal account: \n")
while True:
sUserInput = input("Please enter admin username: \n")
sUserInput = sUserInput.lower()
if sUserInput == sAdmin:
break
else:
print("Wrong username please try again! \n")
while True:
sPassInput = input("Please enter admin password: \n")
if sPassInput == sAdminPassWord:
print("Login Successful")
break
else:
print("Wrong password please try again! \n")
# this function allows the user to create a new account starting with the username and then
# it asks for the creation of a password
def new_account():
print("\nPlease create a new account.\n")
while True:
global sNewUser1
sNewUser1 = input("\nPlease enter a new username.\n"
"It should be at least 5 characters long\n"
"and not contain spaces or special characters: \n\n")
if len(sNewUser1) < 5:
print("Your username is too short. Please try again: ")
elif sNewUser1.count(" ") > 0:
print("Your username contains spaces. Please try again: ")
elif sNewUser1.isalnum() is False:
print("Your username contains a special character. "
"Please try again: ")
else:
# call another function
break
while True:
global sNewPass1
sNewPass1 = input("\n\nPlease enter a new password.\n"
"It should be at least 6 characters long\n"
"with at least one uppercase letter,\n"
"one lowercase letter, one special character"
"and one number: \n\n")
if len(sNewPass1) < 6:
print("\nYour password is too short. Please try again: ")
elif any(lower.islower() for lower in sNewPass1) is False:
print("\nYour password does not contain lowercase letters. "
"Please try again: ")
elif any(upper.isupper() for upper in sNewPass1) is False:
print("\nYour password does not contain uppercase letters. "
"Please try again: ")
elif any(digit.isdigit() for digit in sNewPass1) is False:
print("\nYour password does not contain a number. "
"Please try again: ")
elif any(not char.isalnum() for char in sNewPass1) is False:
print("\nYour password does not contain a special character. "
"Please try again: ")
elif sNewPass1.replace(" ", "") != sNewPass1:
print("\nYour password contains whitespaces. "
"Please try again: ")
else:
# call another function
break
def name_selection():
print("Account created sucessfully!")
global sName1
sName1 = input("Please enter your name:\n")
def user_login():
while True:
sUserLogin = input("Please enter your username:\n")
if sUserLogin == sNewUser1:
break
else:
print("Wrong username please try again:\n")
while True:
sUserPass = input("Please enter your password:\n")
if sUserPass == sNewPass1:
print("\nWelcome: ", sName1)
break
# # calls the menu function
menu()
# # calls the admin login function
admin_login()
# calls the function to create a new account
new_account()
# calls the name creation function after an account has been made
name_selection()
# calls the function to allow the user to login using there credentials
user_login()

How can I run through all these ifs in python?

When I run the program It will only run the first If and make those specific changes. Noticed when i switched them around and only the first one gives me what I want... Thanks for the help.
if SW1 != r['SW1']: #check the received value of SW1 & change it on the App if there is a mismatch
print("Changing SW1 status to the value in the database.")
if self.sw1.active == True:
self.sw1.active = False
else:
self.sw1.active = True
else:
return
if LED1 != r['LED1']: #check the received value of led1 & change it on the App if there is a mismatch
print("Changing LED1 status to the value in the database.")
if self.led1.active == True:
self.led1.active = False
else:
self.led1.active = True
else:
return
if LED2 != r['LED2']: #check the received value of led2 & change it on the App if there is a mismatch
print("Changing LED2 status to the value in the database.")
if self.led2.active == True:
self.led2.active = False
else:
self.led2.active = True
else:
if LED3 != r['LED3']: #check the received value of led3 & change it on the App if there is a mismatch
print("Changing LED3 status to the value in the database.")
if self.led3.active == True:
self.led3.active = False
else:
self.led3.active = True
else:
return
You should not return in else after every if. This will make the function to close after the first if is failed. I will explain it further with one example.
Take this function which checks if a number is even.
def foo_bar(n):
if n%2==0:
print("Even")
else:
return
print("I have been reached")
If an even number is passed, you will see the below output
>>> foo_bar(10)
Even
I have been reached
If the Odd number is passed, you will not see any output as the function is returning None and Terminated in else.
Now if you have multiple ifs in the function,
def foo_bar(n):
if n%2==0:
print("Even")
else:
return
if n%3==0:
print("Divisible by 3")
print("I have been reached")
If you pass 9 as an argument, the above function prints nothing. It is because after one condition is checked , you are returning None which terminates the function.
Hope this answers your question.

Lua - Concatinate several arguments passed into a function, until condition is met, then pass string to next function

pvMessage is sent from another function, the message often comes in a few parts almost instantly. I am looking to store the pvMessages and concatinate the message to the last. Therefore a master string is created with all parts.
Example.
pvMessage #1 = thisispart1msg
pvMessage #2 = now part two is being received
pvMessage #3 = Part 3
MasterMessage = thisispart1msgnow part two is being receivedPart 3
I have tried several attempts at solving this issue. The storing of the message outside the function is proving harder then I though, I keep overwriting the previous message.
function ProcessClientMessage( pvMessage )
if StartMessage == "" then
StartMessage = pvMessage
pvMessage = ""
end
if pvMessage ~= "" then
if MiddleMessage == "" then
MiddleMessage = pvMessage
pvMessage = StartMessage .. MiddleMessage
pvMessage = ""
end
end
if pvMessage ~= "" then
if EndMessage == "" then
EndMessage = pvMessage
pvMessage = StartMessage .. MiddleMessage .. EndMessage
pvMessage = ""
end
end
if pvMessage ~= "" then
ProcessClientMessageReset()
end
end
If there are always three parts that you want to concatenate, something like this may work:
local buffer = {}
local function ProcessClientMessage(pvMessage)
if #buffer < 3 then
table.insert(buffer, pvMessage)
else
--ProcessClientMessageReset(table.concat(buffer, ""))
print(table.concat(buffer, ""))
buffer = {}
end
end
ProcessClientMessage("thisispart1msg")
ProcessClientMessage("now part two is being received")
ProcessClientMessage("Part 3")
ProcessClientMessage("more thisispart1msg")
ProcessClientMessage("some now part two is being received")
ProcessClientMessage("Part 4")
This should print:
thisispart1msgnow part two is being receivedPart 3
more thisispart1msgsome now part two is being receivedPart 4
This problem also be solved with coroutines:
local function consumer(consume)
print(consume()..consume()..consume())
return consumer(consume)
end
local process = coroutine.wrap(consumer)
process(coroutine.yield)
process('foo')
process('bar')
process('baz')
process('hello')
process('world')
process('test')
EDIT: As Egor pointed out, the order of evaluation isn't technically defined (although it's left to right in every Lua implementation I'm aware of) so here's an alternative that would work even if your interpreter was doing gcc -O3-like optimizations:
local function consumer(consume)
local a = consume()
local b = consume()
local c = consume()
print(a .. b .. c)
return consumer(consume)
end
You can make use of the "or" assignment to simplify initialization of the global variable and then concatenate the string to the result. Consider:
function ProcessClientMessage(msg)
msg_global_ = (msg_global_ or "")..msg
print(msg_global_) -- Just for debug purposes so we can print each step
end
do
ProcessClientMessage("thisispart1msg")
ProcessClientMessage("now part two is being received")
ProcessClientMessage("Part 3")
end
The variable msg_global_ contains the string being built. If it has not been added too yet, then it will be nil. In this case the or "" will be executed and default the string empty.
We then simply append the string msg.
The output looks like:
thisispart1msg
thisispart1msgnow part two is being received
thisispart1msgnow part two is being receivedPart 3
When you actually process the message, just set the global variable to nil and you are good to go again.

Lua - Error repeating a function?

I'm working on an 'impossible' game, where you are basically asked a bunch of trick questions.
The first time I run the script, everything works perfectly. If the user decides to play again by typing in 'y', it will re-run the mainScript function. However, the script automatically re-runs the mainScript after finishing it a second time, without taking user input. I might be making a simple mistake, I'm not sure. Here is the script: (Sorry, I know it is kind of long)
math.randomseed(os.time())
local lives = 3
local points = 0
Questions = {
{"What is the magic word?", "A) Please", "B) Abra-Cadabra", "C) Lotion", "D) Cheese", "c"},
{"Does anyone love you?", "A) Yes", "B) No", "C) Everyone loves me!", "D) My mother does", "b"},
{"How many fingers do you have?", "A) None", "B) Eight", "C) Seventeen", "D) Ten", "d"},
{"What is 1 + 1?", "A) Window", "B) Door", "C) Two", "D) That's a stupid question", "a"}
}
savedQuestions = {} --Load the Questions table into savedQuestions
for i, v in pairs(Questions) do
table.insert(savedQuestions, v)
end
function loadTable() --Load the savedQuestions into Questions
for i = 1, #savedQuestions do
table.insert(Questions, savedQuestions[i])
end
end
function waitForStart()
local chk = io.read() tostring(chk)
if (chk:sub(1, 5)):lower() == "start" then
return true
end
waitForStart()
end
function lookForAnswer(ans)
table.remove(Questions, number)
local input = io.read() tostring(input)
if input:lower() == ans then
points = points + 1
return true
end
lives = lives - 1
return false
end
function mainScript()
lives = 3
points = 0
print("Welcome to the Impossible quiz!")
print("Type 'start' when you are ready to begin\n")
waitForStart() io.write("\n")
for i = 1, #Questions do
number = math.random(1, #Questions)
local prob = Questions[number]
local q = prob[1]
local a = prob[6]
print(q)
print(prob[2] .. "\n" .. prob[3] .. "\n" .. prob[4] .. "\n" .. prob[5] .. "\n")
if lookForAnswer(a) then
print("Correct! Points: " .. points .. " Lives: " .. lives .. "\n\n")
else
print("WRONG! Points: " .. points .. " Lives: " .. lives .. "\n\n")
if lives <= 0 then
return false
end
end
end
return true
end
function checkForReplay()
print("Would you like to play again? (Y / N)")
local chk = io.read() tostring(chk)
if (chk:sub(1, 1)):lower() == "y" then
return true
end
return false
end
function checkWin()
if mainScript() then
print("You won!")
print("Points: " .. points .. "\n")
if checkForReplay() then
Questions = {}
loadTable()
mainScript()
else
exit()
end
else
print("You lose!")
print("Points: " .. points .. "\n")
if checkForReplay() then
Questions = {}
loadTable()
mainScript()
else
exit()
end
end
end
while true do
checkWin()
end
You should factor out the "startover" logic into the loop, you would see better. Then you notice how there is common code in both if chunks of your checkWin, factor that out too:
function checkWin()
if mainScript() then
print("You won!")
else
print("You lose!")
end
print("Points: " .. points .. "\n")
if not checkForReplay() then
exit()
end
end
while true do
checkWin()
-- if you get here you have not exited so start over:
Questions = {}
loadTable()
mainScript() -- oops! what's that doing here?
end
Note also that it is better to let the script return than call os.exit() (assuming that is what exit() is in your code -- see for example How to terminate Lua script?):
function checkWin()
if mainScript() then
print("You won!")
else
print("You lose!")
end
print("Points: " .. points .. "\n")
return checkForReplay()
end
local playAgain = checkWin()
while playAgain do
Questions = {}
loadTable()
playAgain = checkWin()
end

Lua: How to execute different blocks depending on conditions?

I have this table:
no_table ={
{a="3", b="22", c="18", d="ABC"},
{a="4", b="12", c="25", d="ABC"},
{a="5", b="15", c="16", d="CDE"},
}
This function:
function testfoo()
i = 1
while no_table[i] ~= nil do
foo(no_table[i])
i = i + 1
end
end
and the foo function:
function foo(a,b,c,d)
if no_table[i][4] ~= no_table[i-1][4]
then
print (a+b)
elseif no_table[i][4] == no_table[i-1][4]
then
print (b+c)
end
end
Can you help me find? :
A way to be able to check if the two tables are or not equal (currently it gives me cannot index nil)
A way to execute only the "print (b+c)" code if the equality is true, or if is not true then both "print (a+b)" first and "print (b+c) secondly without duplicating the code.
Lots of problems I'm seeing in this. First, I'd never rely on i being set in an external function, it really should be a local variable and passed as a parameter if you need it. That said, you need to check if no_table[x] exists before trying to access no_table[x][y]. So, for foo you'd have:
function foo(a,b,c,d)
if not (no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4])
then
print (a+b)
elseif no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (b+c)
end
end
Also, for numbers in the table, if you want to do arithmetic, you need to remove the quotes:
no_table ={
{a=3, b=22, c=18, d="ABC"},
{a=4, b=12, c=25, d="ABC"},
{a=5, b=15, c=16, d="CDE"},
}
Next, in testfoo, you're passing a table, so you either need to split out the values of a, b, c, and d on your function call, or you can just pass the table itself and handle that in foo:
function foo(t)
if not (no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4])
then
print (t.a+t.b)
elseif no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (t.b+t.c)
end
end
This results in:
> testfoo()
25
37
31
Edit: One final cleanup, since the conditions are the same, you can use an else rather than an elseif:
function foo(t)
if no_table[i] and no_table[i-1] and no_table[i][4] == no_table[i-1][4]
then
print (t.b+t.c)
else
print (t.a+t.b)
end
end