theRAPTLab / gsgo

GEM-STEP Foundation repo migrated from GitLab June 2023
1 stars 1 forks source link

Add a new enum type (maybe called options?) #755

Open jdanish opened 1 year ago

jdanish commented 1 year ago

The idea is that if you declare a variable, you could then set options (rather than min and max) that would then be made available in the wizard. So, something like:

addProp color options prop color addOption red prop color addOption blue prop color addOption green

And then if you need to test or set it, the wizard would give a list of the options so that you knew that you were picking a valid choice.

I could imagine these ultimately being part of a number or string (or both)?

Something like:

addProp color string 'gray' prop color addOption 'red' prop color addOption 'green'

Then instead of setTo you could use setToOption and it'd give you a list of options?

jdanish commented 1 year ago

Two more thoughts: 1) It might be nice to make this a name and value, so 'red' might have a value of 'red' or a value of '255, 0, 0' or whatever. 2) If we can use some of this for setting color for at least some cases ... that'd be amazing.

benloh commented 1 year ago

@jdanish We could use some more examples of enumerated variables. Other than color, can you provide three different examples of enumerated variables and how they might be used?

jdanish commented 1 year ago

Organism type: producer,consumer, decomposer

Energy level low (1), medium (5), high (10)

Fish state: hungry, content, full, dead

jdanish commented 1 year ago

Sorry ... how to use:

For type, it might be that these are just labels that get applied and the enum is just to help guarantee consistent spelling. Alternatively, if a character is type "producer" then it gains energy from touching the sun. If it is type "consumer" it won't, but gains energy from touching a plant. Etc.

For energy level, this might be a way to start the characters out at one of 3 levels using a drop-down with a label rather than having to guess good numbers.

Fish, uses the state to determine how it looks (if hungry show the hungry thought balloon, if content, do not, if dead, do not interact). Etc.

benloh commented 1 year ago

Thinking this through some more, we might have to implement an optionsString type and an optionsNumber type, otherwise any math you do will be broken, e.g. string "5" is not the same as number 5.

I suppose we could add a way to specify the value type with out a new keyword. But kind of depends on how expressions will be handled.

jdanish commented 1 year ago

Would it be possible to do a setOptions similar to setMin? Otherwise that makes sense. Thanks!

benloh commented 1 year ago

@jdanish working through the design of enums...I want to run this by you and @dsriseah to make sure that this makes sense.

I think you're wanting to do multiple things:

But the GEMSCRIPT limitations around being able to assign GVar values to properties and feature properties are still active. e.g. we still can't do something like prop entityType setTo PRODUCER where PRODUCER is a enum/dictionary value. We still have to rely on stack operations to do that kind of assignment.

We did look into trying to implement setToOption, but after much time exploring numerous approaches, including building "options" into GVars, it became clear that while we could support that approach via pure GEMSCRIPT coding, the wizard interface would require a very deep architectural changes to make it work.

After conferring with Sri, we think the appropriate solution is to introduce the concept of Constants. (This design doc will turn into the wiki writeup)

This mostly works now, but there is a ton of cleanup that we still have to do to get it to a demo-able state. And if we want color support for the Costume feature, we would also need to add some new methods.

Does this sound like it would address your needs?


Constants

Constants can be defined for any agent, including the global agent. Use the addConstant keyword. To assign a prop or featureProp to a constant value, use the constantPush keyword.

addConstant keyword

You define agents using the addConstant keyword

Syntax

addConstant <constantName> <GVar Type> <constantValue>

Where:

Example:

addConstant PRODUCER string 'prod'

constantPush keyword

A prop or featProp can be set to a constant by using stack operations.

Syntax

constantPush <constantName>

Where:

Example:

addConstant PRODUCER string 'prod'
constantPush PRODUCER
propPop Fish.entityType

Referencing Constants and Testing Expressions

Constants can be referenced as an agent object property and used as part of expressions. e.g.

... {{ ...character.constant.PRODUCER... }} ...
... {{ ...Fish.constant.PRODUCER... }} ...

Typically you would use a constant reference as part of a testing expression, e.g.

ifExpr {{ Fish.prop.entityType.value === Fish.constant.PRODUCER }} [[
  dbgOut "I am a producer"
]]

NOTE that ifProp doesn't work because we GVars can't reference another object

addConstant BEAVERCOSTUME string 'beaver.json'
// THIS WON'T WORK -- `BEAVERCOSTUME` is not defined in this context
ifProp character.costumeType equal BEAVERCOSTUME [[ ... ]]

Global Constants

Since we are using agent object references, references to the global agent can also work. This is how you would define global constants.

# BLUEPRINT Global
// Add Global Constant
addConstant EVIL string 'evil'
addConstant GOOD string 'good'
# BLUEPRINT Shark
// use pop/push
constantPush Global.EVIL
propPop Shark.alignment

// Test Global Constant
ifExpr {{ Shark.prop.alignment.value === Global.constant.EVIL }} [[
  dbgOut "Bwahaha"
]]

Special Constants (e.g. color)

Colors are a special use case. While we can introduce the concept of tuples, it's probably easier at this point to use css strings to define colors. e.g.

// define the colors
addConstant RED string '#f00'
addConstant GREEN string '#0f0'
addConstant BLUE string '#00f'

We can then add a new feature property that can be used to set Costume colors. (We can't use a feature method call because we would run into the same problem of not being able to reference a constant). For example, we can introduce a colorCSS feature property that simply accepts css strings, so you could potentially use #f00, #f003, #ff0000, #ff000033, red, rgba(255,0,0,0.3), and any other valid css string. The downside of this is we would lose the ability to do any math on colors without adding conversion methods.

// use push to set the color
constantPush GREEN
// define a new Costume property that supports css hex
featPropPop character.colorCSS

Example Scripts

Some example scripts:

Defining Constants

// Strings
addConstant PRODUCER string 'prod' // string can be blank, just used for testing/confirming values
addConstant CONSUMER string 'cons'
addConstant DECOMPOSER string 'decomp'
// Numbers
addConstant LOW number 0
addConstant MED number 1
addConstant HIGH number 2

Setting a Prop to a Constant Value

// use pop/push
constantPush PRODUCER
propPop Fish.entityType

Setting a Global Prop to a Constant Value

# BLUEPRINT Global
addConstant EVIL string 'evil'
addConstant GOOD string 'good'
# BLUEPRINT Shark
// use pop/push
constantPush Global.EVIL
propPop Shark.alignment

Testing an Expression

ifExpr {{ Fish.prop.entityType.value === Fish.constant.PRODUCER }} [[
  dbgOut "I am a producer"
]]

NOTE that ifProp doesn't work because we GVars can't reference another object

addConstant BEAVERCOSTUME string 'beaver.json'
// THIS WON'T WORK -- `BEAVERCOSTUME` is not defined in this context
ifProp character.costumeType equal BEAVERCOSTUME [[ ... ]]

Testing a Global Expression

ifExpr {{ Shark.prop.alignment.value === Global.constant.EVIL }} [[
  dbgOut "Bwahaha"
]]

Implementing Color Support

This will be dependent on adding a new method/property to the Costume Feature to support something like this.

// define the colors
addConstant RED string '#f00'
addConstant GREEN string '#0f0'
addConstant BLUE string '#00f'
// use push to set the color
constantPush GREEN
// define a new Costume property that supports hex
featPropPop character.colorHex
jdanish commented 1 year ago

I feel like I'm missing something here, and apologize. The main goal is to simplify the selection of values by a student. This feels like it is a) no different from using an all caps property, and b) requires advanced scripting anyhow (e.g., Exp). So while the color setting is cool, this doesn't really help simplify kid's lives and I am actually not sure I am seeing the advantage of using a constant over a prop that is well named? I apologize if I am missing something key.

benloh commented 1 year ago

Yeah, the fundamental problem is that we cannot assign a GVar value to another property without resorting to stack operations. So any implementation is going to look like this.

We can hack together a code-only version that only supports options for local agents (not global), but as soon as we try to implement a wizard for this, it all falls apart. The wizard can't access the options list.

e.g. we can write this in GEMSCRIPT. and it works great...

// define the options
addProp colour string 'black'
prop colour addOption RED string '#f00'
prop colour addOption GREEN string '#0f0'
prop colour addOption BLUE string '#00f'
// assign prop to an option
prop colour setToOption RED

..but if we use the wizard to select the line prop colour setToOption RED, the option RED is treated as a string, not a selectable list of options. To do what you want, we'd have to somehow construct a list of options (RED, GREEN BLUE) to pull out of the colour prop, and more importantly, be able to access the values of the options (e.g. #f00, #0f0, etc). But during the wizard construction time, we can't access those values, so we can't construct the list of choices.

It occurs to me one possible terribly hacky workaround is to add something similar to the comment-bookmark widget (779a95f812e27c29e92471f367e2d76a102ef3f9) where we bypass the standard wizard UI and inject a selection popup UI to access the list of options. The question is can we read the agent property options to construct the list. This would be hacky and probably setting a bad precedent.

jdanish commented 1 year ago

Pardon my ignorant question but thinking about how the Boolean wizard works, what if we had another gVar type that functioned like a Boolean but you could add key / value pairs and they all appear in the wizard as a drop-down? We could assume they are all strings or have an enumString and enumNumber (maybe called optionsString and optionsNumber)? I think that’d work like a mega Boolean from a user’s perspective and would make it so you don’t need to set to arbitrary values but a set list.Or if we have a setToOption instead of setTo which pulls the list? I like the first idea better but either would be better than the stack operations which I think only I understand…Or would that have the same problem? I love getting colors to be easier but I fear I’d never use constants because I can more easily have a comment above:// change this to ‘producer’ or ‘consumer’prop Animal.type setTo ‘producer’Kids could make mistakes but it’s far more intuitive than the stack functions for them I think.

benloh commented 1 year ago

Yeah, stack operations are not at all intuitive.

And the questions aren't ignorant. It's a complex system that I definitely have trouble getting my head around.

Creating another GVar type was essentially what I did with the addConstant keyword. The basic problem is that while GEMSCRIPT allows us to make constructs like <keyword> <set-to-method> <value>, the <value> parameter only allows us to reference a simple string, number, or boolean, NOT a GVar or more complicated object. So even though we can arbitrarily define new GVars (e.g. a constant 'PRODUCER' via addConstant addOption 'PRODUCER' or a boolean-like variable), our referent <value> can't be the the GVar object -- prop entityType setTo PRODUCER doesn't work because PRODUCER is not a simple javascript string, number, or boolean, but a GVar or other object. prop entityType setTo "producer" works because "producer" is a simple string, but prop entityType setTo PRODUCER doesn't work because PRODUCER isn't a simple string, number, or boolean. This is why we need to use stack operations for all this stuff.

That said, I'm playing with a workaround hack ala comment styles, where we essentially add another compile pass to first pull out constant definitions, and then hack an interface that allows you to select one of the defined constants. This works because we don't fundamentally change the script engine, but instead just shim in a UI to support selection. We'll see if it works.

jdanish commented 1 year ago

OK, cool. I’ll wait on the hack before offering more rambling then :)