Trying to convert a C program into a lua script

Posted by Boxknife on Sun 05 Apr 2009 12:45 AM — 5 posts, 18,138 views.

#0
I wrote a program in C to generate all the possible combinations of a list of ingredients. I'm trying to convert it to Lua, but with little success.

Here's what it does:

Enter ingredients:> kalan kalan sack sack

craft kalan 
craft kalan kalan 
craft sack 
craft kalan sack 
craft kalan kalan sack 
craft sack sack 
craft kalan sack sack 
craft kalan kalan sack sack 


Here's the C source code:
http://pastesite.com/6195
And here's what I'm trying to do in Lua:
http://pastesite.com/6194

It keeps giving me this error when I try to run it:
[string "Alias: "]:40: attempt to concatenate field '?' (a nil value)

I have no idea what that means.

Amended on Sun 05 Apr 2009 05:24 AM by Boxknife
Australia Forum Administrator #1
Your Lua code was:


function ColourNote(a, b , c)

end
ColourNote("red", "black", "%1")
buffer = "craft"
words = {" "}

n=0

local w
wordcount=0

  for w in string.gmatch("%1", "%a+") do
    wordcount=wordcount+1
    words[wordcount]=w

  end
  
s=0

  for i=1,wordcount,1 do
    s=bit.bor(s,1)
    s=bit.shl(s,1)
    ColourNote("red", "black", words[i])
  end

s=s+1
copys=s

x=0
hasword=0
wordsums={"0"}
wordvals={"0"}

  for i=1,copys+1,1 do
    x=i
    j=1
      while x do
        if bit.band(x,1) then
          hasword=1
          
          buffer=buffer .. string.lower(words[j])
                                 
          buffer=buffer .. " "
          j=j+1
        end
      end
      if hasword then
                    
      ColourNote("red", "black", buffer)
      end
    hasword=0

  end



I can't help thinking there is an easier way. :)

After a bit of thought, I believe a recursive function is the way to go.

My idea was, to initially show all ingredients. Then, omit one, and show the remaining ones (so in your case, there are 4 lines with only 3 ingredients). Now for each of the 3 ingredients, you recurse, so that you now omit one of the 3, showing a line of 2 ingredients, and so on, till you are down to one.

To stop duplicates a simple table of "done" stops you repeating things (eg. the last single ingredient would keep appearing often).

My example code is:


ingredients = { "flour", "eggs", "sugar", "salt" }

require "copytable"  

done = {}

function permute_ingredients (t)

    local result = table.concat (t, ", ")
    
    if not  done [result] then
      print (result)
      done [result] = true
    end -- if not shown this one yet

  -- if more than 1 item, recurse
  if #t > 1 then
 
    for i = 1, #t do
      local t2 = copytable.shallow (t)
      table.remove (t2, i)  -- get rid of one of them
      permute_ingredients (t2)
    end -- for
  
  end -- if 

end -- function permute_ingredients

permute_ingredients (ingredients)


Running this gives:


flour, eggs, sugar, salt
eggs, sugar, salt
sugar, salt
salt
sugar
eggs, salt
eggs
eggs, sugar
flour, sugar, salt
flour, salt
flour
flour, sugar
flour, eggs, salt
flour, eggs
flour, eggs, sugar


This is 15 lines, which sounds about right for 4 ingredients.

If I add another ingredient (yeast) I get 31 lines, which also sounds right (2^5 - 1):


flour, eggs, sugar, salt, yeast
eggs, sugar, salt, yeast
sugar, salt, yeast
salt, yeast
yeast
salt
sugar, yeast
sugar
sugar, salt
eggs, salt, yeast
eggs, yeast
eggs
eggs, salt
eggs, sugar, yeast
eggs, sugar
eggs, sugar, salt
flour, sugar, salt, yeast
flour, salt, yeast
flour, yeast
flour
flour, salt
flour, sugar, yeast
flour, sugar
flour, sugar, salt
flour, eggs, salt, yeast
flour, eggs, yeast
flour, eggs
flour, eggs, salt
flour, eggs, sugar, yeast
flour, eggs, sugar
flour, eggs, sugar, salt

#2
Brilliant!
#3
For those of you interested, here is a crafting script that leverages Nick's recursive algorithm.

Usage: tcraft item1 item2 item3 ...
What it does: sends all possible "craft <combination>" combinations to the MUD.


<aliases>
  <alias
   match="tcraft (.*)"
   enabled="y"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "copytable"  

done = {}
--Recursive algorithm courtesy of Nick Gammon
function permute_ingredients (t)

    local result = table.concat (t, " ")
    
    if not  done [result] then
      print ("craft " .. result)
      SendImmediate("craft " .. result)
      done [result] = true
    end -- if not shown this one yet

  -- if more than 1 item, recurse
  if #t &gt; 1 then
 
    for i = 1, #t do
      local t2 = copytable.shallow (t)
      table.remove (t2, i)  -- get rid of one of them
      permute_ingredients (t2)
    end -- for
  
  end -- if 

end -- function permute_ingredients

--permute_ingredients (ingredients)
ingredients = { }
local w = ""
wordcount=1
 for w in string.gmatch("%1", "%a+") do
    ingredients[wordcount]=w
    wordcount=wordcount+1

  end

permute_ingredients (ingredients)

</send>
  </alias>
</aliases>
Amended on Sun 05 Apr 2009 06:52 PM by Boxknife
Australia Forum Administrator #4
That looks good!

By the way, you don't need to set w to an empty string outside the loop (it is a different w anyway). Also table.insert inserts things into a table, so that saves counting. Like this:



local ingredients = { }

for w in string.gmatch("%1", "%a+") do
  table.insert (ingredients, w)
end

permute_ingredients (ingredients)