Multi-threading functions in Computer Craft - lua

I'm working on a project where I want to update the clock on screen say every 5 seconds unless the user inputs something. This is the code I have so far,
function thread1()
term.clear()
term.setCursorPos(1,1)
write (" SteveCell ")
local time = os.time()
local formatTime = textutils.formatTime(time, false)
write (formatTime)
print ("")
print ("")
for i=1,13 do
write ("-")
end
print("")
print ("1. Clock")
print ("2. Calender")
print ("3. Memo")
print ("4. Shutdown")
for i=1,13 do
write ("-")
end
print ("")
print ("")
write ("Choose an option: ")
local choice = io.read()
local choiceValid = false
if (choice == "1") then
-- do this
elseif (choice == "2") then
-- do that
elseif (choice == "3") then
-- do this
elseif (choice == "4") then
shell.run("shutdown")
else
print ("Choice Invalid")
os.sleep(2)
shell.run("mainMenu")
end
end
function thread2()
localmyTimer = os.startTimer(5)
while true do
local event,timerID = os.pullEvent("timer")
if timerID == myTimer then break end
end
return
end
parallel.waitForAny(thread1, thread2)
shell.run("mainMenu")
Unfortunately it's not working. If someone could help me with this, I would really appreciate it. Thanks :)

You want to do something like this (Im not doing the correct on screen drawing, only the time)
local function thread1_2()
-- both threads in one!
while true do
local ID_MAIN = os.startTimer(5)
local ID = os.startTimer(0.05)
local e = { os.pullEvent() }
if e[1] == "char" then
-- Check all the options with variable e[2] here
print( string.format( "Pressed %s", e[2] ) )
break -- Getting out of the 'thread'
elseif e[1] == "timer" and e[2] == ID then
ID = os.startTimer(0.05) -- shortest interval in cc
redrawTime() -- Redraw and update the time in this function!!!
elseif e[1] == "timer" and e[2] == MAIN_ID then
break
end
end
end
Also, ask this in the proper forum, you have more chance getting an answer there!
Another note, get more into event handling, it really helps.

FYI Lua doesn't have 'multi-threading' as in executing multiple routines simultaneously. What it does have is 'thread parking.' You can switch between routines (yielding) and switch back and it will resume where it left off, but only a single routine will be active at any given time.
This is my go-to Lua reference, which explains in detail:
http://lua-users.org/wiki/CoroutinesTutorial

Related

Trying to impliment the minimax algorithm, but I get an error suggesting it cant compare a nil value to a int

Here is the code I made sure to comment the minimax functions so I can make it easier for you guys to understand. I have tried to give it a go and try to understand what each bit does and I am not confident on what each step is doing or if it is doing what I want correctly.
I should say I tried following this tutorial (https://www.freecodecamp.org/news/minimax-algorithm-guide-how-to-create-an-unbeatable-ai/), I think the website did a good job about explaining how minimax works but the code used in the tutorial is written in a different language I have tried my best to convert it to my lua tic tac toe game, without the AI/minimax algorithm the game is a great 2 player game, but I wanted to challenge my self and give a go at making an ai for tic tac toe, this is how far I have gone.
local board_data = {top_L= " ", top_M = " ", top_R= " ", mid_L= " ", mid_M= " ", mid_R= " ", low_L= " ", low_M= " ",
low_R= " "}
local location = {"top","mid","low"}
local position = {"_L","_M","_R"}
local function find_empty_slots(board_info)
local empty_slots = {}
for key, value in pairs(board_info) do
if value == " " then
table.insert(empty_slots,key)
end
end
return empty_slots
end
local function _draw_board() -- Draws the games table
print(
board_data["top_L"].."┃"..board_data["top_M"].."┃"..board_data["top_R"].."\n"..
"------\n"..
board_data["mid_L"].."┃"..board_data["mid_M"].."┃"..board_data["mid_R"].."\n"..
"------\n"..
board_data["low_L"].."┃"..board_data["low_M"].."┃"..board_data["low_R"].."\n"
)
end
local function _check_if_position_exists_and_is_empty(_input) -- Checks if the user has entered a valid position and that it is empty
return board_data[_input] == " "
end
local function _check_win_condition(current_turn) --Checks if the current player has won
for i = 1, 3 do --Firstly checks each rows
if board_data[location[i]..position[1]] == current_turn and board_data[location[i]..position[2]] == current_turn and board_data[location[i]..position[3]] == current_turn then
return true --Then checks columns
elseif board_data[location[1]..position[i]] == current_turn and board_data[location[2]..position[i]] == current_turn and board_data[location[3]..position[i]] == current_turn then
return true
end -- Then checks diagonals
if board_data[location[1]..position[1]] == current_turn and board_data[location[2]..position[2]] == current_turn and board_data[location[3]..position[3]] == current_turn then
return true
elseif board_data[location[3]..position[1]] == current_turn and board_data[location[2]..position[2]] == current_turn and board_data[location[1]..position[3]] == current_turn then
return true
end
end
end
local function check_tie_condition()
local is_not_empty = 0
for i = 1,3 do
if board_data[location[i]..position[1]] ~= " " and board_data[location[i]..position[2]] ~= " " and board_data[location[i]..position[3]] ~= " " then
is_not_empty = is_not_empty + 3
end
if is_not_empty == 9 then
return true
end
end
end
local function get_array_size(array)
local count = 0
for i in pairs(array) do
count = count + 1
end
return count
end
local function minimax(board,current_player) -- MINIMAX ai
local empty_slots = find_empty_slots(board) -- Finds the empty slots on the tic tac toe board
if _check_win_condition("X")then --Checks if someone has won,lost, or draw
return -1
elseif _check_win_condition("O") then
return 1
elseif empty_slots == 0 then
return 0
end
local all_test_plays = {} --Stores all the plays of the ai
for i = 1, get_array_size(empty_slots) do -- Loops
local current_test_play = {} --a current fake/test play of the game
current_test_play["Index"] = empty_slots[i] --Creates a key called Index and sets the empty slots key to it, the keys are strings, check board data to understand
board_data[empty_slots[i]] = current_player --Sets the empty slot to the current player which would be ai for the moment
if current_player == "O" then -- Checks whos playing and apply the minimax function again to produce a recursion
local result = minimax(board_data,"X") -- Get the result from going again in the board
current_test_play["Score"] = result -- Then saves the result to a new key called score
else
local result = minimax(board_data,"O")
current_test_play["Score"] = result
board_data[empty_slots[i]] = " " -- We set the slots index to a blank/empty when done
table.insert(all_test_plays,current_test_play) -- Insert the current_test_play to all test_plays to compare
end
local best_play = nil -- a place to store the best play for the ai
local best_score = nil -- stores the best score aswell
if current_player == "O" then -- O is the ai in this case
best_score = -1000 -- Not sure why I need to do this, I followed some tutorial written in a different language that used infinity which I assumed it just a big number
for i = 1, get_array_size(all_test_plays) do -- Loop through all the plays avaliable
if all_test_plays[i]["Score"] > best_score then -- compare to see if the score is better then best_score once again i dont have a clue
best_score = all_test_plays[i]["Score"] --Seting the score and play
best_play = all_test_plays[i]["Index"]
end
end
else
best_score = 1000 -- pretty much a copy and past from the top except its not negative
for i = 1, get_array_size(all_test_plays) do
print(all_test_plays[i]["Score"])
if all_test_plays[i]["Score"] < best_score then --THIS IS ERROR LINE 107
best_score = all_test_plays[i]["Score"]
best_play = all_test_plays[i]["Index"]
end
end
end
return best_play -- we return it so this would be the best play the ai would make I think.
end
end
local function _game() --The main loop of the game
local current_turn = "X" -- it holds two states X or O
print("Please select a position from the board, to access the top row, type top_position position being L, M , R e.g top_L \n")
_draw_board()
while true do
print("Player " ..current_turn.. " is selected!")
if current_turn == "O" then -- Checks if its Os turn/AI and make it play the minimax function
local play = minimax(board_data,"O")
print(play)
current_turn = "X"
else -- Else its the players turn
local Input = io.read()
if _check_if_position_exists_and_is_empty(Input) then
board_data[Input] = current_turn
_draw_board()
if _check_win_condition(current_turn) then
print("Player "..current_turn .. " Has won this game!")
break
elseif check_tie_condition() then
print("The game has ended at a tie!")
break
end
if current_turn == "X" then -- Switch to the other player
current_turn = "O"
elseif current_turn == "O" then
current_turn = "X"
end
else
print("\nThat wasnt a command in the table or the slot wasnt empty!, example top_L, mid_R, low_M \n")
end
end
end
end
_game()
Here is the error I received, I also tried including some of the print statements that I put trying to figure out why it didnt do things correctly, or maybe I forgot to do something. I have also commented on which line the error , error-ed out
Player O is selected!
-1
nil
lua: /mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:107: attempt to compare nil with number
stack traceback:
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:107: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:84: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:87: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:84: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:124: in local '_game'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:153: in main chunk
[C]: in ?
The terminal process "bash '-c', 'lua '/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua''" terminated with exit code: 1.
I won't do all the debugging for you, but at some point table all_test_plays is empty.
This causes minimax's return value best_play to become nil as the loop that could assign a value to it is not run due to the empty table.
This is then assigned to the Score field which later is compared vs a number.
Your logic seems to be broken. Go back through your code and compare it with the tutorial's code. Most importantly make sure you understand what this algorithm is doing. you should be able to do the same with pen and paper. Ensure that your "AI" is doing what you would do. At some point there will be a difference.

How to recieve number AND strings using only one io.read()?

I want to do a program that tells the user if what kind of information is he telling...
that's what I have for now, but as you can see, it is missing the IF function to identify if it is a number, but I don't know how to get a number not using io.read('*number)
a = io.read()
if a == string.lower(a) then
print('It's a lower string')
end
if a == string.upper(a) then
print('It's an upper string')
end
pls help
Keep reading with a = io.read() and try to convert to a number:
a = io.read()
if tonumber(a) then
print("It's a number")
elseif a == string.lower(a) then
print("It's a lowercase string")
elseif a == string.upper(a) then
print("It's an uppercase string")
end

Lua "or" statement issue

I'm very new to Lua, and I'm doing a very simple text based adventure thing, but it wont work. My code is as follows:
while input ~= ("leave cave" or "leave") do
print("What do you want to do?")
input = io.read()
if input == "inspect" then
print("You are in a cave")
elseif input == "leave cave" or "leave" then
print("You leave the cave")
elseif input == "inv" then
for i,v in pairs(inv) do
print(i, v)
end
else
print("You didn't write a valid command...")
end
end
-- leave cave
input = ""
print("What do you want to do?")
input = io.read()
while input ~= "follow path" do
if input == "inspect" then
print("You are at the base of a hill. There is a path.")
elseif input == "follow path" then
print("You follow the path. There is a gate.")
elseif input == "inv" then
for i,v in pairs(inv) do
print(v)
end
else
print("That's not a valid command...")
end
end
What I'm trying to do is have it so whenever the user types leave, or leave cave, it proceeds to the next segment (the path one), however, when I type "leave" and then type "inspect" again it says "I am in a cave" rather than what it should be saying which is saying that you left, and you see a path. And when I type leave cave, and then inspect, it spams "You are at the base of a hill. THERE IS A PATH" over and over, indefinitely.
And when I type "inv" it doesn't print my inventory, and instead prints "You left the cave," but doesn't actually leave.
a or b can't make a value that means "either a or b" -- that would be too complicated.
In fact, if you ask it to choose between two strings, it will just pick the first:
print("leave cave" or "leave") --> leave cave
or is only meant to be used on booleans -- you have to combine it on multiple full conditions:
while (input ~= "leave cave") and (input ~= "leave") do
In this case, a repeat ....... until <condition> loop might serve you better:
repeat
print("What do you want to do?")
input = io.read()
-- <do stuff>
until input == "leave" or input == "leave cave"
While or cannot accomplish such a complex operation, it is possible to recreate the effect yourself with some hacky metatable code.
Please note I do not reccomend using this code in any serious professional or commercial programs, or really at all for that matter, this code is inefficient and unecessary, however it is a fun piece of code to do exactly what you're looking for. It's just a fun way to experiment with the power of Lua.
local iseither
iseither = setmetatable({},{
__sub = function(arg1,arg2)
if arg2 == iseither then
arg2.Value = arg1
return arg2
else
if type(arg2) ~= "table" then
error("Second operator is -iseither- was not a table",2)
else
for i,v in ipairs(arg2) do
if arg1.Value == v then
arg1.Value = nil
return true
end
end
arg1.Value = nil
return false
end
end
end
})
print(1 -iseither- {1,2,3,4,5})

How can i skip a section of code lua?

It is my first lua project and i have a trouble with skipping part of my code. I want the code to stop after part "Cool". So if i write good and it answers cool i want the rest of the code to stop since after that the next question is not relative anymore.
How it works:
Code says: Hello
You say: anything
Code says: How are you?
You say: good
after you say good it will say cool.
If you say anything other than good it will ask "Why?"
e.g. you say: bad
Code says: It will be alright
I want it to stop after "cool" and skip out the further part of the code.
os.execute(" cls ")
print("Hello")
odp = io.read()
if odp == string then
end
tof = true or false
print("How are you?")
odp2 = io.read()
if odp2 == "good" then print("Cool") tof = true
else print("Why?") tof = false
if tof == true then os.execute(" pause ")
end
end
odp3 = io.read()
if odp3 ~= math then print("It will be alright")
print("Okay, I have to go see you.")
end
os.execute(" pause ")
When you compile code, it becomes the body of a function. The prototypical way of exiting a function is with a return statement. A function can have zero or more return statements.
But, since you want to exit the program, you can instead call os.exit().
You've just got to nest your "if" statements differently. All you need to do is put the rest of the code into the "else" part of your "if" statement, like this:
os.execute(" cls ")
print("Hello")
odp = io.read()
if odp == string then
end
tof = true or false
print("How are you?")
odp2 = io.read()
if odp2 == "good" then
print("Cool")
tof = true
else
print("Why?")
tof = false
if tof == true then
os.execute(" pause ")
end
-- You had an "end" here.
odp3 = io.read()
if odp3 ~= math then
print("It will be alright")
print("Okay, I have to go see you.")
end
os.execute(" pause ")
end -- You literally just need to move it here.
That way, it only gets input from the user after it asks for it, and only if the user doesn't answer, "good" to the "How are you?" question.
Note that I re-indented the code, but it's still the same code in the same order. I just made it more standard-looking and easier to visually see the structure of the program.

if statement not working in Lua for io.read

I'm trying to make a 'simple' Y/N answer choice thing. (That you saw all the time on old programs) But an If Statement I'm using doesn't seem to want to work. I even print out the variable and it is nowhere near what i want to compare yet it still passes it.
--Porgram Functions
function check()
--Local Variables
local num = 0
local loop = true
io.write("Continue? (Y/N):")
--User input
local input = io.read()
while(loop==true) do
if (input=="y" or "Y") then
print("Ok!")
loop = true
num = 1
elseif (input=="n" or "N") then
print("Fine...")
num = 2
else
print("Invalid Answser!")
loop = true
num = 0
end
end
print(input)
return(num)
end
print (check())
I would've written your function like this:
function check()
io.write("Continue? (Y/N): ")
answer = io.read()
while( not (answer == "Y" or answer == "N") ) do
io.write("Invalid Answer! Try again (Y/N): ")
answer = io.read()
end
if answer == "Y" then
print("Ok!")
return 1
else
print("Fine...")
return 2
end
end
print(check())
Some examples of its use:
Continue? (Y/N): Huh?
Invalid Answer! Try again (Y/N): N
Fine...
2
>Exit code: 0
>lua -e "io.stdout:setvbuf 'no'" "a.lua"
Continue? (Y/N): Huh?
Invalid Answer! Try again (Y/N): Y
Ok!
1
A working version of your code would be:
function check()
local num = 0
local loop = true
io.write("Continue? (Y/N):")
while(loop==true) do
--User input
local input = io.read()
if (input == "y" or input == "Y") then
print("Ok!")
num = 1
loop = false --we want to stop looping if input is valid
elseif (input == "n" or input == "N") then
print("Fine...")
num = 2
loop = false --we want to stop looping if input is valid
else
print("Invalid Answser!")
-- loop = true no need to set looping to true again
num = 0
end
end
return(num)
end
The changes made were:
Get the user input inside the while loop, this way if the input is invalid and the loop goes again the same logic behind getting the input is used, we don't have to code two cases for getting input; one outside the loop the other within. It also pauses execution when the loop starts again, this was what was producing all that output!
input == "y" or "Y" doesn't do what you think. Instead it evaluates to (input == "y") or ("Y"), what you want it input == "y" or input == "Y".
You needed to set loop to false when the input was either "y" or "Y" or "n" or "N", otherwise the loop would continue.
Fourthly setting the loop to true inside the loop is unnecessary, it begins as true, and the only change you can make is to set it to false. And since each of the conditions are mutually exclusive i.e input being "y" or "Y" mutually exclusive to input being "n" or "N" or it being neither "y" or "Y" or "n" or "N". You don't need to worry about it being set to false unless you wanted the loop to end.
local function check()
io.write"Continue? (Y/N): "
local ans, num = {y = 1, n = 2}
repeat
num = ans[io.read():lower()] or 3
io.write(({"Ok!\n","Fine...\n","Invalid Answer! Try again (Y/N): "})[num])
until num < 3
return num
end
print (check())

Resources