Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Lil Programming Questions Sticky

A topic by Internet Janitor created Oct 28, 2022 Views: 3,552 Replies: 144
Viewing posts 39 to 45 of 45 · Previous page · First page
(+1)

This is a question regarding scripting. I would like to have a button "toggle" between two states; revealing a widget A, then hiding a widget A, then revealing a widget A, etc. but don't know what the script for that would look like. And speculatively, would it be possible to add other elements so that the button might cycle between revealing widget A, to revealing widget B,  to revealing widget C, then back to hiding them all? Thanks in advance!

Developer (1 edit)

The first example is very straightforward: widgets have a .toggle[] method. If you call it with a single argument it will alternate the value of their .show attribute between "none" and the string specified ("solid", "transparent", or "invert"):

on click do
  a.toggle["solid"]
end

You can see more examples of .toggle[] in the release notes.

Cycling through displaying several different widgets is a bit more complicated.


One approach would be to test the .show attributes of widgets, find which one is currently visible, and then set the .show attributes of all the widgets in a group in order to advance to the "next" selection. The main issue here is it's very ugly and inconvenient to add more widgets to the cycle:

on click do
  if     "solid"~a.show a.show:c.show:"none" b.show:"solid"
  elseif "solid"~b.show b.show:a.show:"none" c.show:"solid"
  else                  c.show:b.show:"none" a.show:"solid"
  end
end

We can reduce repetition and make maintenance a bit easier by making a list of widgets of interest, scanning for them in a loop, and using an index modulo the length of the list to pick the next item:

on click do
  cycle:a,b,c
  current:0
  each wid index in cycle
    if "solid"~wid.show current:index end
  end
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

If we want to get really clever, there's also a "vector-oriented" way to handle that search instead of an each-loop. In general, multiplying a list of integers [0,n) by a length-n list containing zeroes and a single one will "mask out" the index of the one:

(0,1,2,3)*(1,0,0,0)   # (0,0,0,0)
(0,1,2,3)*(0,1,0,0)   # (0,1,0,0)
(0,1,2,3)*(0,0,1,0)   # (0,0,2,0)
(0,1,2,3)*(0,0,0,1)   # (0,0,0,3)

So we could compute "current" like so:


Giving a complete script as follows:

on click do
  cycle:a,b,c
  current:sum(range count cycle)*"solid"=cycle..show
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

If you want to include "don't show anything" in the cycle, you can add a "0" dummy element to the list of widgets; 0.show will always be 0, which is not equal to the string "show", and attempting to set the .show property of a number is likewise harmless:

on click do
  cycle:0,a,b,c
  current:sum(range count cycle)*"solid"=cycle..show
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

Does that answer the question?

Edit: one more note: depending on what you're trying to do it might be worth considering using a slider widget instead of a button; Sliders naturally represent choosing between a range of integers, which could represent an index into a list:

on change val do
  cycle:0,a,b,c
  cycle..show:"none"
  cycle[val].show:"solid"
end

See also, the enum contraption.

(+1)

This is great. Exactly what I was looking for (and more!) It's enlightening to see how many different ways their are to tackle a problem. Thanks so much for this John.

(2 edits) (+1)

Hi! I’m a bit stuck trying to figure out how to search for a substring inside a table. I’m thinking of a journal application where I have Date and Entry columns, and I was hoping to add a search function that returns rows that match a certain substring. Reading the docs, it looks like I can match the first part of the string by doing this:

select Date Entry where ("hi%m" parse Entry) from journal_list.value

But I can’t quite figure out how to match a string that doesn’t start at the beginning. 😅

Developer (1 edit)

The "in" operator can test whether a substring is present in a string:

 "upon" in "stars upon thars"
1

To use this on a column, we could define a helper function. While we're at it, this can also lowercase the column we're searching for a case-insensitive match:

on like needle haystack do
     each v in (list "%l")format haystack
         needle in v
     end
end

Allowing us to write a query like so:

ex:insert Date Entry with
     20210412 "Once Upon A Time"
     20220709 "There once were"
     20230101 "stars upon thars"
end
select where like["upon" Entry] from ex
# +----------+--------------------+
# | Date     | Entry              |
# +----------+--------------------+
# | 20210412 | "Once Upon A Time" |
# | 20230101 | "stars upon thars" |
# +----------+--------------------+

How's that?

(+1)

Amazing thank you so much for the help! I’m still getting used to Lil you can do so much with so little. I’m just now seeing this: “The each loop collects together the results of each iteration of the loop and returns them” 🤯

Developer

The absence of a direct equivalent to SQL's "like" operator has plagued me for some time, so in Lil v1.40 I decided to introduce a first-class equivalent. Using this operator, we can now perform the same type of case-insensitive search (as well as quite a few handy variations) without needing a helper function:

ex:insert Date Entry with
     20210412 "Once Upon A Time"
     20220709 "There once were"
     20230101 "stars upon thars"
end
select where ((list "%l")format Entry) like "*upon*" from ex
# +----------+--------------------+
# | Date     | Entry              |
# +----------+--------------------+
# | 20210412 | "Once Upon A Time" |
# | 20230101 | "stars upon thars" |
# +----------+--------------------+

If you prefer the previous way, it will be necessary to rename the "like[]" helper function, since "like" is now a reserved word. Sorry for any inconvenience!

Hello,

There's probably a trick for that, but I didn't find a way to create a single element dict:


("foo") dict ("bar") => {"f":"b", "o": "r"}



Whereas:

("foo", "baz") dict ("bar", "qux")


does the expected.


Developer

The straightforward way is to use the "list" operator, which wraps its argument in a length-1 list:

 (list "foo") dict (list "bar") 
{"foo":"bar"}

A fancier, more concise way (provided your keys are strings) is to perform an amending assignment to an empty list, like so:

 ().foo:"bar"
{"foo":"bar"}
(+1)

Thanks! very helpful!

Some cards in my deck have a field called "group" with a number in the text field.

In my deck, how would I go about selecting a random card that matches a specific group number and then send the player to that card?

Developer (1 edit)

To obtain a list of the cards with a particular group number you could use a query something like

c:extract value where value..widgets.group.text=2 from deck.cards

(Note that if a 'group' field doesn't exist it will behave the same as a card in group "0"; you probably want to count from 1 for your groups)

Given that list, you can pick a random item and navigate to some card like so:

go[random[c]]

Or, all at once,

go[random[extract value where value..widgets.group.text=2 from deck.cards]]

Does that make sense?

(+1)

Yes! Thank you.

(+1)

Is it possible to hide a column in a grid? I find myself having to use a lot of hidden grids and then selecting a subset of the columns into a visible grid. Which is fine enough, but I wonder if I'm misunderstanding the scope of variables or features of grids and maybe there's a way to not do this.

Developer

You can adjust the widths of the columns of a grid to hide trailing columns. In "Interact" mode, a small draggable handle appears between column headers:


You can reset the columns to their default uniform spacing with the "Reset Widths" button in grid properties.

Manually resizing column widths enforces a minimum size. You can set column widths to 0 programmatically via "grid.widths", but a 0-width column looks a bit odd; I'll make a note to fix that in the next release.

(+1)

Somehow I missed the grid.widths value in the grid interface. Thanks!

Developer

FYI i've patched the problem with 0-width columns in grids; that should now offer a fairly flexible option for visually suppressing columns without physically removing them from the underlying table. You can try it out now at the bleeding-edge source revision, and the fix will be incorporated into the v1.41 release; probably next week.

I'm having a hard time wrapping my head around arrays, maybe it's not what I actually need to be using. Basically I have two fields that I can enter text and I want to take each field and append them to an array that splits it all up by word. So "This is my text"  in field1 and "More text" in field2 would be ["This", "is", "my", "text", "More", "text"] that I could then manipulate.

Developer

Starting from the two fields you describe:

You can obtain the text of either field through its ".text" attribute. The "split" operator breaks a string on the right at instances of the string on the left and produces a list. Splitting one string on spaces gets us part of the way to what you're asking for. We can then use the comma operator to join a pair of lists.

" " split field1.text

The best way to experiment with this sort of thing is to use The Listener. Ask a "question", get an answer:

Another approach for gathering space-separated words from several fields would be to use an "each" loop:

Does that help point you in the right direction?

(+1)

This does! When looking at the docs I guess this made me think "split" and such were tied specifically to tables. But now I'm starting to wrap my head around this.

Developer (1 edit)

In general, for string manipulation the main tools Lil provides are:

  • drop, take, split, and parse for cutting strings apart into smaller pieces
  • fuse and format for gluing pieces together to make strings
  • like, in, <, >, and = for comparing and searching strings

...and a few of the more complicated utility functions functions in rtext do also apply to plain strings.

I may not understand fully how random works.

Say I have a table with 5 rows stored in 'temp'

random[temp,1] will usually get me a row with all the data, But sometimes it just results in the integer 1.


Why is that?
Developer

The Lil "comma" operator forms a list by combining the elements of its left and right arguments.

If you use this operator to combine a table and a number, the table will be coerced to its list interpretation (a list of the rows of the table, each a dictionary), and then combined with the number 1 (whose only element is itself), forming a list of several dictionaries and the number 1. Applied to such a list, random[] will occasionally choose the 1.

When you call a Lil function with multiple arguments, commas should not be placed between arguments. You probably meant

random[temp 1]

 Instead of

random[temp,1]

Furthermore, note that if you specify 1 as a second argument to random[] you will get a length-1 list as a result, whereas if you call random[] with only a single argument you will get a single value.

In preparing this post I have also observed that there is some inconsistency between native-decker and web-decker with respect to applying random[] to table values, which may have compounded the confusion; I'll have this fixed in the v1.41 release tomorrow. In the meantime the alternative is to explicitly crack the table into rows before making a random selection, like so:

random[(rows temp)]
(+1)

Thank you!

Viewing posts 39 to 45 of 45 · Previous page · First page