-
Notifications
You must be signed in to change notification settings - Fork 53
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
World function DSL #1320
Comments
@kostmo @byorgey what do you think about this proposal? The proposed format could subsume current world function, but might be more generic then what was imagined in #1290. I think we could still manage to squeeze it in UI though. If we assume that sample has priority over murmur, then we can show always both and allow user to enable either or both. Maybe at first we would not allow the player to add new list entries in UI. 😅 |
I think this is headed in the right direction, though I think I would design the DSL rather differently. I will give it some thought and come back with some concrete proposals for comparison. I really don't think we should worry about the UI at this point --- the idea is just to be able to have a more flexible way to design worlds in scenario descriptions. |
Edited 6/15 to include feedback/ideas. Here are my current thoughts towards DSL for world functions. I think it ought to be a real programming language (though not a general-purpose or Turing complete one), not just a configuration language: that is, there should be a sense of building/computing the world function we want out of compositional pieces rather than specifying a bunch of configuration data. I think all the below will be straightforward to implement (though probably still a lot of work). @xsebek , @kostmo would love to hear your thoughts/feedback! Design considerationsThe language ought to be able to handle things like:
Types
FramesEach value of type
SyntaxI will be deliberately vague about concrete syntax; I imagine that it might be nice to support both compact, "applicative" syntax like
Perhaps one could even use a mix of the two, using YAML syntax for big structures and using the compact syntax for expressions that would be tedious to write out in a tree form. In what follows I will use compact syntax but the concrete syntax is not really the important thing. I did consider the possibility of using an embedded DSL, so that world descriptions would actually consist of Haskell code. This would definitely save some work implementing a type checker and interpreter, however, (1) it would require some complex code to interpret Haskell within Haskell, which I'd rather not have to do given the recent unreliability of the Another crazy idea would be to piggyback off of our existing machinery and use swarm-lang syntax and/or types. However, we definitely don't want to use CESK machines to run these since they need to be as fast as possible. ExpressionsFor each kind of expression I will give an example and explain the type and semantics. This should not be taken as an exhaustive list, there might be other things we want that I have missed and we can certainly explore alternatives, add more things later, etc.
ParametersIf we wanted to allow user-configurable parameters other than the seed (as in #1290) we could allow a Examples
|
I dare say this would be an essential feature for ergonomic world design. Manually calculating the offset for each tile in #1334 was tedious and not scalable, and is essentially now read-only. There are some concepts from Diagrams that would greatly facilitate world design. In addition to the "relative positioning" described above, also being able to define an "envelope" that may be larger "map" rectangle (see the giant, uniform rectangle here as a motivating example). It would be a riot to make I would also like to be able to independently specify This world description language is shaping up to be a hybrid of a fragment shader and box model. Very interesting... |
Noted. I wasn't 100% sure when I wrote that, but now that I've thought through it a bit more I'm confident they should be straightforward to implement.
Yes, makes sense. I think all we need is something like
Ah, makes sense. That also sounds easy to add.
Ha, yes, that's a nice way to think of it. Somehow I seem to keep designing DSLs in this space. See also https://hackage.haskell.org/package/boxes and https://github.com/diagrams/active/ and https://hendrix-cs.github.io/csci360/projects/quilt/Quilt.html ... |
DSL for programming worlds, towards #1320 and #29 (and, indirectly, toward #50, since the world DSL should make a nice target for world saves) . Eventually this should be able to recreate all the world description/building features we have, though there is still a long way to go. But currently we can at least recreate the "classic" procedurally-generated world. I think this is a solid foundation we can merge as a first step, and then work on adding more features in subsequent PRs. Below are some notes that should help in reviewing. Note that the large number of files changed is due in large part to the elimination of the `default` field in scenario descriptions; see the "changed files" section below for an overview of the important/interesting changes. Issues split off from this one: #1394 #1395 #1396 #1397 Major changes ============ - New `data/worlds` subdirectory - All `.world` files are parsed at load time and saved in a `WorldMap` which gets threaded through, similar to `EntityMap` (perhaps we should think about passing around a single record instead) - Standard "classic" world - Used to be `testWorld2`, defined in Haskell code; now it is defined via the DSL in `worlds/classic.world`. This should make it much easier to experiment with variations. - We can now automatically extract entities mentioned in a world DSL term with `extractEntities`. There used to be an explicit list in `testWorld2Entities`, used to check pedagogy, generate documentation, etc., but it turns out it had (predictably) gotten out of date! This can't happen anymore. - It is now referenced in several tutorials (backstory, farming, world101, speedruns, etc.) - The `default` field of world descriptions is no more: one can use `dsl` to just specify a constant - Note in `Swarm.Game.State`, `dslWF` and `arrayWF` are combined using the `Monoid` instance to create `wf`. - `Erasable` - It used to be the case that if some kind of default terrain + entity was specified (e.g. stone + water), any `map` would completely override the default. However, we want to move towards combining everything with a `Monoid` instance. But by default this means the default entity would show through anywhere the `map` did not specify an entity. So we need a way to explicitly "erase" an entity from a lower layer. - If `e` is a `Semigroup`, then `Maybe e` is a `Monoid` where `Nothing` acts as an identity element. Likewise, `Erasable e` is a `Monoid` but adds two new elements: `ENothing` to be an identity, and `EErase` to be an *annihilator*. i.e. combining with `EErase` is like multiplying by zero. - We can now specify `erase` as an entity to override entity underneath. - There are several Haskell files with only changes related to `Erasable`, relating to e.g. the world editor, `PCells`, etc.; I'm not 100% sure I've always done the right thing here. DSL overview =========== - Integer, float, and Boolean literals. Note that `3` is *always* an `int`, and `3.0` is a `float`. It makes things much easier to not have to deal with `3` possibly being either `int` or `float`, though it does make things slightly more annoying for programmers. - Standard boolean, arithmetic, and comparison operators - `if ... then ... else ...` - `<>` operator for combining via `Semigroup` instance - Cell literals are enclosed in curly braces. Unlike the previous awkward world description syntax with one, two, or three-element lists denoting terrain, terrain + entity, or terrain + entity + robot, there can now be any number of elements in any order. - `{foo}` will be resolved as either terrain, an entity, or a robot, whichever is successful. So if the names are unambiguous one can just write `{tree}` or `{stone}`. - It is possible to explicitly indicate the type of cell value with syntax like `{entity: tree}` or `{terrain: stone}`. - Multiple items separated by commas is syntax sugar for combining with `<>`. e.g. `{tree, entity: boulder, stone} = {tree} <> {entity: boulder} <> {stone}`. - Ability to refer to the `seed` - Refer to the current `x` or `y` coordinates or the `hash` of the current coordinates - `let`-expressions for multiple variables: `let x1 = e1, x2 = e2, ... in ...` - `overlay [e1, e2, ...]` layers `e1` on the bottom, `e2` on top of that, etc., using the `Semigroup` instance for world functions - `"foo"` imports the DSL term in `worlds/foo.world` - `perlin` function to generate perlin noise - `mask` function to mask with a condition Changed files =========== - `Swarm.Util`: moved the `acquire` function here and gave it a more descriptive name. - `Swarm.Doc.Gen`: can now extract mentioned entities directly. - `Swarm.Game.Failure`: added new failure modes - `Swarm.Game.Scenario.Topography.WorldDescription`: get rid of `defaultTerrain` field, add `worldProg` for DSL. - `Swarm.Game.State`: see comment. - `Swarm.Game.World`: a bit of reorganization. Added a bunch of modules under this. - `Swarm.Game.World.Coords`: moved some code here from `Swarm.Game.World`. - `Swarm.Game.World.Gen`: moved some things here from `Swarm.Game.WorldGen` (also deleted a bunch of irrelevant code), and also added the `extractEntities` function to get all entities mentioned by a DSL term. - `Swarm.Game.World.Syntax`: raw, untyped syntax for world DSL terms. - `Swarm.Game.World.Parse`: parser for world DSL terms. Fairly standard. - `Swarm.Game.World.Typecheck`: takes raw, untyped terms produced by the parser and both typechecks and elaborates them into a simpler core language. An interesting feature is that the core language is *type-indexed*, so that the Haskell type system is actually ensuring that our typechecker is correct; every typechecked world DSL term value has a type which is indexed by a Haskell type corresponding to the type of the underlying DSL term. For example, `{entity: tree}` would have a type like `TTerm [] (World CellVall)` etc. Once terms make it through the typechecker, there cannot possibly be any bugs in the rest of the pipeline which would result in a crash, because the Haskell type system. (There could of course be *semantic* bugs.) Understanding exactly how the typechecker works is not too important. Of interest may be the `resolveCell` function, which determines how we decide what `Cell` is represented by a cell expression in curly braces. - `Swarm.Game.World.Abstract`: compile elaborated, typechecked world DSL terms down into an extremely simple core language with only constants and function application. This gives us very fast evaluation of world DSL terms. Understanding this module is not really necessary but there is a link to a blog post for those who are interested in how it works. - `Swarm.Game.World.Compile`: a further processing/compilation step after `Swarm.Game.World.Abstract`. Currently we don't actually use this, since it doesn't seem like it makes a big efficiency difference. - `Swarm.Game.World.Interpret`: interpreter for abstracted world DSL terms. - `Swarm.Game.World.Eval`: just puts together the pieces of the pipeline to evaluate a typechecked world DSL term. - `Swarm.Game.World.Load`: just loading world DSL terms from disk.
@byorgey shall we close this? 🙂 There are already some followup Issues, so I would rather create a new Meta Issue to track progress, rather than keeping this around. |
@xsebek Why not just turn this issue into the meta issue? This issue still has a lot of description, specifications, ideas, etc. that are not captured in any of the followup issues, so I'd rather not close it. |
Because reading discussion in threads with edited messages and updated initial description is confusing. 😅 I think citing the relevant messages in new Issues will be clearer. Also I am pretty sure GitHub does not send notifications for edits. |
OK, that's fair. 😄 Let me split out some more issues from this one first. |
Is your feature request related to a problem? Please describe.
The world function is currently static except for seed. Being able to parametrise it would be helpful in several places:
Another related problem is that we currently have competing ways to specify the world.
Namely specifying the seed, default entity and a part of map.
When the default entity is specified, it will disregard the seed and default world function.
Describe the solution you'd like
We should create a simple configuration scheme, that we can play with, modify and extend.
In YAML the current world function could look like this:
The Haskell datatype
WorldParameters
would follow this structure and get interpreted liketestWorld2
.Describe alternatives you've considered
In general (ignoring the specific syntax proposed here) the only alternative is passing parameters ad-hoc in code.
That is fine for current seed and default entity, but will need more structured approach later.
Additional context
Adding a new field should not brick all player save files... at least once we have a stable release. 😄
A new benchmark would be needed.
The text was updated successfully, but these errors were encountered: