[Solved] Merge identical nodes in craft recipe

Post Reply
User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

[Solved] Merge identical nodes in craft recipe

by texmex » Post

I'm trying to write some code where I grab all craft recipes of all items respectively with. Most things work already, but I can't figure out how to count the occurances of identical items within the same craft recipe and "merge" them, that is combining them into one occurance of the item suffixed by a number equal to the number of occurances in the "old" recipe.

Essentially I want to turn

Code: Select all

	recipe = {
		{"",           "",           ""          },
		{"group:wood", "",           "group:wood"},
		{"group:wood", "group:wood", "group:wood"},
},
↳ into ↴

Code: Select all

	recipe = {{"group:wood 5"}},

Here's what I've written so far:
Spoiler

Code: Select all

function values(t)
  local i = 0
  return function() i = i + 1; return t[i] end
end

local items = minetest.registered_items

for _,a in pairs(items) do
	local recipe = minetest.get_all_craft_recipes(a.name)
	if a.tiles ~= nil and recipe ~= nil then
		for b in values(recipe) do
			for c in values(b.items) do
				--CODE??
			end
			crafting.register_recipe({
				type = "inv",
				output = b.output,
				items = b.items,
				always_known = true,
			})
		end
	end
end
Last edited by texmex on Wed Oct 03, 2018 17:21, edited 3 times in total.

User avatar
Hybrid Dog
Member
Posts: 2834
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Merge identical nodes in craft recipe into one, with cou

by Hybrid Dog » Post

You could use a table as hash map to store the counts for each item.
An item in a craft recipe could be e.g. "default:stone 3", so the item name is not necessarily the itemstring, thus I used the ItemStack function to parse the itemstring reliably.
In the end a list is created which contains the items with their count as itemstring, i.e. the "new" recipe.

Code: Select all

local item_counts = {}
for y = 1, #recipe do
	local row = recipe[y]
	for x = 1, #row do
		local item = ItemStack(row[i])
		local itemname = item:get_name()
		if item_counts[itemname] then
			item_counts[itemname] = item_counts[itemname] + item:get_count()
		else
			item_counts[itemname] = item:get_count()
		end
	end
end

local new_recipe,n = {},1
for name, count in pairs(item_counts) do
	new_recipe[n] = name .. " " .. count
	n = n+1
end

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: Merge identical nodes in craft recipe into one, with cou

by texmex » Post

That looks very promising, HybridDog. Thank your for this! I’ll try it out soon.

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: Merge identical nodes in craft recipe into one, with cou

by texmex » Post

Hmm, I'm trying the code out but the for x = 1, #row do never gets to run with local recipe = minetest.get_all_craft_recipes(a.name):
Spoiler

Code: Select all

function values(t)
  local i = 0
  return function() i = i + 1; return t[i] end
end

local items = minetest.registered_items

for _,a in pairs(items) do
	local recipe = minetest.get_all_craft_recipes(a.name)
	if a.tiles ~= nil and recipe ~= nil then

		local item_counts = {}
		for y = 1, #recipe do
			--print("y runs now")
			local row = recipe[y]
			for x = 1, #row do
				print("x runs now")
				local item = ItemStack(row[i])
				local itemname = item:get_name()
				if item_counts[itemname] then
					item_counts[itemname] = item_counts[itemname] + item:get_count()
				else
					item_counts[itemname] = item:get_count()
				end
			end
		end

		local new_recipe,n = {},1
		for name, count in pairs(item_counts) do
			 new_recipe[n] = name .. " " .. count
			 n = n+1
		end

		for b in values(new_recipe) do
			crafting.register_recipe({
				type = "inv",
				output = b.output,
				items = b.items,
				always_known = true,
			})
		end
	end
end
Last edited by texmex on Wed Oct 03, 2018 13:45, edited 4 times in total.

User avatar
12Me21
Member
Posts: 873
Joined: Tue Mar 05, 2013 00:36
GitHub: 12Me21
Location: (Ignore all of my posts before 2018)

Re: Merge identical nodes in craft recipe into one, with cou

by 12Me21 » Post

minetest.registered_items isn't an array, it uses the item names as keys.

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: Merge identical nodes in craft recipe into one, with cou

by texmex » Post

12Me21 wrote:minetest.registered_items isn't an array, it uses the item names as keys.
Yet this works fine:
Spoiler

Code: Select all


function values(t)
  local i = 0
  return function() i = i + 1; return t[i] end
end

local items = minetest.registered_items
--table.sort(items)

for _,a in pairs(items) do
	local recipe = minetest.get_all_craft_recipes(a.name)
	if a.tiles ~= nil and recipe ~= nil then
		for b in values(recipe) do
			for c in values(b.items) do

			end
			crafting.register_recipe({
				type = "inv",
				output = b.output,
				items = b.items,
				always_known = true,
			})
		end
	end
end
(Sorry, I wrote "ipairs" where in reality I use a "pairs" function, and I didn't publish my "values" function.)

User avatar
12Me21
Member
Posts: 873
Joined: Tue Mar 05, 2013 00:36
GitHub: 12Me21
Location: (Ignore all of my posts before 2018)

Re: Merge identical nodes in craft recipe into one, with cou

by 12Me21 » Post

Make sure this is running after all mods have loaded.

EDIT:
the output of minetest.get_all_craft_recipes is: (from lua_api.txt)

Code: Select all

    * recipe entry table:
        * `method`: 'normal' or 'cooking' or 'fuel'
        * `width`: 0-3, 0 means shapeless recipe
        * `items`: indexed [1-9] table with recipe items
        * `output`: string with item name and quantity
The input items aren't split into rows.

Try this:

Code: Select all

local function combine(recipe)
	local item_counts = {}
	for _, itemstring in pairs(recipe.items) do -- must use pairs here because some slots are nil.
		local stack = ItemStack(itemstring)
		local item_name = stack:get_name()
		item_counts[item_name] = (item_counts[item_name] or 0) + stack:get_count()
	end
	return item_counts
end

minetest.register_on_mods_loaded(function()
	for name in pairs(minetest.registered_items) do
		local recipes = minetest.get_all_craft_recipes(name)
		if recipes then
			for _, recipe in ipairs(recipes) do
				print(dump(combine(recipe)))
			end
		end
	end
end)

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: Merge identical nodes in craft recipe into one, with cou

by texmex » Post

Wow, I didn't know register_on_mods_loaded even existed. That will be super useful instead of soft depend on every other little mod!

Your function looks super slick, but it returns

Code: Select all

{["group:wood"] = 5,}
and not

Code: Select all

{{"group:wood 5"}},

User avatar
12Me21
Member
Posts: 873
Joined: Tue Mar 05, 2013 00:36
GitHub: 12Me21
Location: (Ignore all of my posts before 2018)

Re: Merge identical nodes in craft recipe into one, with cou

by 12Me21 » Post

texmex wrote:Wow, I didn't know register_on_mods_loaded even existed. That will be super useful instead of soft depend on every other little mod!
I think that was added in 5.0, but before that it was common to use minetest.after(0, function).
texmex wrote: Your function looks super slick, but it returns

Code: Select all

{
	["default:stick"] = 2,
	["default:brick"] = 4
}
and not

Code: Select all

{{"group:wood 5"}},
Oh, right
just replace `return item_counts` with something like

Code: Select all

	local stacks = {}
	for name, count in pairs(item_counts) do
		table.insert(stacks, name.." "..count)
	end
	return stacks
I'm not sure whether items with metadata are allowed in recipes... That would break this.

User avatar
Hybrid Dog
Member
Posts: 2834
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: Merge identical nodes in craft recipe into one, with cou

by Hybrid Dog » Post

Sorry, I didn't read the api.
Converting the hashmap to a list should still work, as 12Me21 already showed.
I generally don't use table.insert.

Code: Select all

local new_recipe = {}
for name, count in pairs(item_counts) do
	new_recipe[#new_recipe+1] = name .. " " .. count
end

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: Merge identical nodes in craft recipe into one, with cou

by texmex » Post

Thank you both, you've been immensely helpful!

I ended up building on 12Me21's solution and with the (for me) newly discovered register_on_mods_loaded function:

Code: Select all

local function combine(recipe)
	local item_counts = {}
	for _, itemstring in pairs(recipe.items) do -- must use pairs here because some slots are nil.
		local stack = ItemStack(itemstring)
		local item_name = stack:get_name()
		item_counts[item_name] = (item_counts[item_name] or 0) + stack:get_count()
	end
	local stacks = {}
	for name, count in pairs(item_counts) do
		table.insert(stacks, name.." "..count)
	end
	return stacks
end

minetest.register_on_mods_loaded(function()
	for name in spairs(minetest.registered_items) do
		local recipes = minetest.get_all_craft_recipes(name)
		if recipes then
			for _, recipe in ipairs(recipes) do
				if recipe.method == "normal" then
					crafting.register_recipe({
						type = "inv",
						output = recipe.output,
						items = combine(recipe),
						always_known = true,
					})
				end
			end
		end
	end
end)
(spairs is pairs but sorted)

Now I can finally haz grid-less crafting!

Image
Attachments
crafting.png
crafting.png (59.2 KiB) Viewed 750 times
Last edited by texmex on Thu Oct 11, 2018 06:40, edited 1 time in total.

User avatar
Hybrid Dog
Member
Posts: 2834
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: [Solved] Merge identical nodes in craft recipe

by Hybrid Dog » Post

Did you consider special recipes yet?
* Shapeless recipes are already grid-less I think, so you may want to handle them differently. recipe.width is 0 in this case.
* There're also replacements, where you get more than one itemstack as crafting output, an item in the craft grid is replaced with another one: https://github.com/minetest/minetest/bl ... .txt#L6130
* Mods might register functions which are executed when the player crafts something: https://github.com/minetest/minetest/bl ... .txt#L3669, a mod may for example make the player be teleported somewhere when it crafts something.

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: [Solved] Merge identical nodes in craft recipe

by texmex » Post

Nope, I haven't yet. This is merely to carry over the majority of MTG recipes to the other system in order to build and iterate upon it but not with the goal of having complete feature parity.

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: [Solved] Merge identical nodes in craft recipe

by texmex » Post

Hybrid Dog wrote:* Shapeless recipes are already grid-less I think, so you may want to handle them differently. recipe.width is 0 in this case.
I think they work equally good as the otherones, not seeing why I should treat them differently.
Hybrid Dog wrote:* There're also replacements, where you get more than one itemstack as crafting output, an item in the craft grid is replaced with another one: https://github.com/minetest/minetest/bl ... .txt#L6130
Can you show me a replacement recipe? I haven't found a single one in MTG at least, that's not a cooking recipe. (this code is only supposed to handle normal ones)
Hybrid Dog wrote:* Mods might register functions which are executed when the player crafts something: https://github.com/minetest/minetest/bl ... .txt#L3669, a mod may for example make the player be teleported somewhere when it crafts something.
I think that works already, at least the awards mod is able to detect crafted recipes.

User avatar
Hybrid Dog
Member
Posts: 2834
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: [Solved] Merge identical nodes in craft recipe

by Hybrid Dog » Post


‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
texmex
Member
Posts: 1753
Joined: Mon Jul 11, 2016 21:08
GitHub: tacotexmex
In-game: tacotexmex

Re: [Solved] Merge identical nodes in craft recipe

by texmex » Post

Hybrid Dog wrote:Here's a replacement recipe: https://github.com/minetest-mods/moretr ... s.lua#L110
You're right, the recipe is craftable but the replacements aren't added. Not too interested in tending to since that requires changes to the crafting mod itself, of which I know nothing but the API.

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest