HYPERTHINGS

Project Summary

IN PROGRESS An roguelike-like about understanding yourself and your environment through observation and creative spellcasting.

Written in Common Lisp

Expected release in late 2021 for Linux and Windows.

A recording of an avatar navigating a map.

Contributor Commits

Interpreting Spells: Slot Queries

Tue, 28 Sep 2021 09:40:49 -0500

For game components, rules embedded in the slot definitions accept a :with keyword parameter that takes an object.

grammar::interpret can be specialized on the class of such objects to build the final output of the parse.

I have chosen to make a class that is a kind of newtype (see haskell) definition around comparator functions, which are used to compare slot values with a parse-obtained value during queries.

For example:

(<query-term> (:or <thing1> <thing2>)  
                  :with (slot-compparison #'degree<=)) 

will compare a slot value with the result of a or rule using the function DEGREE<=

As currently written, the GRAMMAR:INTERPRET implementation for SLOT-TERM-RULE instances outputs an object that reifies a query of the ECS into an instance of SLOT-QUERY.

Because a spell expression may involve numerous distinct slots among different components, the SLOT-QUERY instances are exanimed before being run -- they are prioritized to minimize run time.

~Colin Okay

Graphical Sprites, Material Interactions, Growth, and More

Thu, 19 Aug 2021 08:03:03 -0500

In the last month I have been working on adding (somewhat pitiful, but charming I hope) graphical sprites to the game. I have been using FLOSS software tools like the GNU Image Manipulation Program and the Gnome Color Picker.

I have been making fairly small sprites (10x16 pixels) and then resizing them at render time. Sprites are assembled into a sprite sheet using my own project imbricate.

In addition to "graphics", the game now supports several interelated systems that control things like the flow of liquids, plant growth, creature spawning, fire, and the behavior of "sentient" beings.

Its been a blast.

~Colin Okay

Added RULE-ACTION slot to GRAMMAR-RULE and updated accordingly

Wed, 14 Jul 2021 11:39:52 -0500

Formerly, to specify a rule in a slot I had to supply the following information:

RULE-NAME RULE-CLASS PRODUCTION-EXPRESSION 

the PARSE method would be specialzied on the rule class to produce unique results. But this had the unfortunate consequence of separating what you do with a parse from what was actually being parsed (i.e. the production expression). So I have modified the situation to the following:

RULE-NAME PRODUCTION-EXPRESSION :ACTION ACTION-EXPRESSION 

where :ACTION is just a keyword (:-> is also valid here) and ACTION-EXPRESSION is either a non-null symbol or a list. This keyword argument is optional. I.e. RULE-NAME PRODUCTION-EXPRESSION would be enough.

If it is a symbol, it should name a function of two arguments RULE and VALUE. The RULE argument will be passed the rule instance itself (in case you need the additional context that instance may provide) and VALUE is what the parse of the production expression yields on a successful parse.

Rule classes can still be specialzied. Now it is done, however, more reasonalby on a per-grammar basis. If necessary one may specialize the method RULE-CLASS on a grammar to get ahold the the desired rule class. This is just what is done in the case of classes whose metaclass heirarchy includes GRAMMATICAL-CLASS. Those grammars use the subclass of GRAMMAR-RULE called SLOT-TERM. Additional logic is built around SLOT-TERM to allow for querying the ECS based on a component and slot and updating an entity in a similar way.

~Colin Okay

Added RULE-ACTION slot to GRAMMAR-RULE and updated accordingly

Wed, 14 Jul 2021 11:39:52 -0500

Formerly, to specify a rule in a slot I had to supply the following information:

RULE-NAME RULE-CLASS PRODUCTION-EXPRESSION 

the PARSE method would be specialzied on the rule class to produce unique results. But this had the unfortunate consequence of separating what you do with a parse from what was actually being parsed (i.e. the production expression). So I have modified the situation to the following:

RULE-NAME PRODUCTION-EXPRESSION :ACTION ACTION-EXPRESSION

where :ACTION is just a keyword (:-> is also valid here) and ACTION-EXPRESSION is either a non-null symbol or a list. This keyword argument is optional. I.e. RULE-NAME PRODUCTION-EXPRESSION would be enough.

If it is a symbol, it should name a function of two arguments RULE and VALUE. The RULE argument will be passed the rule instance itself (in case you need the additional context that instance may provide) and VALUE is what the parse of the production expression yields on a successful parse.

Rull classes can still be subclassed, however, now it is doen more reasonalby on a per-grammar basis. If necessary one may specialize the method RULE-CLASS on a grammar to get ahold the the desired rule class. This is just what is done in the case of classes whose metaclass heirarchy includes GRAMMATICAL-CLASS. Those grammars use the subclass of GRAMMAR-RULE called SLOT-TERM. Additional logic is built around SLOT-TERMS to allow for querying the ECS based on a component and slot and updating an entity in a similar way.

~Colin Okay

Embedding Grammar Into Classes

Sat, 10 Jul 2021 09:25:42 -0500

This is a pretty significant commit. Surely there will be bugs, but as of now the reason I undertook to refactor is complete.

See, the game is principally driven by the spell language, which is the only part of the game that is especially interesting. To put it another way, a boring langauge would result in a boring game.

It follows, therefore, that experimenting with the language should be as easy as possible. An additional premise to that conclusion is that: frictionless experimentation leads to faster exploration of possibilities which leads to better results. But at some point a few weeks back tweaking the langauge began to feel like a drag -- why?

Well, initially I had used a single static tree (modeled as a list of lists) to define the grammar. The approach was very much inspired by that taken in a few chapters in PAIP. This works great if all you want to do is define a language.

But for Wherefore Wizardry the langauge isn't just a language - it is intimately bound up with the data of the game. Changing the data of the game, for example by redefining a component class, would entail redefining the language. Furthermore there were additional linguistic needs apart from parsing and interpreting the spells that players cast: producing descriptions of entities based on their properties, and reporting things that happen to entities over time. Each of these language-oriented mechanics are closely connected with the data model of the game. When I was using the PAIP-esque approach I found myself needing to synchronize changes from one place to another, and much of that synchronization relied on implicit knowledge, a known code smell. See A Philosophy Of Software Design

"There must," I thought, "be a better way". Hopefully this commit inaugurates that better way. As of this commit, certain grammatical rules can be embedded into class definitions as slot options. When a class is redefined, so is the grammar! Everythign is in one place: the component classes.

I had to do some tricky MOP work to get this all to work right, and there is at least one hacky work-around involving my failure to figure out why a MOP method wasn't being called when I thought it should be, but it works!

~Colin Okay

Defined Grammatical Component Metaclasses

Fri, 9 Jul 2021 09:40:45 -0500

The base metaclasses that are integrated with the spell language gramamr inherit from the component metaclasses as wella s a subclass of GRAMMATICAL-CLASS called SPELL-GRAMMATICAL-CLASS.

SPELL-GRAMMATICAL-CLASS simply ensures that the singleton SPELL-GRAMMAR is the grammar used by its instance classes.

For each of the three component classes there is a grammatical component class:

  1. composite is the grammatical version of record
  2. property is the grammatical version of flag
  3. category is the grammatical version of enum

The idea is that some components are exposed to the player for use in spells and some are not. RECORD, FLAG, and ENUM are not, while COMPOSITE, PROPERTY, and CATEGORY are.

~Colin Okay

Factored component metaclasses into three kinds

Fri, 9 Jul 2021 09:24:15 -0500

The three kinds are

  1. record-component : which is extends indexed-class
  2. flag-component : which extends singleton
  3. enum-component : which extends multiton

This factoring became necessary because of the indexed classes. It doesn't make sense to index flag and enum style components, hence indexing needed to be separate from the notion of a component.

Components, again, are classes that store their instances keyed by the entity to which they are associated.

Some types of component - namely records and enums - also keep a reverse store, a mapping from instances to entities. In the case of records that mapping is one-to-one. In the case of enums, the mapping is one-to-many; i.e. many entities are associated with a single instance of an enum component.

~Colin Okay

Indexing logic

Wed, 7 Jul 2021 09:19:24 -0500

Implementing the indexing logic necessitated building out the component protocol. Functions were added:

  1. indexing and deindexing functions: ensure that entities are kept up to date in each index maintained by a component.
  2. adding, dropping, and getting components for an entity
  3. an entity-query function that is a general purpose way to lookup entities within a component table that takes advantage of any indices that may be present but falls back to a general purpose whole-table search.

~Colin Okay

indices and component metaclass

Wed, 7 Jul 2021 07:24:24 -0500

A component class is one that stores its instances in a table keyed by entities. Such classes also support indices, which allow the user to retrieve entities by properties of component instances.

Indices are fairly generic and can be customized according to

  1. index-row-key : how a key is made from a component row
  2. index-lookup-key : produce a key from arbitrary arguments
  3. index-lookup : given a key, actually look up entities
  4. remove-from-index : drop an entity from an index
  5. update-index : add / update an entity in an index

So indicies mainly vary on:

  1. whether they store an entity uniquely in a key location vs whether they store a list of values; and
  2. whether their key is determined by a few slot values or by some other means. In the case of multiton components, for example, the whole multiton instance object is the key.

~Colin Okay

Added grammar-base.lisp

Tue, 6 Jul 2021 06:27:37 -0500

I am taking an extremely OO approach to parsing. There are classes for GRAMMARs, for GRAMMAR-RULEs, and for GRAMMAR-OPERATORs used to construct certain rule productions.

The PARSE function is generic, and is specialized on grammars and rules, but with sane default implementations for the base grammar and base rule class. PARSE is also specialized on symbols and three predicates have been defined to distinguish different symbols: RULE-SYMBOL-P, OPERATOR-SYMBOL-P, and TOKEN-SYMBOL-P.

Each instance of a GRAMMAR-RULE has a name, which is a RULE-SYMBOL-P, and a production, which configures what the rule will parse.

GRAMMARS contain tables of rules and a single 'start-rule' which says where a parse begins with that grammar.

Other generics include the tokenizer function, which is specialized per grammar if you like, but which has a sane default implementation, and a rule lookup function, which was made to be generic to support how rules are collected and sorted for parsing - the idea is that several different productions can be associated with the same name, and so instances of different rule classes might also be associated with the same name, and hence different implementations of PARSE might apply.

~Colin Okay

Added slecs.lisp

Fri, 2 Jul 2021 17:07:41 -0500

SLECS is the reason I am refactoring this project. Prior to today's initial commit I had been working on this game for about 6 weeks.

About one to two weeks ago I began to implement the spellcasting langauge. Everything was going smoothly until it came time to add more components to the ECS. A code smell seemed to waft up whenever I needed to manually keep the language and the components in sync. Likewise, the choices I made about the representation of the spell language grammar ended up forcing me to manually update the entity description functions whenever the grammar changed.

This would not do. I did not want to have to manually synchronize different parts of the code base. But, I thought, I'll just plod along until the game is finished -- after all, I'll probably never touch the code again, or do so very rarely, once the game is "finished".

But then it dawned on me: hey! this is making it hard to play with the spellcasting langauge, but, the spellcasting language IS the game! Hence, I am refactoring to keep a single "source of truth" where the knowledge necessarily shared between components, spellcasting langauge, and the entity descriptions are automatically kept in sync.

This is to be the Spellcasting Language Entity-Component/Concept System. SLECS.

~Colin Okay