next_inactive up previous


LRFC (Lesser RFC) countermoves.sourceforge.net-001

Abstract:

Topic: Teeny Weeny Microgame Engine (aka Teeny)

Version: Draft 26 Feb 2005

URL: http://countermoves.sourceforge.net/teeny/

Distribution policy: Public Domain

Authors: stephan@s11n.net, with support of the members of countermoves-general@lists.sourceforge.net


Contents

0.1 Overview

The "teeny weeny microgame engine" (aka Teeny) specifies an extremely basic model for basic boardgames, specifically with emphasis on grid-based microgames. The goal is essentially to provide a common reference docuement which can be used to implement Teeny engines (or ``interpreters'') in a number of programming languages. This document concerns itself mainly with definitions of a set of abstractions, and only treats details of user interfaces or implementation languages when helpful in strengthening a definition or providing an example. It is the specific intent of Teeny to be implementable in even very basic environments, such as with ``scripting'' languages and in restricted environments such as embedded systems (where screen space, if nothing else, is at a premium).

This LRFC does not provide any API (Application Programming Interface) definitions, but primarily lays out the conventions which implementations should account for.

Teeny is designed SPECIFICALLY for small-scale games, with no more than 52 "units" (called Actors) on a game board at a time, and game boards (often called ``maps'' or ``boards'') of sizes no more than (approximately) 20x20. The main reason for this is evolutionary: Teeny was originally inspired by the ground combat system of "Sid Meier's Pirates!" (2004 release), and implemented in a text-based client in which only small games were practical. Additionally, these limitations are kept to ensure that Teeny's data structures can be expressed in ways which are easilly parseable by commonly-available tools and languages, such as awk, sed, etc. It is conceivable that an entire Teeny implementation could be done in awk.

This document is written in the tradition of RFCs (Request For Comments), which have long been used to guide the paths of many international standards. As far as i am aware there is no governing body for RFCs, and that would imply that anyone may release their own. However, the ''Lesser'' prefix was chosen to distinguish this document from a ''standard'' RFC. This LRFC does have some commonality with RFCs, though it is notably less formal in nature.

0.2 Reference Implementation

While i hesitate to call it a "reference implementation": it is log in to my laptop as 'stephan', and 'cd ~/cvs/teeny'. It's all there. Seriously, though: all of the primary "requirements" laid out in this LRFC are implemented already in C++ using a combination of shell-style and curses interfaces, plus a partial Perl-based implementation as proof of concept. Thus this LRFC is significantly influenced by "practical experience", and is not just an "off the cuff" list of things i think such an interface *should* provide.

0.3 Definitions

This section defines words for which a relatively strict definition is required in order to adequately interpret this LRFC.

Board (also map): the "world" in which a "game session" takes place. A Board is defined essentially as a rectangular grid, each cell of which contains some type of Square (see below) and possibly an Actor (see below) or other client-specific data.

Square: the generic term of "one unit of boardgame space". A "square" might actually be a hexagon, depending on the game, or even something more abstract like a space of real-estate, a country or sector of a galaxy.

Coordinate point: an X/Y coordinate point which represents a point on the game board. Exact interpretation is game/client-specific. In practice these pairs will often be used by application programmers to relay the data to the user interface (e.g., X/Y pixel/cursor coordinates). In other clients the pair might be an indentifier for a more abstract Square, like the country of Germany. The term Square can often be used interchangeably with Coordinate point, as they effectively mean the same thing in many contexts. We could arguably include a Z dimension, but this would unduly complicate the more common case implementation of 2-dimensional playing fields.

Terrain: internal representation of one Square's terrain. In Teeny this is simply a single ASCII char. By convention, TE's are restricted to non-alphanumeric characters.

Actors: actors are what are normally called "units" in microgames and wargames. An actor is an abstract representation of an arbitrary "game piece" in a battle. Whether an actor represents one individual or a group of soldiers, a battleship, a command post, etc. is game-dependent. As with Terrain, an Actor's most basic representation is a single char. Actors are represented by alphabetic characters. To simplify the use of character-based Teeny UIs, it is recommended that one player be associated with lower-case letters, and one with upper-case letters.

Avatar: the physical representation of an Actor or Terrain on the screen. e.g., in a curses-based interface, "-" might be the avatar for a Square of "clear terrain", and "A" might be the avatar of a squad of infantry.

API: Application Programming Interface. Programmers use the API of software to control it's operation (and thus the programmer is normally limited by the expressiveness of the API).

COMPLIANT Teeny implementation: an implementation of Teeny which contains the basic functionality laid out in this document. That is, it at least implements all MUST functionality (as defined below).

MUST and REQUIRED: if we say an implementor MUST do [something] then a Teeny implementation is only "compliant" if all MUST items are implemented.

MAY and OPTIONAL: if we say a client MAY do [something] then a Teeny implementation has no requirement to implement the feature. MAY features are listed here primarily as ideas for implementors, or for potential MUST features in the LRFC for an extended, Not-so-Teeny specification.

SHOULD or RECOMMENDED: in-between MAY and MUST, SHOULD means the described feature is significantly important as to be expected in any implementation, but does not justify a MUST. An example is load/save support: this can be technically difficult to achieve, but is essentially required in order to provide a usable application. SHOULD might also be read as "MUST (unless it's unfeasible or unduly difficult to implement)".

UNSPECIFIED: behaviour which is "unspecified" is left to interpretation by implementors. An example is, "if an Actor is moved to an off-board Coordinate, the result is unspecified." UNSPECIFIED is essentially a synonym for ''client-dependent'' or ''implementation-dependent.''

0.3.1 Avatar value ranges

The conventions detailed below are MUSTs for the framework.

Teeny's defines Avatars as a single byte of data. In the majority of programming languages this is the 'char' type (or similar). Implementations may use larger types, but may not use smaller types, and any serialization of Avatars which are outside the range of char is non-portable.

Note that whether a 'char' is ''signed'' or ''unsigned'' is unimportant for our definition. What is strictly required is that one byte of storage be available to represent an Avatar, be it an Actor or Terrain (or other) Avatar.

As mentioned above, when it comes to "allowable ranges" of characters, Teeny's design limits itself to the following, fairly arbitrary, conventions:

Implementors are not required to do error checking to confirm these bounds, but may do so if they like. Implementors are free to enforce any subset of these conventions as loosely or strictly as they see fit. When clients complain about a crash when the user enters '*' as an Actor, tell them to read this LRFC ;).

Justifications:

The above restrictions are primarily in place to simplify implementations by guaranteeing implementors that they only have to accept these ranges of characters, and need not concern themselves with unicode, multibyte, "special characters", etc. Parser and data structure implementors can also take advantage of these distinctions if they like. e.g., a curses-based interface might map keys [a-zA-Z] to jump to the associated Actor, without having to go through some lookup table to find the associated Actor (the letter/Avatar is the unique ID of the Actor, which "should" give us a key which we can use to address the Actor in the game board, for example. e.g., myboard.selectActor('A')).

0.4 Boards

Game boards, also called ''boards'', ''maps'' and ''playing field/area'' are made up of several components:

  1. A geometry: width and height. The exact interpretation of width and height are of course client-specific. For our purposes we will assume that X/Y coordinates map directly to Board Squares (either 1:1 or via some deterministic transformation).
  2. A Terrain Avatar associated with each Square. Behaviour is UNSPECIFIED when a board is used with ''missing squares'', so to say.
  3. Any number of Actors, within the valid Avatar range for Actors, each associated with a Square (note: off-board Actors are not considered by this LRFC, but implementors are free to do so). No more than one Actor may be in any given Square, except as an optional extension supported by an implementation. See below for more on this.
That short list of items encapsulates all of the core data requirements for a Teeny implementation. The above conventions are MUSTs for implementors, but the details of the internal data are 100% up to the the implementor's descretion.

Notes regarding "stacking" units:

The engine may assume that only one Actor may be in any given Square. This is solely to simplify implementations, not to make game designers' lives easier. This requirement is primarily intended to make life easier for text-mode clients. In graphical UIs this limitation really comes to light, where the UI can more easily accomodate "stacked" Actors. In text-based implementations the limitation can be worked around by using "proxy" Actors. For example, if we end up stacking A and B, we can remove A and B and replace them with C, which we somehow remember is actually a collection of Actors A and B. Implementors MAY implement Actors as "stackable" if they like, but this feature would be considered an extension, not core functionality.

0.5 Teeny Command Language Specification

NOTE: this entire section, except where explicitely noted, is a MUST for implementations. The command language described here essentially defines all of the required operations which must be performable on the core Teeny data structures.

The internals of a Teeny implementation should be controllable via a standardized "language" for sending string-based commands. This is to simplify the interaction between shell-based, web-based, and other exotic clients and servers.

This LRFC specifies that "command language", rather than specifying the implementation-specific APIs. This allows implementors to use whatever API conventions they feel are appropriate. The command language is intended to be line-oriented and stateless in and of itself (e.g., a command has no inherent way of knowing what the command before it did), to allow the language to be used in stateless environments like over HTTP (e.g., by passing commands to a web server using URL parameters).

Commands are formatted exactly like traditional shell commands, but without complex constructs like loops. This LRFC does not make the assumption that any given operation is atomic (at any level), due to game-specific rules which might force violations of this (e.g., in Game ABC, moving a piece might, require that other pieces move automatically as a result. Backgammon has such a case when landing on single enemy pieces.). Implementors are free to extend or modify the syntax, but SHOULD at least support the basic grammar described here (even if only via translation via UI events, or whatever).

The exact input mechanism for commands in unspecified: they may come in from the keyboard, data files, a network connection, or /dev/null for all we care. They may come in via a user interface, which translate UI gestures into command strings (this is how much of the curses implementation works).

A note on error handling: this language does not at all accomodate error handling. The reasons are basically a) it's intended to be so braindead that the errors could only come from the humans and b) to simplify interaction between Teeny implementations coded in different programming languages. e.g., a command sent over the web via a URL can end up in any back-end language, and specifying the exact error responses here would unduly complicate implementations. Implementors are free to implement any error reporting scheme they like for their implementations of the Teeny command set. The C++ and Perl reference implementations, for example, internally use the time-honored tradition of returning zero on success and non-zero on error, and simply send an error message to the console when they detect a problem.

The format used for commands is conventionally:

context command [arguments...]
"Global" commands, like 'load' and 'save' have no specific context prefix, though context specific load/save features are allowed as extensions. Arguments must be representable as strings (i.e., no pointer references), but the interpretation of the strings is client-dependent. (e.g., whether your client treats arg3 as an int, a long, a string, or as a reference to an Actor, is up to the implementation).

Symbols used in the command descriptions:

The list of core commands, including arguments and descriptions, follows, broken down by general context of the command.

0.5.1 Board-related commands

The following commands relate directly to the current game board.

board set X Y (TERRAIN|STRING) [BOOL:DRAW_VERTICAL]
Sets the Terrain Avatar at the given Square. If given a STRING then it should all (length of STRING) Terrain Avatars, starting at the given coordinate and working to the right. As usual, implementations may do as little or as much range-checking as they like, both in regards to validity of the Terrain Avatar and the length of the string vis-a-vis the size of the Board.

The DRAW_VERTICAL: if set to 1 (default is 0) then it should ''draw'' vertically (downwards, starting from the given point). This implementation may not be feasible in some environments (e.g., doing it in shell code would be challenging). Note that by supporting a STRING variant, the single-TERRAIN-arg variant comes for free, as it can be treated as a STRING with a length of one.

board create WIDTH HEIGHT [TERRAIN]
Recreates the game board using the given width, height and terrain (optional: a reasonable default is assumed). Whether the board retains older content or not is unspecified (because it is normally programmatically simpler to erase the old board and start over). If TERRAIN is not specified then an implementation-specified default is used.

board clear [actors] [terrains]
When used with no optional parameters, clears the board to "a default state" (client-dependent). When optional parameters are given, only those elements of the board are cleared (e.g., Actors or Terrain). The geometry of the Board will not change by calling this command.

0.5.2 Actor-related commands

The following commands relate directly to Actors.

actor move X Y ACTOR
Moves the given ACTOR to the given Square. Move is responsible not only for moving the given ACTOR, but also for adding new entries. The logic is this: if ACTOR is not on the board, we add it at the given coordinate. If an Actor is moved to an of-board Coordinate, the result is unspecified.

actor remove ACTOR
Removes the actor from the Teeny engine. Whether the Actor is "completely removed from play" is unspecified.

Implementations may want to create abbreviated forms for these commands, such as ''mv'' and ''rm''.

0.5.3 Game-related commands

These commands related to the gobal state of the game. That implies the content of the current Board, including all Actors and Terrain. Whether or not that covers any other data is implementation-specific.

load STRING
This command is RECOMMENDED command. SHOULD load the state of the Teeny engine from the given identifier. The interpretation of the identifier is of course client-specific (filename, URL, db record id, etc). Implementors are free to leave 'load' unimplemented, but must not provide "unexpected behaviour" when a load command is received. Implementors may accept other arguments in their load handlers, such as multiple filenames or authentication information (which is probably most flexibly handled using a URL form for filenames). For LRFC purposes, the FIRST option passed to load is the one required for the load interface: optional arguments, if any, must come after the required first argument.

save STRING
This command is RECOMMENDED, and should perform the converse of loading. The SHOULD clause is the same as for load: implementors are not required, due to the technical difficulties and variations, to provide save support. As with loading, implementors are free to support multiple command arguments, provided that they appear after the required STRING parameter.

0.5.4 General command language conventions and notes

Unless noted otherwise, all "logically erroneous" cases provide unspecified behaviour. For example: assume we have a Gameboard of 10x10 Squares. What is the result of:

actor move -1 27 A
???

The answer is: it's client-specific. Some might use out-of-bounds coordinates to represent "off-board" units, hidden units, or similar (e.g., hidden units could be placed at the negative values of their X/Y coordinate). An interface based on libncurses++ is likely to throw an exception on OOB, due to that library's heavy use of exceptions for the most trivial of error cases.

0.5.5 Extended/Extension commands

Implementors are free to add their own commands, and are encouraged to expand the above conventions. Some examples of hypothetical commands/extensions:

move_relative [+-]X [+-]Y ACTOR
For example, the curses client implements a relative-move using the 'move [-+]X [-+Y] syntax', as that can be translated into an absolute command and forward to the main handler.

move_multiple RELATIVE_X RELATIVE_Y ACTOR_1 ... ACTOR_N

board save (actors|terrain) filename

board redraw (for a curses-based client!)
Clients are also encouraged to add their own completely new contexts. Some examples:

network msg broadcast "You're up, player 2!"

network msg player TARGET_PLAYER_ID "message string"

create_soldier X Y (this might be a handler which auto-allocates appropriate Avatar)

system ls $HOME
It is hoped that the simple string-based nature of the language will provide the following benefits to implementors:

0.6 A common file format recommendation

While implementors will invariably want to use their own file formats, the following is suggested as a reference point for the most basic of data which a Teeny gameboard needs to know about.

Without explaining the history or the why's, here's what the "teeny" game board file format like:

# Comment line - ignored. Start demo.teeny

# Format: one record per line.

# Most records are in this format:

# KEYWORD=DATA

# But future additions may use different syntaxes.

###################################################

# row keyword: defines one row of a game board.

row=...............

row=.-------.

row=.-%----%-.

row=.-------.

row=.-------.

row=.-------.

row=.-------.

row=.-------.

row=.---%---.

row=.-------.

row=...............

# actor keyword: maps an ACTOR AVATAR to an X Y point

actor=10 3 a

actor=11 3 b

actor=11 4 c

actor=10 12 A

actor=11 12 B

actor=12 12 C

#

# End of "required" support.

#

########################################################################

# Reserved/future keywords:

# file_format_version=VERSION

# teeny_*=*

# command=command string

# polyhedron=COUNTdFACES (e.g. 4d6, 2d20, etc) Defines the die/dice

# used for the game.

########################################################################

#

# STRICTLY OPTIONAL options:

#

######

## ANSI-color-capable clients:

######

#

# terrain_color_ansi keyword: maps terrain avatars to ANSI color

# attributes.

#

# Format:

#

# terrain_color_ansi=TERRAIN ESC_SEQUENCE

# actor_color_ansi=ACTOR ESC_SEQUENCE

#

# Where ESC_SEQUENCE is an ANSI color escape string WITHOUT the

# leading escape codes. e.g. 01;33 or 00;32. The escape codes

# are left out of the serialized data because they can cause

# grief for some file parses (like XML).

#

######

## Curses-capable clients:

######

#

# terrain_color_curses keyword: maps ncurses attributes to terrain

# avatars.

#

# Format:

#

# terrain_color_curses=TERRAIN INT

# actor_color_curses=ACTOR INT

#

# where INT is an int suitable for use with the ncurses attrset()

# function (and friends).

#

######

## Pixmap-capable clients

######

#

# terrain_pixmap keyword: maps terrain avatars to graphics.

# Format:

#

# terrain_pixmap=TERRAIN KEY

# actor_pixmap=ACTOR KEY

#

# where KEY is some lookup key which the client can use. This may

# be a filename, like mytrooper.png, a base name, like mytrooper,

# or any other key which clients can negotiate. i recommend using

# a base filename or relative filename, such as foo.png or

# GameName/units/foo.png. Implementations can normally easily provide

# some configurable feature with which to resolve the keys. e.g.,

# a function like find_pixmap( "foo.png" ), which returns

# "/usr/local/lib/teeny/foo.png".

#

########################################################################

# End demo.teeny

########################################################################

Things to note:

This format is trivial to parse in a number of programming languages. One perl-based parser exists which converts that format to a much more complicated libs11n-formated file, for use in C++ reference implementation code. Thus i may hand-edit a board in emacs, filter it via this tool, and then open it in the curses UI via the "native" file format (which contains other info).

0.7 Things this LRFC "could arguably" cover but does not

Colors/icons for Actors and Terrain. These are simply too client-specific, and don't mean anything at all in some interfaces. Hell, they don't always mean the same thing to all observers, either. It may be interesting to investigate a basic palette definition which abstractly represents such UI-specific information without being UI-specific. Nothing immediately comes to mind, though.

Commands for fetching, as opposed to modifying, game state. Programmatically speaking, returning the result of any fetch command is inherently implementation specific, and we will not even begin to touch on the multitudinous technical difficulties involved with implementing RPC [Remote Procedure Calls] and cross-language calls. Implementors should define their own APIs intended for use by their clients in doing things like updating the client UI to accomodate changes to game state. e.g., after an Avatar is moved across the board, the client UI needs to be told about it so that it's view of the board can be updated.

Authentication and Cheat-detection: these must be considered to be client-level issues, as solving these generically at the Teeny library level would be technically extremely difficult, if not impossible, to achieve. Specific Teeny implementations are welcome to provide any sort of authentication/cheating-related measures they feel are necessary. They may, for example, require that their input files come from some authenticated source, like a digitally signed and encrypted file containing Teeny data.

0.8 Misc Notes...

We sometimes see the term "text-based clients". What does that mean? A client application without a "real" user interface. For example, Teeny may be implemented as a Perl script which simply reads commands from stdin, modifying an internal represenation of the map. As output it could show a textual rendering of the game board, like:

--b-

-####-

-####-

-####-

A---
or even dump out a file using the format described above.

Now let's do a few commands:

actor move 3 0 A

actor move 4 0 b

actor move 5 0 Z
(remembering that 'move' is also 'add if not already there'.)

Now the map looks like:

-AbZ-

-####-

-####-

-####-

---
Command:

board terrain 2 2 %%
Results in:

-AbZ-

-####-

-#%%#-

-####-

---

0.9 Random thoughts specifically aimed at implementors

Several possibilities for implementing a simple Board interface quickly come to mind, appropriate for a variety of languages:

The encoding approach, while efficient, is not extendable. The layered approach, using one map or array/vector/string per avatar layer, is sufficiently simple for many different languages, and is theoretically at least marginally extendable. For example, support for "hidden troops" on the battlefield can be partially implemented by adding a HiddenActors layer and moving hidden Actors to that layer, and not rendering it in the UI (of course, users with a debugger or similar tool can easily read the data).

Multiple players: i highly recommend reserving one "case" of the alphabet for one player, and one for the other. e.g., a-z for player one and A-Z for player two.

Coloring the Actor avatars: i have specifically avoided this issue so far, because i don't feel that coloring at the unit level is necessary at Teeny's scope. Coloring avatars based on their owning Player, their damage state, etc, would be desireable, but that type of modeling is necessarily game-specific, which means it is far outside of Teeny's scope. That said, i did originally implement a colorized version by mapping ANSI escape sequences to characters, and the current ncurses version has some hard-coded mappings. A similar approach can be used by clients to map arbitrary attributes to Actors or Terrain, including color information.

Shell-style back-ends: one of the advantages of the command language approach to modifying game state is that it is relatively straightforward to create a mapping between tokens and functions pointers/names/handles/references in most languages (even in Unix shells). Such mappings make it easy for implementors to add new functions without changing their core command parsing code. The C++ Teeny reference implementation uses eshell (http://s11n.net/eshell/) to provide such a framework (disclaimer: the author of this LRFC wrote that software).

About this document ...

LRFC (Lesser RFC) countermoves.sourceforge.net-001

This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir23331sE12Pd/lyx_tmpbuf0/teeny-lrfc.tex

The translation was initiated by stephan on 2005-02-27


next_inactive up previous
stephan 2005-02-27