Skip to content

Instantly share code, notes, and snippets.

@selfsame
Last active February 5, 2018 11:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save selfsame/c895dc90429c035ea611932c80f30dc2 to your computer and use it in GitHub Desktop.
Save selfsame/c895dc90429c035ea611932c80f30dc2 to your computer and use it in GitHub Desktop.

ideas for tildemush's WITCH scripting language

Let's imagine an Alice and Wonderland themed little cake.

A MUD has a complex system of commands that interact with objects. A player typing eat the little cake would be parsed into the eat command, find a little cake object in the current scope, check for an eatable property, and enact some result, either a default player.name+"eats the"+subject.name or some custom interaction provided by the object.

A MUSH is a "shared hallucination", we avoid hard coded rules and interactions and let the users roleplay as they see fit. A player could /do eats the little cake, /do shrinks down to the size of a mouse!, and then go about pretending to be a tiny person until they got bored.

A WITCH script describes an object which can then be created in the MUD by users. How can these objects facilitate user defined interaction? The current spec has a hears directive giving access to text in the current room. Much like an IRC bot, the objects could parse this and react:

object "little cake" by "selfsame {
  describe { 'A small yellow cake with a label that reads "eat me"' }
  hears "(\w+) eats .*(little cake|cake)" {
    print(heard[0]+" shrinks down to the size of a mouse!")
  }
}

These reactions rely on the user writing good parsers, we could imagine mistakes like:

selfsame watches "Boy eats world"
selfsame watches "Boy shrinks down to the size of a mouse!

Another gotcha would be multiple little cakes in the same room, which would all think they were being eaten.

formalizing verbs & entities

What if tildemush provided the parsing? selfsame eats the cake is object verb subject, and if those two nouns could be resolved in the room scope, it could dispatch an action directive.

action "eat" {
  print("[subject] shrinks down to the size of a mouse!")
}

tildemush could use spacy or nltk to tokenize input, and the verb's lemma to avoid redundancy.

There's more complex patterns, like [selfsame] (feeds) the [little cake] to the [alligator] or [selfsame] (buys) the [jacket] from the [merchant] with a [credit card], which could be avoided or handled down the road.

@vilmibm
Copy link

vilmibm commented Dec 14, 2017

i love the parsing idea!

I've been thinking from the perspective of objects adding possible actions for users who possess them in inventories; in other words, something like:

$ /get cake
$ /eat cake
vilmibm shrinks down to the size of a mouse!
**Cake is no longer in your inventory
$ /eat cake
**Hm, that command doesn't seem to do anything...

with code looking something like

object "cake" by "vilmibm" {
  alias {
    little cake
    yellow cake
  }
  describe {
     A tasty looking cake. It's yellow, suggesting a delicious vanilla flavor. A small tag attached to the cake says "eat me."
  }
  action "/eat" {
    print("$(player.name) shrinks down to the size of a mouse!")
    self.drop()
  }
}

I like this idea because of its simplicity; I'm slightly nervous about noun/verb detection but agree that with NLTK it shouldn't be too onerous. It seems much more flexible and means objects not in an inventory[1] can still provide actions with the same interface as those in an inventory.

[1]: my code sample assumes that the action is only provided when an object is held. in thinking about non-held objects providing actions, i think some kind of guard would be helpful; something like

action "eat" while carried {
   ...
}

@vilmibm
Copy link

vilmibm commented Dec 14, 2017

(i guess i wasn't clear that i love your idea and want to pursue it!)

@selfsame
Copy link
Author

@vilmibm it was clear and I'll be able to help implement the parsing when the time comes :)

Some thoughts on inventory:

A "scope" concept could replace while carried guards, where a player's scope would be everything in the room and their inventory. /eat cake would check the available scope and dispatch if a cake was found. This could augment the guards, in case you wanted to declare actions for carried items only.

Or perhaps something like:

action "eat" {
  if this in subject.inventory:
    ...
}

I'm a bit unclear on how WITCH directives compare to functions with arguments. The above imagines that directives have provided variables (this, subject). Also assuming that objects can perform actions, like an alligator that eats a random thing in the room)

@vilmibm
Copy link

vilmibm commented Dec 18, 2017

@selfsame so I added a new, bigger example. instead of going right to parsing verbs and nouns, i propose getting off the ground with a simplistic system:

/do <verb> <object> <rest>

where <verb> maps to an action listener defined by an object; <object> is the name of an object in the same room; and <rest> is whatever follows (and might be empty).

I further propose actions take arguments like so:

action "feed" (thing) {}

thing maps first to everything following the given verb. If multiple arguments are specified, they are matched via space delimiting.

For example:

# given the above example action...
/do feed horse delicious grain
# thing is the string "delicious grain"

but given this action:

action "feed" (thing0, thing1) {}
# thing0 is "delicious" and thing1 is "grain". To lump these together, use quotes:
/do feed horse "delicious grain"

Is the string pattern matching too confusing, I wonder?

@selfsame
Copy link
Author

@vilmibm the horse example is great, I think it's a solid goal for how parsing should work that can be extended down the road.

Some notes on resolving actions:

  • the subject is always the object that issued the /do command
  • the verb is always the first word /do eat
  • the object needs to be resolved, initially we can assume it's the (optional) third word of the action /do eat grain and not /do eat the grain.
    • This word needs to be compared with objects in scope, if a match is found the action is called on it.
    • we don't need an object binding, since this is always the object of the action.
    • may be some issues here with dispatching "zero-object" actions (/do smile evily).
  • I'd say a single argument binding for the <rest> string is sufficient.

With the current sketches, this sort of thing is possible:

# alligator
action "feed" (thing){
  room.say("The {this} clamps it's jaws onto the {thing}!")
  this.do("eat {thing}")
}

# spagetti
action "eat"{
  room.say("The {this} dissapears!")
}

/do feed alligator spagetti
*** The alligator clamps it's jaws onto the spagetti!
*** The spagetti dissapears!

@jmdejong
Copy link

jmdejong commented Feb 5, 2018

What's going on now is both trying to design a language, and trying to define the interface it implements.
Wouldn't it be more useful to design the interface first in python (or hy) to have something running, and then define the witch language to make things easier that should be easier?

Many townies already know python or can adapt a python example to what they need, and I think it's a lot easier to define our own language once we can test what non-programmers find difficult and what's easy to integrate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment