[Learning LUA] Lesson 3: Values and Operators is up!
Re: [Learning LUA] Lesson 3: Values and Operators is up!
Then I definetely need to study the framework more!
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com
The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563
My preciousss: http://www.moonsharp.org
The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563
My preciousss: http://www.moonsharp.org
Re: [Learning LUA] Lesson 3: Values and Operators is up!
Such a great idea Spider!
I had actually just set about looking for some basics texts to learn more... but... this thread!
I had actually just set about looking for some basics texts to learn more... but... this thread!
Labyrinth of Lies (viewtopic.php?f=14&t=4400)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
Legacy of Lies (viewtopic.php?f=22&t=12983&hilit=+legacy)
Re: [Learning LUA] Lesson 3: Values and Operators is up!
Hi all,
A scripting help session with drakkan today on the LoTNR chat gave me an idea to contribute to this thread with a general programming principle, which is called
DRY - Don't Repeat Yourself
This says that you should note copy paste identical code many times around because:
1) You can make copy/paste/modify errors
2) If you want to change your code or fix an error in it, you have to change it everywhere, which is
a) long and tedious
b) very error-prone
Basically, you need to follow the rule of three principle. You can copy something twice. Three times? Make a function for it and call the function instead.
Ok, let's go with a practical example. Drakkan wanted a script that checks three alcoves for a red gem, and opens a door if a gem is in all three of them.
STEP 1
First Draft
Here is a first way of doing this, the simplest one. The function checkThree alcoves is called from the alcoves onActivate trigger, when you insert an item:
STEP 2
Making it DRY
As you see, in the code above, we are repeating the for loop iterator code three times. Rule of three says we should make 1 function instead.
In the example above (step 1), the only thing that changes between the three times is the alcove. So let's make a new function which returns true if a "red_gem" is found in a given alcove: as the alcove is the only changing parameter, we will make this the argument of the function:
Now, we can rewrite our checkThreeAlcoves function like this:
STEP 3
Cleaning temporary variables
The script above is fine, but now that our code is more DRY, we realize we don't need the temporary boolean variables. As our checkOneAlcove function returns true or false, we can use it directly in an if statement. Let's clean up the code:
Much shorter and cleaner, right?
STEP 4
Making code reusable
Again, the script above is fine, but what if we want to check for three green gems elsewhere in the dungeon? Or check for 5 rocks in another place? We would need to copy/paste/modify our checkAlcoves functions, again making our code non-DRY. (or WET, which goes for Write Everything Twice).
The solution? Making functions more generic so that the same function can be reused multiple times.
First, let's add a second item argument to our checkOneAlcove function, so that is can check for different items. Let's also call it more appropriately:
and now our main function becomes:
STEP 5
Making code even more reusable
Ok, now the alcoveContainsItem function is reusable, but not the main function, as the funny name above shows. The following version allows us to give it three different alcoves, and an item name:
STEP 6
Making code even EVEN more reusable
Ok, now the function above can check for any item in any three alcoves. But what if we want two alcoves or four, or 10? We cannot start doing a lot of functions like that:
That'll be definetly not DRY.
So how to handle "any number of alcoves"? The solution is to give the function a table of alcoves as an argument, as tables can have any size:
Here, I start by having a variable that is true if all alcoves have the item, and then check alcoves in the table one by one, and if one fails to contain the item, set the variable to false.
STEP 7
Making it even EVEN EVEN EVEN more reusable, or What the hell is that door doing in there anyway?
One last thing to do, is to get out the door opening code from inside the alcove checking function, as we might eventually want to close a pit, or spawn a monster, or whatever. So, final version of code:
Enjoy!!!!
A scripting help session with drakkan today on the LoTNR chat gave me an idea to contribute to this thread with a general programming principle, which is called
DRY - Don't Repeat Yourself
This says that you should note copy paste identical code many times around because:
1) You can make copy/paste/modify errors
2) If you want to change your code or fix an error in it, you have to change it everywhere, which is
a) long and tedious
b) very error-prone
Basically, you need to follow the rule of three principle. You can copy something twice. Three times? Make a function for it and call the function instead.
Ok, let's go with a practical example. Drakkan wanted a script that checks three alcoves for a red gem, and opens a door if a gem is in all three of them.
STEP 1
First Draft
Here is a first way of doing this, the simplest one. The function checkThree alcoves is called from the alcoves onActivate trigger, when you insert an item:
Code: Select all
function checkThreeAlcoves()
-- Make one boolean variable for each alcove
local gemInLeftAlcove = false
local gemInCenterAlcove = false
local gemInRightAlcove = false
-- Check left alcove, and set its variable to true if gem is found
for i in leftAlcove:containedItems() do
if i.name == "gem_red" then
gemInLeftAlcove = true
end
end
-- Check center alcove, and set its variable to true if gem is found
for i in centerAlcove:containedItems() do
if i.name == "gem_red" then
gemInCenterAlcove = true
end
end
-- Check left alcove, and set its variable to true if gem is found
for i in rightAlcove:containedItems() do
if i.name == "gem_red" then
gemInRightAlcove = true
end
end
-- Test if all variables are true and open door
if gemInLeftAlcove and gemInCenterAlcove and gemInRightAlcove then
door:open()
end
end
Making it DRY
As you see, in the code above, we are repeating the for loop iterator code three times. Rule of three says we should make 1 function instead.
In the example above (step 1), the only thing that changes between the three times is the alcove. So let's make a new function which returns true if a "red_gem" is found in a given alcove: as the alcove is the only changing parameter, we will make this the argument of the function:
Code: Select all
function checkOneAlcove(alcove)
-- Check if gem is in alcove and return true
-- Note: When the script finds one it returns true and exits the
-- function right away, not checking further items
for i in alcove:containedItems() do
if i.name == "gem_red" then
return true
end
end
-- If the script managed to get to here, this means a gem was not found. So let's return false:
return false
end
Code: Select all
function checkThreeAlcoves()
local gemInLeftAlcove = false
local gemInCenterAlcove = false
local gemInRightAlcove = false
if checkOneAlcove(leftAlcove) then
gemInLeftAlcove = true
end
if checkOneAlcove(centerAlcove) then
gemInCenterAlcove = true
end
if checkOneAlcove(rightAlcove) then
gemInRightAlcove = true
end
if gemInLeftAlcove and gemInCenterAlcove and gemInRightAlcove then
door:open()
end
end
Cleaning temporary variables
The script above is fine, but now that our code is more DRY, we realize we don't need the temporary boolean variables. As our checkOneAlcove function returns true or false, we can use it directly in an if statement. Let's clean up the code:
Code: Select all
function checkThreeAlcoves()
if checkOneAlcove(leftAlcove)
and checkOneAlcove(centerAlcove)
and checkOneAlcove(rightAlcove) then
door:open()
end
end
STEP 4
Making code reusable
Again, the script above is fine, but what if we want to check for three green gems elsewhere in the dungeon? Or check for 5 rocks in another place? We would need to copy/paste/modify our checkAlcoves functions, again making our code non-DRY. (or WET, which goes for Write Everything Twice).
The solution? Making functions more generic so that the same function can be reused multiple times.
First, let's add a second item argument to our checkOneAlcove function, so that is can check for different items. Let's also call it more appropriately:
Code: Select all
-- DRY and Reusable version
function alcoveContainsItem(alcove, item)
for i in alcove:containedItems() do
if i.name == item then
return true
end
end
return false
end
Code: Select all
function checkLeftCenterAndRightAlcovesForRedGems()
if alcoveContainsItem(leftAlcove, "gem_red")
and alcoveContainsItem(centerAlcove, "gem_red")
and alcoveContainsItem(rightAlcove, "gem_red") then
door:open()
end
end
Making code even more reusable
Ok, now the alcoveContainsItem function is reusable, but not the main function, as the funny name above shows. The following version allows us to give it three different alcoves, and an item name:
Code: Select all
function checkThreeAlcovesForAnItem(alcove1, alcove2, alcove3, item)
if alcoveContainsItem(alcove1, item)
and alcoveContainsItem(alcove2, item)
and alcoveContainsItem(alcove3, item) then
door:open()
end
end
Making code even EVEN more reusable
Ok, now the function above can check for any item in any three alcoves. But what if we want two alcoves or four, or 10? We cannot start doing a lot of functions like that:
Code: Select all
function checkTwoAlcovesForAnItem(alcove1, alcove2, item)
function checkThreeAlcovesForAnItem(alcove1, alcove2, alcove3, item)
function checkFourAlcovesForAnItem(alcove1, alcove2, alcove3, alcove4, item)
function checkFiveAlcovesForAnItem(alcove1, alcove2, alcove3, alcove4, alcove5, item)
So how to handle "any number of alcoves"? The solution is to give the function a table of alcoves as an argument, as tables can have any size:
Code: Select all
function checkAlcovesForAnItem(alcovesTable, item)
local allAlcovesContainTheItem = true
for _, alcove in ipairs(alcovesTable) do
if not(alcoveContainsItem(alcove, item))
allAlcovesContainTheItem = false
end
end
if allAlcovesContainTheItem then
door:open()
end
end
STEP 7
Making it even EVEN EVEN EVEN more reusable, or What the hell is that door doing in there anyway?
One last thing to do, is to get out the door opening code from inside the alcove checking function, as we might eventually want to close a pit, or spawn a monster, or whatever. So, final version of code:
Code: Select all
-- Specific function to call for that puzzle
function openDoorIfAlcovesHaveRedGems()
if checkAlcovesForAnItem({leftAlcove, centerAlcove, rightAlcove}, "gem_red") then
door:open()
end
end
-- Generic function used by the above one
function checkAlcovesForAnItem(alcovesTable, item)
local allAlcovesContainTheItem = true
for _, alcove in ipairs(alcovesTable) do
if not(alcoveContainsItem(alcove, item))
allAlcovesContainTheItem = false
end
end
return allAlcovesContainTheItem
end
-- Generic sub-function used by the above one
function alcoveContainsItem(alcove, item)
for i in alcove:containedItems() do
if i.name == item then
return true
end
end
return false
end
Enjoy!!!!
Re: [Learning LUA] Lesson 3: Values and Operators is up!
I'm sorry sir, I would like to check whether in the left alcove is a red gem, in the middle there is a green one, and in the rightmost a blue one. How can I do that?Diarmuid wrote:
Enjoy!!!!
*I see a forthcoming Step 8*
alois
- Dr.Disaster
- Posts: 2876
- Joined: Wed Aug 15, 2012 11:48 am
Re: [Learning LUA] Lesson 3: Values and Operators is up!
Either go with alcoveContainsItem() of Step 4 and simply change the items looked for in the "if" statement or develop Step 8 by moving from a single item to an item table. Naturally the "for" loop then needs to have two index variables instead of one.alois wrote:I'm sorry sir, I would like to check whether in the left alcove is a red gem, in the middle there is a green one, and in the rightmost a blue one. How can I do that?
*I see a forthcoming Step 8*
I prefer Step 4 for readability and less time spend for writing code that prolly never gets used again. Why? Beginning with Step 5 code specialization starts so it's reusability is not guaranteed. In example when an alcove needs to be checked for more then one item Step 5 and up are not up for the task without additional code.
Re: [Learning LUA] Lesson 3: Values and Operators is up!
This is true, thanks for pointing out the code specialization issue. I would have stopped at step 4 probably too in a normal situation, but here I knew that drakkan had a whole puzzle of alcoves like that of different colors (36 alcoves he said), so I kept on doing a function for that.Dr.Disaster wrote:I prefer Step 4 for readability and less time spend for writing code that prolly never gets used again. Why? Beginning with Step 5 code specialization starts so it's reusability is not guaranteed. In example when an alcove needs to be checked for more then one item Step 5 and up are not up for the task without additional code.
- SpiderFighter
- Posts: 789
- Joined: Thu Apr 12, 2012 4:15 pm
Re: [Learning LUA] Lesson 3: Values and Operators is up!
It's great to see this thread taking off as intended! Thanks to everyone for your contributions. I'm going to hold off on any more newbie tutorials for the next two weeks, becuase I have too many balls in the air, and because I'm heading for outpatient surgery on Friday. I want to take this week to get as much of my mod done as I can, since it's so close to being completed (finally), and finishing it will allow me to really dig in deep with Lua. I'll still be monitoring the thread, and I'll still be around the forums (although probably not next weekend, as I imagine I'll simply be enjoying the drugs )
-
- Posts: 366
- Joined: Tue Jan 15, 2013 3:26 am
- Location: Oregon
Re: [Learning LUA] Lesson 3: Values and Operators is up!
That is a very nice lesson diarmuid. If I could request one thing though, can you write a a nice simple lesson on passing variables between functions?
You lesson is valuable but if someone doesn't know how top pass things back and forth (hmmm, i.e. me) then it becomes really confusing quickly.
Thanks for your input either way though.
You lesson is valuable but if someone doesn't know how top pass things back and forth (hmmm, i.e. me) then it becomes really confusing quickly.
Thanks for your input either way though.
Re: [Learning LUA] Lesson 3: Values and Operators is up!
Holy crap this is a lot of information to process. And I'm behind! Thankfully I'm understanding most of it so far.
Re: [Learning LUA] Lesson 3: Values and Operators is up!
When's the next lesson, Spider?
"I'm okay with being referred to as a goddess."
Community Model Request Thread
See what I'm working on right now: Neikun's Workshop
Lead Coordinator for Legends of the Northern Realms Project
Community Model Request Thread
See what I'm working on right now: Neikun's Workshop
Lead Coordinator for Legends of the Northern Realms Project
- Message me to join in!