LuaTechnology.level is not consistent

Things that already exist in the current mod API
PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

LuaTechnology.level is not consistent

Post by PyroFire »

The problem:

It is difficult to determine (in lua) what level infinite researches are, particularly if they switch between regular (-1, -2 etc) to the infinite (-5 to -10).
Example: "worker-robots-speed-6".

When it's researched (you have level 6 worker robot speed), .level returns 7 and .researched returns false.

What this means is there's an extra +1 in the math somewhere, and to fix it you need to -1.
Example of how to do this:

Code: Select all

if(tonumber(tech.name:sub(-1,-1))==tech.level-1)then if(tech.researched)then is_full_researched=true research_level=tech.level else is_partially_researched=true research_level=tech.level-1 end end
This creates a lot of confusion from not having a clean "researched level" number that is equal to the actual research level.
A LuaTechnology.is_partially_researched would make this a lot easier and a lot less confusing (particularly when dealing with migrations that change technologies like this)
Adding also LuaTechnology.researched_level that automatically gives you the "Real" research level would also be appreciated.

An alternate to this would be to give LuaForce a .get_technology_level() that lets you use "worker-robots-speed" and it adds the -1, -2 etc internally for its checks (however it would do them).
Last edited by PyroFire on Thu Sep 19, 2019 10:02 pm, edited 1 time in total.

Bilka
Factorio Staff
Factorio Staff
Posts: 2914
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by Bilka »

What is your goal? What would .is_partially_researched do?
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by PyroFire »

I have researches tied to numbered prototype names, so you can "upgrade" the entity.
Half way through this research chain, it changes from 'regular' researches to a multi-level research.

The goal is to have a clean number that gives me the "real" research level for this purpose.
Anything else is incidental to this goal.

My level checking function currently looks like this:

Code: Select all

function research.level(n,f) f=f or game.forces.player local ft=f.technologies local r=ft[n.."-0"] or ft[n.."-1"] local i=0 while(r)do if(r.researched)then i=r.level r=ft[n.."-".. i+1] else r=nil end end return i end
.is_partially_researched gives me the check against the first level, so if i know if it has any levels on it at all instead of doing weird checks.

In the mean time, i have already changed my researches over to normal ones so i don't have to deal with this problem.

User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5097
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by Klonan »

I don't really see how this is useful

If you want to know the level, you can check technology.level

If you want to know if it is 'fully researched', just check that technology.level == technology.prototype.max_level


I think you are making it over-complicated for yourself

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by PyroFire »

Klonan wrote: ↑
Wed Sep 18, 2019 1:37 pm
I don't really see how this is useful
I may have the wrong solution to the problem, but that doesn't change the fact that this problem is a problem.
Klonan wrote: ↑
Wed Sep 18, 2019 1:37 pm
If you want to know the level, you can check technology.level
WRONG
LuaTechnology.level on a multi-level research returns the current level, plus one (+1).
So if i'm at level 6 of say, worker-robot-speed, game.forces.player.technologies["worker-robots-speed-6"].level will return 7.
Not 6.
At the same time, if the .level was 6, it would mean the research has no levels completed on it.
Meaning technology.level does not return the true level for multi-level research, instead it returns the current level plus one.
Klonan wrote: ↑
Wed Sep 18, 2019 1:37 pm
If you want to know if it is 'fully researched', just check that technology.level == technology.prototype.max_level
WRONG
Because of the above issue with the +1, technology.level==technology.prototype.maxlevel will actually return true when i am one level LESS than the max level, and not when that research is actually finished.
I haven't actually tested this case scenario (don't need to bother because of obvious conflicts - it either stops at the max level at both max level minus one, and the max level, or it will tell you the level is its max level plus one when it's actually at max level. In both cases, it doesn't make sense!!)
The only method of telling if it is fully researched is by checking .researched==true (which is only true for a multi-level research when the final level is finished)
Klonan wrote: ↑
Wed Sep 18, 2019 1:37 pm
I think you are making it over-complicated for yourself
Is it really me over-complicating it "for myself", or is the factorio mod interface the thing that over-complicates it for me?
Or is it the fact that there is no clear and simple method to read the current research level for a multi-level research is what over-complicates it for me?

Perhaps you're talking more about my decision to use a multi-level technology in the first place when that feature either isn't finished or working properly, or is missing a mod interface that should be there? (hence this thread)

And note, these issues apply only to multi-level research, and does not affect "normal" researches.
This issue becomes particularly frustrating if you have a research chain that changes from "normal" researches to this multi-level research type, where an arbitrary +1 is added to the level for seemingly no apparent reason.

I raised this issue in discord before, and was told it's like this so it is consistent with the technologies menu and how that displays multi-level researches.
That's fine.
It's just very counter-intuitive when trying to read the research level in lua, and this counter-intuitive design is what over-complicates this simple desired goal: Get the current level of a multi-level research without these headaches!!

I hope this makes sense, if not, please feel free to give it a try.

Research worker-robots-speed to the first level of the multi-level research, then try and get the worker-robots-speed level (in lua, don't just read the technology screen - i need a number!!).
You will then see the hot mess of senseless over-complication that i am talking about and asking for help with.
Furthmore, if this behaviour is intended, it is not noted anywhere on the factorio mod api. It would be nice if it was, because this was seriously confusing when i first encountered it. Still doesn't actually elegantly solve the problem

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5205
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by eradicator »

What's wrong with a naive if then or check?

Code: Select all

local level = tech.level - (tech.researched and 0 or 1)
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

Bilka
Factorio Staff
Factorio Staff
Posts: 2914
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by Bilka »

You still haven't said what the requested function should do. What should it return when? What data type is it even? That would help judge whether it is necessary or can be done with already existing api functions.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology should have .is_partially_researched

Post by PyroFire »

Bilka wrote: ↑
Thu Sep 19, 2019 7:33 pm
You still haven't said what the requested function should do. What should it return when? What data type is it even? That would help judge whether it is necessary or can be done with already existing api functions.
Well the function is irrelevant for the most part.
The problem is achieving the desired outcome cleanly.
My examples/suggestions so far:

LuaTechnology.is_partially_researched would be true/false, similar to .researched, return true when *at least* the first level has been researched. This is equal to tech.level>tech.prototype.level
LuaTechnology.researched_level (or LuaForce.get_technology_level(tech_name)) returns 0~inf, similar to .level, would return the "true level" of that particular research chain, inclusive of previous and future levels.
In the case of worker-robots-speed;

forces.player.technologies["worker-robots-speed-6"].researched_level == 1 (when worker-robots-speed-1 is researched)
forces.player.technologies["worker-robots-speed-1"].researched_level == 5 (when worker-robots-speed-5 is researched)
forces.player.technologies["worker-robots-speed-6"].researched_level == 12 (when worker-robots-speed-6 is researched up to level 12. for comparison on this specific case, ["worker-robots-speed-6"].level would "normally" return 13 at this stage)

You can see how LuaForce.get_technology_level(tech_name) may actually be more appropriate here, instead of having this .researched_level and it be the same value for every entry of LuaTechnology in force.technologies.
I also believe for force.get_technology_level the tech_name should be the base technology name and not a specific level. eg. giving it "worker-robots-speed", so without the "-1", "-5" etc.
This way the difference between "normal" and "multi-level" researches does not need to be distinguished, it just gives you the level. No fussing about.
False if it hasn't been researched, and nil (specifically nil) if the technology doesn't exist (or throw error? not sure how you'd otherwise handle this case. nil seems to be cleanest) because technologies can be marked with a level 0, so returning 0 when not researched is not the right answer here.
eradicator wrote: ↑
Thu Sep 19, 2019 7:27 pm
What's wrong with a naive if then or check?

Code: Select all

local level = tech.level - (tech.researched and 0 or 1)
This is a great attempt of the kind of thing i was looking for, and is effectively what such a function i am requesting would do, but you wildly miss the mark (and it's not obvious either). How do you get the level when the previous levels are not multi level researches, or it switches between normal and multi-level researches?
And you still require yet more handling if there were more levels after that multi-level research (adding different science bottles for example, or changing their unit counts (not the formula - the "*x" for each "count")).


Combine these problems and you get a hot mess looking like this:

Code: Select all

function research.level(n,f)
	f=f or game.forces.player
	local ft=f.technologies
	local r=ft[n.."-0"] or ft[n.."-1"]
	local i=0
	while(r)do
		if(r.level~=r.prototype.max_level)then
			local lvl=r.level
			if(lvl>r.prototype.level)then
				i=lvl-1
				if(r.researched)then
					r=ft[n.."-"..i+2]
				else
					r=nil
				end
			else
				r=nil
			end
		elseif(r.researched)then
			i=r.level
			r=ft[n.."-".. i+1]
		else
			r=nil
		end
	end
	return i
end

Like i said:
PyroFire wrote: ↑
Thu Sep 19, 2019 7:16 pm
This issue becomes particularly frustrating if you have a research chain that changes from "normal" researches to this multi-level research type, where an arbitrary +1 is added to the level for seemingly no apparent reason.

What i am suggesting:

Code: Select all

game.print(game.forces.player.get_technology_level("worker-robots-speed"))

And as for whether it can be done with the existing api or not.
Well.. You can see my code above (untested), but to put it another way;
Klonan wrote: ↑
Wed Sep 18, 2019 1:37 pm
If you want to know the level, you can check technology.level
If you want to know if it is 'fully researched', just check that technology.level == technology.prototype.max_level
You didn't appear to be aware that this needed to be done in the first place.
If all that comes of this interface request is making it clearer that this is how the technology.level works in the API documentation, that would go a very long way as well, just to save anyone else the headache of figuring this out:
"Returns level + 1 for multi-level researches. To get true current level, first check if the .level is greater than prototype.level, if it is then return .level minus one, unless .researched is true which you can just return .level. It works this way because these are the numbers used by the menu." .. Or something like that. With a bit more work this sentence can probably be written so it actually makes sense?

Bilka
Factorio Staff
Factorio Staff
Posts: 2914
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: LuaTechnology.level is not consistent

Post by Bilka »

LuaTechnology.is_partially_researched would be true/false, similar to .researched, return true when *at least* the first level has been researched. This is equal to tech.level>tech.prototype.level
So no need for is_partially_researched because as you say there is an equal existing way to get that. That's great, moving to already exists.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

Boodals
Fast Inserter
Fast Inserter
Posts: 129
Joined: Sun Feb 11, 2018 7:10 pm
Contact:

Re: LuaTechnology.level is not consistent

Post by Boodals »

Pro tip: more text is bad.

Rseding91
Factorio Staff
Factorio Staff
Posts: 12512
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: LuaTechnology.level is not consistent

Post by Rseding91 »

I've been following this thread as replies came in but I really don't understand any of it.

* When reading .researched it will give "true" or "false".
* When .researched is "true" then .level == "the level the technology has been researched up to"
* When .researched is "false" then .level == "the next level not researched"

In the case of "I want to know what level has been researched" it's as simple as "has *technology X* been researched?" if "yes" then ".level" is the level that has been researched otherwise ".level - 1" has been researched.
If you want to get ahold of me I'm almost always on Discord.

Bilka
Factorio Staff
Factorio Staff
Posts: 2914
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: LuaTechnology.level is not consistent

Post by Bilka »

PyroFire wrote: ↑
Thu Sep 19, 2019 9:34 pm
Combine these problems and you get a hot mess looking like this:

Code: Select all

function research.level(n,f)
	f=f or game.forces.player
	local ft=f.technologies
	local r=ft[n.."-0"] or ft[n.."-1"]
	local i=0
	while(r)do
		if(r.level~=r.prototype.max_level)then
			local lvl=r.level
			if(lvl>r.prototype.level)then
				i=lvl-1
				if(r.researched)then
					r=ft[n.."-"..i+2]
				else
					r=nil
				end
			else
				r=nil
			end
		elseif(r.researched)then
			i=r.level
			r=ft[n.."-".. i+1]
		else
			r=nil
		end
	end
	return i
end
Just for fun I decided to dissect the code you posted as needed. First thing that jumped out at me was the "r.level~=r.prototype.max_level" check. That's the same as "not r.researched", with one exception, it's true when researched level = max level -1. This causes a bug where tech at that level will cause the function ignore that tech completely (example at end of post).
So, I replaced it with "not r.researched" which fixes the bug. This makes it obvious that the "if(r.researched)then" in that block can never be true, so it can be removed. Now, both blocks inside "not researched" result in "r" being set to nil, so that can be moved out of the if for lvl > r.prototype.level. That check is unnecessary anyway, if the level == prototype.level, the tech has no progress at all, so the tech level is 1 higher than the researched level. Now, let's remove the unnecessary if from "elseif(r.researched)then" since that's the only remaining possibility when "not r.researched" and get rid of the last else block that will never be hit. Now we arrive at this:

Code: Select all

function research.level(n,f)
	f=f or game.forces.player
	local ft=f.technologies
	local r=ft[n.."-0"] or ft[n.."-1"]
	local i=0
	while(r)do
		if not r.researched then
			i=r.level-1
			r=nil
		else
			i=r.level
			r=ft[n.."-".. i+1]
		end
	end
	return i
end
So, I went and tried it out. It works and is much neater. It's also the same as the suggestions above by eradicator and Rseding.

I also discovered that your original code does not work correctly for level 2 researched of demo shooting speed (max level is 3):
Image

I hope this helps you a bit, at least to discover that your original code is bugged.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology.level is not consistent

Post by PyroFire »

Bilka wrote: ↑
Sat Sep 21, 2019 3:47 pm

Code: Select all

function research.level(n,f)
	f=f or game.forces.player
	local ft=f.technologies
	local r=ft[n.."-0"] or ft[n.."-1"]
	local i=0
	while(r)do
		if not r.researched then
			i=r.level-1
			r=nil
		else
			i=r.level
			r=ft[n.."-".. i+1]
		end
	end
	return i
end
So, I went and tried it out. It works and is much neater. It's also the same as the suggestions above by eradicator and Rseding.

I hope this helps you a bit, at least to discover that your original code is bugged.
Indeed this is what i was aiming to find from this, and a very elegant solution as well.
I'm glad you were able to make sense of what i was trying to do.
It looks very weird with setting the return value based on the next research instead of the current, but ain't no denying how clean that is.
I'm not surprised my code attempt was bugged, too many .levels all over the place.

Image

Ultros
Fast Inserter
Fast Inserter
Posts: 129
Joined: Thu Apr 04, 2019 4:25 pm
Contact:

Re: LuaTechnology.level is not consistent

Post by Ultros »

Bilka wrote: ↑
Sat Sep 21, 2019 3:47 pm
I hope this helps you a bit, at least to discover that your original code is bugged.
Total props to you for going through all that and fixing the bug, even despite the less than stellar initial attitude.

Bilka
Factorio Staff
Factorio Staff
Posts: 2914
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: LuaTechnology.level is not consistent

Post by Bilka »

Ultros wrote: ↑
Sun Sep 22, 2019 4:00 am
Bilka wrote: ↑
Sat Sep 21, 2019 3:47 pm
I hope this helps you a bit, at least to discover that your original code is bugged.
Total props to you for going through all that and fixing the bug, even despite the less than stellar initial attitude.
If neutrally asking for more information on a request is "less than stellar initial attitude" then I truly wonder what else you'd expect.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5205
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: LuaTechnology.level is not consistent

Post by eradicator »

Bilka wrote: ↑
Sun Sep 22, 2019 4:28 am
Ultros wrote: ↑
Sun Sep 22, 2019 4:00 am
Bilka wrote: ↑
Sat Sep 21, 2019 3:47 pm
I hope this helps you a bit, at least to discover that your original code is bugged.
Total props to you for going through all that and fixing the bug, even despite the less than stellar initial attitude.
If neutrally asking for more information on a request is "less than stellar initial attitude" then I truly wonder what else you'd expect.
To me that sounds like he's talking about the attitude of @PyroFire, not yours @Bilka. Also it took me quite some staring at the code and a night of sleep to even understand what bug you were even talking about. So yea, props for that (/me googles the meaning of "props"). I'd probably have replaced it by "not researched" without even noticing the bug. Also i wrote a lengthy answer yesterday but internet went down, so couldn't post it. So here it comes:
___________

@OP: Are research name enforced like that? Can two completely disconnected and unrelated techs not be called "furnace-o-matron-2000" and "furnace-o-matron-3000" without there being any levels inbetween? Does the first infinite-tech of a chain *have* to be named "-0" at the end? Actually if there are both "-0" and "-1" variants of a technology, then what is the expected "level" if:

a) neither is researched?
b) only -0 is researched?
c) both are researched?

And how is this different from the case where only "-1" exists? I guess my understanding of technology postfixes is too limited (aka nonexistant). Quick test says that "physical-projectile-speed-1"'s level is always "1" regardless of if it's researched or not. And the base game does not have any "-0" technologies.

Also... what do you even need this for. Why do you need to find chains of technologies like this? (Half curiosity, half "maybe there's an even better solution" question.)
___________

I also did some attempts for the "make it shorter" competition. Based on the behavior of @Bilka's version which i understand as: "In a thread of infinite or non-infinite technology find the last technology or the first that is not researched". Here's all the itarations:

1) The while loop has an implicit return condition via "r=nil", we can externalize that and remove the if-then-else clause. Additionally i one-lined the technology fetching and added compatibility for tech-threads that start with an un-postfixed technology (i.e. "logistics" -> "logistics-2").

Code: Select all

function research_level(n,f)
  local ft=(f or game.forces.player).technologies
  local r=ft[n] or ft[n.."-0"] or ft[n.."-1"]
  local i=0 --[[only used when the whole tech "thread" is completely researched, i.e not infinite]]
  while r and r.researched do
    i=r.level
    r=ft[n.."-"..i+1] --[[nil if no next tech exists]]
    end
  return r and (r.level-1) or i --[[if r still exists it must be unresearched!]]
end

2) Tried to make it more readable by using a function for the string operations. Also now keep the technology reference "t" until the end, making the extra counter "i" obsolete. This also means "t" is never nil, removing the existance check from the while loop.

Code: Select all

function research_level(name,force)
  local function tech(level)
    return (force or game.forces.player).technologies[level and name..'-'..level or name]
    end
  local t = tech() or tech(0) or tech(1); assert(t,'Technology not found.')
  while t.researched do --[[if reserched try find next]]
    local x = tech(t.level+1)
    if x then t = x else break end
    end  
  return t.level + (t.researched and 0 or -1)
  end

3) Compressed if-then into the while loop condition to get rid of temporary variable "x", thus gaining 3 lines at the cost of calling tech() a second time for each traversed technology. Storing technologies in "x" to make tech() slightly faster, which also happens to save yet another line ;).

Code: Select all

function research_level(name,force)
  local x=(force or game.forces.player).technologies
  local function tech(level) return x[level and name..'-'..level or name] end
  local t = tech() or tech(0) or tech(1); assert(t,'Technology not found.')
  while t.researched and tech(t.level+1) do t = tech(t.level+1) end
  return t.level + (t.researched and 0 or -1)
  end

4) For comparision replaced the function calls by table lookups like in the original. Saves one line and might be slightly faster, but is imho uglier to read.

Code: Select all

function research_level(name,force)
  local x = (force or game.forces.player).technologies
  local t = x[name] or x[name..'-0'] or x[name..'-1']; assert(t,'Technology not found.')
  while t.researched and x[name..'-'..t.level+1] do t = x[name..'-'..t.level+1] end
  return t.level + (t.researched and 0 or -1)
  end

If your use-case is highly performance critical you should probably benchmark if you can afford the second tech() call or not. As always there's a fine line between short code and fast code.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

Ultros
Fast Inserter
Fast Inserter
Posts: 129
Joined: Thu Apr 04, 2019 4:25 pm
Contact:

Re: LuaTechnology.level is not consistent

Post by Ultros »

Bilka wrote: ↑
Sun Sep 22, 2019 4:28 am
If neutrally asking for more information on a request is "less than stellar initial attitude" then I truly wonder what else you'd expect.

I'm really sorry for the confusion, eradicator was right, and I was talking about the WRONG and other similarly obnoxious behaviour towards others trying to help.

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology.level is not consistent

Post by PyroFire »

Ultros wrote: ↑
Sun Sep 22, 2019 11:09 am
I'm really sorry for the confusion, eradicator was right, and I was talking about the WRONG and other similarly obnoxious behaviour towards others trying to help.
I apologize for any harshness that may have come across in my writing.
The use of punctuation like this is primarily for emphasis.

It's a surprisingly niche problem and very easy to misunderstand or miss in practice, so i felt it was very important to add as much stress to key points as possible.

eradicator wrote: ↑
Sun Sep 22, 2019 7:12 am
I also did some attempts for the "make it shorter" competition. Based on the behavior of @Bilka's version which i understand as: "In a thread of infinite or non-infinite technology find the last technology or the first that is not researched". Here's all the itarations:
Always love these.
I'm sure this can be done even cleaner, i really like your first attempt.
eradicator wrote: ↑
Sun Sep 22, 2019 7:12 am
1) The while loop has an implicit return condition via "r=nil", we can externalize that and remove the if-then-else clause. Additionally i one-lined the technology fetching and added compatibility for tech-threads that start with an un-postfixed technology (i.e. "logistics" -> "logistics-2").

Code: Select all

function research_level(n,f)
  local ft=(f or game.forces.player).technologies
  local r=ft[n] or ft[n.."-0"] or ft[n.."-1"]
  local i=0 --[[only used when the whole tech "thread" is completely researched, i.e not infinite]]
  while r and r.researched do
    i=r.level
    r=ft[n.."-"..i+1] --[[nil if no next tech exists]]
    end
  return r and (r.level-1) or i --[[if r still exists it must be unresearched!]]
end
Second sample though..
eradicator wrote: ↑
Sun Sep 22, 2019 7:12 am
2) Tried to make it more readable by using a function for the string operations. Also now keep the technology reference "t" until the end, making the extra counter "i" obsolete. This also means "t" is never nil, removing the existance check from the while loop.

Code: Select all

function research_level(name,force)
  local function tech(level)
    return (force or game.forces.player).technologies[level and name..'-'..level or name]
    end
  local t = tech() or tech(0) or tech(1); assert(t,'Technology not found.')
  while t.researched do --[[if reserched try find next]]
    local x = tech(t.level+1)
    if x then t = x else break end
    end  
  return t.level + (t.researched and 0 or -1)
  end
Sadly, there is no "break" in factorio lua.
You need to write those with functions and tailcalls.
Also i don't know about creating new instances of a function each time this is called, seems wasteful.
Let me see if i can improve it.

Code: Select all

function research_iterate_level(ft,n,i) local r=ft[n.."-"..i+1]
	if(not r)then return i elseif(not r.researched)then return r.level-1 else return research_iterate_level(ft,n,r.level) end
end
function research_level(n,f)
	local ft=(f or game.forces.player).technologies
	local r=ft[n] or ft[n.."-0"] or ft[n.."-1"]
	return research_iterate_level(ft,n,r.level-1)
end
research_iterate_level is self recursive function to "walk" the researches, and tailcalls with the final level.
research_level sets up the iterator. It doesn't do validation because the r.level-1 sets up the iterator to pick up the "first level".
Catch is this means checking the first research twice. The only flaw. So close yet so far.

eradicator wrote: ↑
Sun Sep 22, 2019 7:12 am
3) Compressed if-then into the while loop condition to get rid of temporary variable "x", thus gaining 3 lines at the cost of calling tech() a second time for each traversed technology. Storing technologies in "x" to make tech() slightly faster, which also happens to save yet another line ;).

Code: Select all

function research_level(name,force)
  local x=(force or game.forces.player).technologies
  local function tech(level) return x[level and name..'-'..level or name] end
  local t = tech() or tech(0) or tech(1); assert(t,'Technology not found.')
  while t.researched and tech(t.level+1) do t = tech(t.level+1) end
  return t.level + (t.researched and 0 or -1)
  end
Creating new functions inside a function that can be called several times in a single tick, (maybe even on_tick), is probably not a good idea.
Also you're doing this comparison twice.
Your first sample is better i think.
eradicator wrote: ↑
Sun Sep 22, 2019 7:12 am
4) For comparision replaced the function calls by table lookups like in the original. Saves one line and might be slightly faster, but is imho uglier to read.

Code: Select all

function research_level(name,force)
  local x = (force or game.forces.player).technologies
  local t = x[name] or x[name..'-0'] or x[name..'-1']; assert(t,'Technology not found.')
  while t.researched and x[name..'-'..t.level+1] do t = x[name..'-'..t.level+1] end
  return t.level + (t.researched and 0 or -1)
  end
This is excellent, however i feel there may be a way to avoid checking x[name..etc..] twice.
One step short of perfection :O!

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5205
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: LuaTechnology.level is not consistent

Post by eradicator »

PyroFire wrote: ↑
Sat Sep 28, 2019 11:20 pm
Sadly, there is no "break" in factorio lua.
You need to write those with functions and tailcalls.
Luckily that's bullshit. All 4 iterations posted above are tested and working.

Code: Select all

/c while true do game.print('Bullshit') break end
PyroFire wrote: ↑
Sat Sep 28, 2019 11:20 pm
Also i don't know about creating new instances of a function each time this is called, seems wasteful.
Let me see if i can improve it.
If the function was outside the closure it wouldn't see the required upvalues. That was an optimization for code readability and briefness. If you have different optimization goals you need to do different things. And if you need help with it you also need to actually tell what those goals are.
PyroFire wrote: ↑
Sat Sep 28, 2019 11:20 pm
Creating new functions inside a function that can be called several times in a single tick, (maybe even on_tick), is probably not a good idea.
Technology research state does not change "several times per tick". Whatever you're doing there is the wrong approach to the problem and you need to fix the approach, not the "solution".
PyroFire wrote: ↑
Sat Sep 28, 2019 11:20 pm
This is excellent, however i feel there may be a way to avoid checking x[name..etc..] twice.
One step short of perfection :O!
Sure. You can store it in yet another temporary variable. Then you're back at version "1)" though ;).
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

PyroFire
Filter Inserter
Filter Inserter
Posts: 356
Joined: Tue Mar 08, 2016 8:18 am
Contact:

Re: LuaTechnology.level is not consistent

Post by PyroFire »

eradicator wrote: ↑
Sun Sep 29, 2019 9:55 am
PyroFire wrote: ↑
Sat Sep 28, 2019 11:20 pm
Sadly, there is no "break" in factorio lua.
You need to write those with functions and tailcalls.
Luckily that's bullshit. All 4 iterations posted above are tested and working.

Code: Select all

/c while true do game.print('Bullshit') break end
Hurray! I could have sworn last time I used it, they didn't work properly.
eradicator wrote: ↑
Sun Sep 29, 2019 9:55 am
If the function was outside the closure it wouldn't see the required upvalues. That was an optimization for code readability and briefness. If you have different optimization goals you need to do different things. And if you need help with it you also need to actually tell what those goals are.
There is only ever one optimization goal.
Less CPU cycles.
It doesn't matter what it looks like to get the cycles cost down, particularly in this case.
eradicator wrote: ↑
Sun Sep 29, 2019 9:55 am
Technology research state does not change "several times per tick". Whatever you're doing there is the wrong approach to the problem and you need to fix the approach, not the "solution".
No, but you may need to call research_level for several different researches under several different functions in the same operation depending on what you're doing - Typically if you need to act based on the research level.

Post Reply

Return to β€œAlready exists”