i am currently scripting an elevator and i have reached a problem where when a button is clicked and the elevator is in use the button will not work because i have a variable active set to true when the elevator is in use so i would like to know what loop would be useful to use so that when a button is clicked and the elevator is active it will still eventually proceed to run its function after the button is pushed?
some pseudo code of what im dealing with here looks like this
1 button is pushed 2 if active = false continue 3 active = true 4 elevator moves to designated floor 5 active = false 6 else nothing happens 7 end
but if another button is pushed while it is active u will see that it will just not work so how could i possibly troubleshoot this issue that has come up?
This is a really poorly done question on your part -- You haven't described what you're doing or exactly what you have in mind.
I'm going to assume you have an elevator, and either an "up", "down", and "stop" button, and a "call to this floor" button (maybe a few of these).
That means the elevator has a few states: moving up, moving down, or stopped.
Based on the last button that was pressed, it has a particular state (or we could get more complicated).
At this point, there are two approaches. Since the important state changes are when you press buttons, it might actually be best to not have some constant loop and just do work when the buttons are pressed. This gets a little complicated, but I'll outline it first, because it sometimes looks nice.
The other option is for something to constantly be looking at the elevator's state and make sure it is appropriate based on the last button pressed. I'll do this second.
I'm going to use a "hypothetical" elevator where the only thing I do is set a variable velocity
-- 1 for up, -1 for down, 0 for stopped.
function stopButton() velocity = 0 end function upButton() velocity = 1 end function downButton() velocity = -1 end
This is remarkably simple -- no loops required.
Now, let's consider a "call" button. For this, we need to know where the elevator is. We'll use y
to mean the elevator's position (but we won't set y
, only read from it). I'll use ty
as the "target y" (the y of the given floor) in this event:
function callTo(ty) if ty == y then -- You would actually want a small -- tolerance, since it probably won't ever be exact velocity = 0 elseif ty < y then velocity = -1 else velocity = 1 end end
This will make the elevator go towards the floor, but it won't stop when it gets there.
An idiomatic way would be to use a while
loop (though a repeat until
would be equivalent)
function callTo(ty) while ty ~= y do -- again, a tolerance would be better if ty < y then velocity = -1 else velocity = 1 end wait() -- Some pause, to not freeze end velocity = 0 end
This will work -- but it will only work with 1 call at a time.
One simple solution is the debounce pattern - we essentially "disable" calling as long as it is still going to its floor:
canCall = true function callTo(ty) if not canCall then return end canCall = false -- No one *else* can call while ty ~= y do --..... end velocity = 0 canCall = true -- Now someone else can call end
This may be what we want, but it may not. We may want to interrupt the current action whenever we press a button (this would be more sensical to most users, though probably isn't what a real elevator would do)
To do that, we will keep track of something like a reverse debounce, where any other call will inform all previous ones to stop operating.
controller = nil -- which call is controlling the elevator? function callTo(ty) local myControl = math.random() -- Or, equivalently, = {}, which I like for succinctness controller = myControl while controller == myControl and ty ~= y do -- .... (same body) end velocity = 0 end
Now, whenever callTo
gets called, it changes controller
, but it doesn't change myControl
-- so controller == myControl
becomes false and the old call stops working.
The final design that we have is essentially just one loop -- it just gets traded off between different calls to the function with a different ty
variable.
With that in mind, we can make a much simpler system:
target = 0 -- ground floor? function callTo(ty) target = ty -- Store most recent call to target end while true do wait() if y == target then velocity = 0 elseif y < target then velocity = -1 else velocity = 1 end end
This is all we need. If we wanted to put some sort of "debounce", we could do that, too:
target = 0 function callTo(ty) target = ty end local realTarget = 0 while true do wait() if y == realTarget then velocity = 0 elseif y < realTarget then velocity - 1 else velocity = 1 end if velocity == 0 then -- I'm at my floor, so now I can go to the next one -- (Maybe also a `wait(1)` in here to stay at the -- floor for a least a moment?) realTarget = target -- Accept the most recent button press. end end
One interesting difference between this and the debounce above is that this will still hear when you press. It just waits until it's done, and then does it. (You could, with the above system, do this by instead repeat wait() until canCall
, but this would be a problem if multiple buttons were pressed in that time -- since they might all start more or less at the same time but don't check the debounce -- or, worse, make a big stack of different locations to track to -- so the elevator would be "frozen" in an odd path for however many times people pressed buttons)
I would like to think that real elevators aren't so dumb. I will write more about this shortly.
Here is what I would suggest a real elevator does:
Keeps a list of all floors that need to be visited. A floor needs to be visited because either it was called to that floor, or because that floor was indicated while inside the elevator. I am not going to make any distinction about priority between the two types of calls, though you could if you wanted to.
Whenever a floor that needs to be visited is passed, stop and open the doors -- then continue on.
Here is what it might look like:
-- finds `val` in table `tab`, returning position found -- warning: false is a valid key. -- Returns `nil` when not found. function find(tab, val) for i,v in pairs(tab) do if v == val then return i end end end local visits = {} -- Floors to visit -- Guaranteed to not contain repeats of any value function callTo(ty) if not find(visits, ty) then -- If the list doesn't already contain this target, -- then add it. table.insert(visits, ty) end end local target = y -- Ground floor? while true do wait() if find(visits, y) then -- I'm on a floor that's wanted! -- (Thus the elevator will stop on its way rather than -- go all the way up then back down to floors in -- the middle) open = true -- Open doors wait(1) open = false -- Close doors table.remove(visits, find(visits, y)) -- remove this floor from the list to visit end if y == target then if #visits > 0 then target = table.remove(1) -- Attend to oldest first. else -- No floors to visit. Stop and wait velocity = 0 end end end