Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Lil Programming Questions Sticky

A topic by Internet Janitor created Oct 28, 2022 Views: 10,463 Replies: 279
Viewing posts 81 to 85 of 85Previous pageFirst page
(+2)

Having a lot of fun with Decker and Lil.
Thank you.

Is this expected behaviour for joining arrays?

a1:("aa","bb","cc","dd")
a2:("x")
show[ a1 join a2 ]
--> (("aa","x"),("bb","x"),("cc","x"),("dd","x"))
show[ a2 join a1 ]
--> (("x","aa"))

Lil Playground

Developer(+1)

From the lil reference manual:

If join is applied to non-table arguments, it produces a list by pairing adjacent elements from x and y. If either argument is a number, it is considered "range" of that argument, and otherwise it is interpreted as a list. For example, "ABC" join 3 gives (("A",0),("B",1),("C",2)).

As you can see, it has a preference for preserving the length of the left argument if they don't match. Some functional languages call this operation on lists "zip()". Applying "join" to tables performs a natural join.

If you simply want to concatenate lists and atoms, use the comma operator (,):

 a1,a2
# ("aa","bb","cc","dd","x")

(Lil doesn't have list literals per se, just atom literals and the comma operator.)

Does that make sense?

(+1)

Is there a way to get  

(("x","aa"),("x","bb"),("x","cc"),("x","dd"))

from a1 and a2 without using each?

Developer(+1)
 ((count a1) take a2) join a1
 
# (("x","aa"),("x","bb"),("x","cc"),("x","dd"))
(+1)

nice, thanks  馃檪

(+1)

Hi guys! I was wondering if there were any video tutorials for Decker and Lil on YouTube or anywhere else. Anything is helpful, thanks!

(1 edit)

The only video I鈥檝e found is this one.

Generating Music in Decker

It鈥檚 not bad and kind of takes you on a journey of someone figuring out Decker, while you watch it to figure out Decker. It鈥檚 very meta. ;-)

Not a video, but here is a handy list of screenshots of these Decker examples.

(1 edit) (+1)

I have this Lil code to print a table of 10 English words that remain words when the 'r's are removed from the word:

words:wods:still:0
each w in "" drop "\n" split read["words"] # /usr/share/dict/words"]
  words[w]:1
  if "r" in w
    wods["" fuse "r" drop w]:w
  end
end
each v k in wods
  if k in words
    still[v]:k
  end
end
show[select word:value wod:wods@value from random[still -10]]

which produces output like

+--------------+---------------+
| word         | wod           |
+--------------+---------------+
| "evaluated"  | "revaluated"  |
| "emigated"   | "emigrated"   |
| "boated"     | "borated"     |
| "estated"    | "restated"    |
| "expatiated" | "expatriated" |
| "elatedness" | "relatedness" |
| "pedated"    | "predated"    |
| "ungated"    | "ungrated"    |
| "pated"      | "prated"      |
| "gated"      | "grated"      |
+--------------+---------------+

if reading a 'words' file that contains only the 3k words that include "ated", this runs in 11s (lila.awk) or 16s (lilt). So, actually reading /usr/share/dict/words with its half a million words, is pretty infeasible.

Is there a much better way to do this in Lil, or is this sort of batch processing out of scope for Lil?

Oh, I had a second question about selecting from an array using an array of booleans, but I found that 'select' can do that. Which gives this code that runs in 22ms in lilt, instead of 16s:

words:extract value where 5<count@value from "\n" split read["words"]
still:select word:value wod:(on f x do "" fuse "r" drop x end)@value
    where (on f x do ("" fuse "r" drop x) in words end)@value
    where (on f x do "r" in x end)@value
  from words
show[table random[still -10]]

but this needs 4s to process only 50k words, so the full dictionary's still out.

Developer (1 edit) (+1)

Hmm. Could be tricky to make this fast in Lil.

In general, using queries is much more efficient than loops. You can slightly simplify

where (on f x do "r" in x end)@value

as

where value like "*r*"

and you could hoist that "in" out of the loop, since it accepts a list as a left argument. Together, these ideas can make the query much more concise:

on strip x do "" fuse "r" drop x end
still:select word:value wod:strip@value
 where (strip@value) in words
 where value like "*r*"
 from words

...but probably not much faster.

You can also avoid stripping the r's twice by using a "subquery"

still:select where wod in words from
 select word:value wod:strip@value
 where value like "*r*"
 from words
(+2)
wods:"\n" split "r" drop words:read["/usr/share/dict/words"]
words:"\n" split words
still:select word:words@index wod:value
  where (wods in words)*(extract value like "*r*" from words)
  from wods
show[table random[still -10]]

this gets an answer in 13min, or 880ms without the 'wod in words' test. I've tried a few alternatives (words dict 1, readdeck of a grid, parsing and reading a json of a table) and nothing seems to cut that down. It seems like building large tables is slow. Maybe due to the allocator?

But, this was only for learning purposes and I gained a better appreciation of the query language from it.

Developer(+2)

I made some localized improvements to C-Lil's implementation of the "in" operator. Using this dictionary file:

https://github.com/dwyl/english-words/blob/master/words_alpha.txt

and this version of the entire script:

words:extract where 5<count@value from "\n" split read["words_alpha.txt"]
on strip x do "" fuse "r" drop x end
still:select where wod in words from
 select word:value wod:strip@value
 where value like "*r*" from words
show[table random[still -10]]

The patch brings execution time on my laptop from about 12 minutes (oof) to about half a second.

(+1)

Is there a nicer way to skip the empty lists resulting from this query?

 str:"hello world"
 extract list value where 1<count index by value from str 
((),(),("l","l","l"),("o","o"),(),(),(),())
Right now I have

 (list ()) drop extract list value where 1<count index by value from str 
(("l","l","l"),("o","o"))

  With the aim of showing only repeat elements, so

 "" fuse first @ (list ()) drop extract list value where 1<count index by value from str 
"lo"
 "" fuse first @ (list 0) drop extract first value where 1<count index by value from str
"lo"
 "" fuse extract L where C>1 from select L:(first value) C:count value by value from str 
"lo"
Developer(+1)

You could use a conditional to compute the extracted column (this is evaluated once per group):

extract if count value first value else () end where 1<count index by value from str

Or you could use the "gindex" column to make the filter retain at most one element per group:

extract where (!gindex)&1<count index by value from str

How's that?

(+1)

Both of those work! I didn't know about gindex, and that seems useful. I kept looking for some way to have a 'where' apply after an earlier 'where' without multiple queries, and the conditional shows a way to do that.

The conditional also suggests this solution:

 extract () unless first value where 1<count index by value from str
(+1)

Hi, I have a question about sounds. In my current project, I have an options card for various things and I want to have an option to mute all sounds in the deck. So far, I thought of putting this script everytime a sound is played, with a checkbox button named "soundsandmusic" on the options card:

if options.widgets.soundsandmusic.value=1
    play["sound"]
end

And I was wondering if there was a more simple way to do this, instead of putting an if statement everytime a sound is played.

Developer(+3)

You could define a function which "wraps" the default play[] in the deck-level script, preventing the original from being called unless your flag is set:

on play x y do
  if options.widgets.soundsandmusic.value=1
    send play[x y]
  end
end

The "send" statement is specifically designed to make it easier to set up these kinds of overrides for existing functions. (Overriding go[] has some interesting potential!)

If you think there are some situations where you may want to call play[] normally, a less invasive approach would be to give your wrapper a different name instead of shadowing the built-in function, and to change e.g. all the instances of play["foo"] within your own scripts to sfx["foo"]:

on sfx x do
  if options.widgets.soundsandmusic.value=1
    play[x]
  end
end

Keep in mind that scripts within modules and contraptions don't "see" deck-level definitions, since they're supposed to be reusable from deck to deck. If you're setting up sound effects for dialogizer text, for example, you'll need to perform the same flag-check. This can also be done in a centralized place with deck-level functions.

Does that make sense?

(+2)

Thank you, it's exaclty what I needed!

Viewing posts 81 to 85 of 85Previous pageFirst page