Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jdanish opened this issue Jun 25, 2023 · 12 comments
Open

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

jdanish opened this issue Jun 25, 2023 · 12 comments
Assignees

Comments

@jdanish
Copy link
Contributor

jdanish commented Jun 25, 2023

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
Copy link
Contributor Author

jdanish commented Jul 12, 2023

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 benloh changed the title It'd be amazing to add a new enum type (maybe called options?) Add a new enum type (maybe called options?) Aug 15, 2023
@benloh
Copy link
Contributor

benloh commented Aug 19, 2023

@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
Copy link
Contributor Author

jdanish commented Aug 19, 2023

Organism type: producer,consumer, decomposer

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

Fish state: hungry, content, full, dead

@jdanish
Copy link
Contributor Author

jdanish commented Aug 20, 2023

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
Copy link
Contributor

benloh commented Sep 7, 2023

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
Copy link
Contributor Author

jdanish commented Sep 7, 2023 via email

@benloh
Copy link
Contributor

benloh commented Sep 15, 2023

@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:

  • Setting a dictionary value
  • Testing an expression against a dictionary value

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:

  • <constantName> -- the name of the constant, e.g. 'PRODUCER`. (Note we use UPPERCASE to designate constant names to help clarify use, though technically there isn't a restriction.)
  • <GVar Type> -- the variable type of the constant, can be string, number, or boolean.
  • <constantValue> -- optional value for the constant. If a target value is not set, the value is automatically set to the constant name, e.g. addConstant PRODUCER string would assign the constant PRODUCER to the string "PRODUCER".

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:

  • <constantName> -- the name of the constant, e.g. 'PRODUCER`. (Note we use UPPERCASE to designate constant names to help clarify use, though technically there isn't a restriction.)

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
Copy link
Contributor Author

jdanish commented Sep 15, 2023

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
Copy link
Contributor

benloh commented Sep 15, 2023

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 (779a95f) 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
Copy link
Contributor Author

jdanish commented Sep 15, 2023 via email

@benloh
Copy link
Contributor

benloh commented Sep 15, 2023

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
Copy link
Contributor Author

jdanish commented Sep 15, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants