Designing a Random Table Generator

Ah, gotcha.

That's actually pretty much exactly what was in my head, but I used the words Generator and Table rather than Block and Table.

A Generator/Block I wonder if - for advanced users - HTML might be an option for formatting. Otherwise, it'd be the VBulletin post WYSIWYG editor.

It's important to note that this is a forum extension in nature. It will use the member database and existing vbulletin assets like post editors and so on. It won't work independently of vBulletin.

Scripts: the dice notation is a no-brainer. I had that in mind anyway. Logic operators I'm not quite grasping yet (ie I obviously know what they are, and intuitively feel the need to be included, but I'm not clear on how or what for, exactly, in this context).

I still don't follow the infinite loop checking. I don't get how the system actually does that.
 

log in or register to remove this ad

Ah, gotcha.

That's actually pretty much exactly what was in my head, but I used the words Generator and Table rather than Block and Table.

A Generator/Block I wonder if - for advanced users - HTML might be an option for formatting. Otherwise, it'd be the VBulletin post WYSIWYG editor.

It's important to note that this is a forum extension in nature. It will use the member database and existing vbulletin assets like post editors and so on. It won't work independently of vBulletin.

Scripts: the dice notation is a no-brainer. I had that in mind anyway. Logic operators I'm not quite grasping yet (ie I obviously know what they are, and intuitively feel the need to be included, but I'm not clear on how or what for, exactly, in this context).

I still don't follow the infinite loop checking. I don't get how the system actually does that.

I probably wouldn't use the word Generator, but that's because I have a specific meaning for Block.

Both a Table and a Block produce a result. Tables by default generate a short fragment of text (due to the way 1 line defines 1 result in the table definition, there's just no room.

A Block is a big pile of text, rich text, html, vBulletin formatted text, makes no difference. Think of putting a whole statblock in there or Monster Manual entry, including image tag for the picture. You can't fit that in a table, but you can make table entry 2 for orc point to [OrcStatBlock] which would cause the Parsing Engine to getch the block and return that as the result for the table.

As for working with vBulletin, I don't have experience coding for it. But generically speaking, we define the Parsing Engine (or Generator as it is do-er of things) to recieve some text (from a wiki, vbulletin post, new web form, whatever), and it parses for those [] and {} tags. It then does whatever we mean for those to do.

The parsing action is a recursive behavior. Meaning that it reads a string "Results from the dragon table = [dragon]" and it finds the dragon table, rolls on it and then parses THAT result which might refer to another table or block (so we can display the stat block). From the parsing engine's perspective, each result, is new content to parse and deal with.

This is how the nesting of tables works in any of these table systems.

Now for explaining the infinite loop-check concept (which is a rough idea I made up and if it was unique and clever I'd patent):

First off, we know EVERY table is going to have a unique name. and each result row in the table MIGHT refer to another table. Ideally, we want to avoid a circle or infinite loop.

When we're in the editor for a signel specific table, we should perform a number of validation checks. Make sure the table name is unique in the system. Fix any other tables that refer to this one if we renamed it (that's called Refactoring in programmer speak). We also want to see if our table is called anywhere else in our chain of tables.

Let's make up some fake tables. Pretend that MyTable is the one we're currently editing and thus will be checking for infinite loops from its perspective.

Code:
TABLE MyTable: 1d2
1: [Devils]
2: [Demons]
ENDTABLE

TABLE Devils:1d3
1: Satan
2: Santa
3: Beelzebub
ENDTABLE

Table Demons:1d4
1:Balor
2:Baatezu
3:Sucubus
4:[Ridethebus]
ENDTABLE

TABLE Ridethebus:1d3
1:Short
2:Long
3:[MyTable]
ENDTABLE

TABLE Unrealted:1d1
1:Dude, this is totally unrelated
ENDTABLE


Let's pretend all this is defined as 1 file per table (ex. MyTable.txt).

Before I save MyTable.txt, I can scan all the files for the previous name of the table (let's say I just renamed it from JanxTable to Mytable). I can find all instances of [JanxTable] and replace with [MyTable]. That will make sure tables don't break when somebody renames a table for whatever reason makes sense at the time. This is a HUGE problem in code, and very valuable to help with.

Using a similar trick, with the help of the Parsing Engine put into a debug mode, we can get a list of ALL the tables that chain from MyTable.

Here's what we find, aranged as a tree:

MyTable
..Devils
..Demons
....Ridethebus
......MyTable

If we find our table name "MyTable" undeneath the top of the tree, we have an infinite loop scenario.

the idea is that you do this detection IN the editor, before it saves, allowing you to nip the problem in the bud.

You can also use this trick to validate that a table refers to other valid tables, preventing dead-ends that won't resolve.

Though, as I explained about the Parsing Engine, it is recursive (self-referencing). Each tag instantiates a nested call to itself. The operating system handles the problem quite well if it goes too deep, so it will throw an exception (an error that is trappable so we can gracefully terminate the work, without completely dying). An infinite loop of this sort hits that limit much sooner than infinity and won't hog all your CPU or memory.
 

more hopefully clear techno-babble:

I may have missed a point, given this stuff gets long and hard to see the scope of my own thesis...


The Parsing Engine is a generic piece of code that should be fed input from any source. Be it Wiki, chat room post, new post from the forum, etc. It should not be cognizant of where the text came from, it just parses it and performs the actions the Lookup and Script tags tell it to.

As I explained in the long-winded example, with the way this thing will be implemented, you're not likely to suffer a problem from an infinite loop as 2 tables refer to each other. The recursive nature of the parsing will cause a stack overflow and the Try..Catch block wrapping the entire thing will clean up the mess.

Scripting is where you are likely to have security risks and true infinite loop problems. Once the scripting languages get into being able to define loops, that's where it will get nuts.

But don't worry about that yet. Depending on what the scripting language enables, will determine your risk.

When I say scripting, I mean any of the wierd stuff that people who like Programming will use, that normal people may not fully understand.

calling for a dice roll is probably the simplest. It requires a notation that is a directive to execute and produce a result. {1d6} is pretty easy, and the internet should be chock-full of code that can parse that and produce the result.

Things get more complicated when you need to lookup on a table 1d6 times.
{1d6 [Dragon]} would tell it to roll 1d6, and lookup the Dragon table that many times.

Just that simple fragment has complexity. The space between 1d6 and the [Dragon] tag is easily detected and we can tell that a quanity is required.

But what of the results. Say we roll a 4. How do you want me to display the results? Comma delimited? Or one per line. That's not so obvious. And remember, this markup may be in the middle of a sentence. One per line would wreck the sentence. Comma delimited would work best for simple results.

Example
Let's see {1d6 [Dragon]} right now.
produces:
Let's see Ancient Blue Dragon, Young Red Dragon, Old Yellow Dragon right now.

But what if the Dragon table produces stat blocks, not just name of dragons.

we might need a way to indicate that we want comma delimited, or a new line per result.

That's where functions might come in (just making this stuff up, so it may be rough):

{1d6 Commatize([Dragon])} would produce the results with comma seperators

{1d6 NewLine([Dragon])} would produce the results with a new line after each result.

Then, let's get into variables. Variables let us temporarily stash a value and then use it someplace else. Including to construct the name of another table.

Let's say we have a CR1Monster table and stat blocks for each (i'm onluy going to do one).

Code:
TABLE CR1Monster:1d4
1: Orc
2: Elf
3: Goblin
4: Flumph
ENDTABLE

BLOCK OrcStatblock
Orc
Likes eating elf meat.  Uses a sword.
AC: 8
Attack: +2, 1d8+2
HP: {1d8}
ENDBLOCK

With that table and stat blocks created, I can do cool magic in a wiki or forum post.

I can type up flavor text in a PBP game like:
GM: You enter the room and see a {$monstername=[CR1Monster]}. It is ready to attack. Here are it's stats:
{Lookup($monstername+"StatBlock")}


and after the Parsing engine manipulates it before it saves to the forum, it would then show up to everybody in my post as:
GM: You enter the room and see a Orc. It is ready to attack. Here are it's stats:
Orc
Likes eating elf meat. Uses a sword.
AC: 8
Attack: +2, 1d8+2
HP: 7


If you follow what's happened, I stashed the result from looking up a CR1Monster into a variable called $monstername. I then used that to programmatically declare the next thing to lookup which was "OrcStatBlock".

I can't do that without a variable.
 

Surely that's a user issue, not a code issue? You choose a table which gives you the results you need. If you want a stat block, you already know that when you call that table. If you just want a dragon name, you call a table which does just that. In theory, we'll eventually have a table for everything.

This'll be a phased release by necessity. I think the most useful thing we can do right now is define the initial phases with an eye to expansion. No point defining how complex logic statements work right now - our job is just to ensure the initial phase will be expandable to handle them later.

So what would our initial phase be? I'd suggest basic table call plus dice notation.
 

Surely that's a user issue, not a code issue? You choose a table which gives you the results you need. If you want a stat block, you already know that when you call that table. If you just want a dragon name, you call a table which does just that. In theory, we'll eventually have a table for everything.

Not in fully automated way. Like my example, I do not know the outcome of the table lookup before the stat block lookup. I'm typing up the whole thing so the entire results are a suprise to me.

If a guy's just plunking in one command at a time, sure. But that's not where this stuff's power is.

The point is, once you have a table for everything, you script everything so it's auto-assembles dungeon room contents, etc. With you none the wiser on what the output will be when you plug the entire block of text in (or pull up the wiki, etc).

Back to the Sequence of Development:
First, table/block tag parsing (trust me, blocks are just like tables with 1 result possible that's "bigger" than a single line of plain text.
and do the dice notation parsing (you'll need it for tables anyway)

After that, get dice notation to repeat lookups.

Then get variables and programatic lookups.

Then get if..else logic to use on those variables.
 

Ah, I see what you mean.

I think there have been a couple of miscommunications re. terminology. Block and Generator aren't interchangeable after all.

I hadn't really considered its usage in forum posts, though that's a good idea. Might be tricky as to when it's parsed - obviously not on post display (as posts usually are) because otherwise it would show a different result every time and to each reader. So that might be tricky, requiring an actual check and rewrite of every post at the time of submitting it.

I also wasn't planning on have tables directly accessible by anything other than other tables and top-level generators.

What I had in mind was this:

I decide to create my new generator, "Morrus' CR1 Pathfinder Room Generator". I reckon it'll be easy. It'll need to roll on a few tables, but I see that the tables BOBSROOMSIZE and ALICESROOMADJECTIVES already exist, as does TOMSCR1MONSTER.

So I make my generator. Unlike a table, a generator is a pretty, formatted thing. I design its format when I make it. A table, on the other hand, just contains data.

So my generator looks like this:

This [BOBSROOMSIZE] is [ALICESROOMADJECTIVES] and [ALICESROOMADJECTIVES].

In the [JIMSPOSITIONS] of the room is [TOMSCR1MONSTER]! It appears to be [ERICSACTIONS].

Which, when someone presses "Generate!" on my generator, spits out the result. It's formatted however I specified when designing the generator. In theory that could be a complex HTML design/format which displays a full character sheet.

So if I understand you right, a block is simply some extra data appended to a table?

I was imagining a very simple user interface. Creating a generator is filling in a form: name, description, full-formatted BB-code of HTML text entry box.

So the user sees in the main generators interface, maybe after some searching/browsing for "CR1 Pathfinder Room":

The following generators matched your criteria:

Morrus' CR1 Pathfinder Room Generator by Morrus
This is a simple generator which describes an underground room and populates it with a CR1 encounter.
[GENERATE!]

The Ultimate Generator of Awesome CR1 Rooms! by Henry
I designed this generator to describe a room in intricate detail, including furnishings, aromas, and wind factors.
[GENERATE!]

So, as you see, a Generator is a unique formatted thing which refers to tables, which are just data.

Your discussion of blocks complicates things, though. So I'm a tad lost. So a block is a section of info appended to a table which provides both extra data and formatting?

My main worry is that if I can't understands the concepts easily, then the users won't be able to either; and the primary aim has to be ease of use.
 

and that's why we're discussing things... :) I have a specific technical vision in my head because of my experience with table generation tools and my experience as a guy who develops software. Things may be fuzzy as you are experiencing the first draft of a brain dump of my concepts (aka, I didn't write it clear enough).


What you just described as a Generator, is the exact way a Block works. I don't want to use the word Generator, because it is an action word and should refer to the worker process, not the content.

Both a table and a block can have markup to induce lookups to other tables and blocks.

TABLE DungeonRoom:1d2
1: This is a {1d6*5} foot square room that has [DungeonRoomContents]
2: The room is {1d8*5} x {1d8*5} feet and has [DungeonRoomContents]
ENDTABLE

BLOCK DungeonRoomContents
The walls are made of [DungeonWalls]. The floor is of [DungeonFloor]. There are {1d6 [DungeonJunk]}. There is also a [CR1Encounter] in the room.
ENDBLOCK

A block is where you rich text goes.As you see from the example, the table refers to some dice rolls and sends the parser to the DungeonRoomContents block for the rest. Generally, in all table definition systems, you are limited to 1 line of text, rather than a huge blob of content. The Blocks aren't cluttered with needing to define table entries, so they have all the room in the world.

I said a Block is like a 1 row table, because functionally, that's how the parser jumps to a block (by treating it the same as a table to find it) and then seeing that there is no random content, it just parses and displays the entire text.

to recap, what I call a block, you are calling a generator.

The user could certainly just pick from a list of tables or blocks, but that's not very efficient. Power users will write up an entire dungeon or adventure module, embedding the macros in place and then plug it into the parser and generate all the content right there. Instant randomly created adventure.

they could certainly make their content a Block and then run that block, but let's assume that both blocks and tables are meant to be mostly static once defined. End users custom entries are more likely to be single-uses. You could differentiate that blocks and generators are technically the same thing, but blocks are intended for generic use (here's an Orc encounter) and Generators are "here's my 1st level adventure that randomly generates content each time.

I've got more to say, as usual, but i better submit this before some web-accident loses my post
 

I think that raises a couple of points:

1) I think you're envisaging a different end use to me. I'm not imagining single use custom entries that the user writes as he goes, but rather discreet individual generators. So for most people (the ones who aren't writing stuff) they're just gonna click "Go" on "here's my 1st level adventure that randomly generates content each time". I don't - at this point - envisage them using the syntax in messageboard posts.

2) I chose Generator because I want the system to use language that gamers understand. A gamer knows what a random generator is, and is likely to have used them many times before; but explaining what a "block" is creates an extra step for the casual user which might provide a barrier. Does that make sense?
 

more stuff:

as I determined, Block=Generator. It can be plain text, html, or vbulletin markup. It can have markup to roll dice and call other tables or blocks.

the Parsing Engine is a chunk of code that takes an input of text that might contain markup and returns that text with the markup replaced with actual lookup results.

We can wire up a new Generate button on this here post editing page, or hook it up to the submit button to run ONCE when I post it, forever remaining static after that.

We have many ways the Parser gets hooked up. That's like the least of our worries as we have many options on where to use it.

Initially, I envisioned 3 basic screens, just to get the critter running:
  1. Main I/O screen
  2. List of Assets screen
  3. Edit Asset screen

1) Main I/O screen.
This is where folks can play with the parser to generate their own results, etc. Folks just type in stuff with tags and then hit the Generate button and it'll post back and return a pretty result (because Blocks can have html or whatever). In theory, what folks type in COULD be saved as a new Block/Generator.

2) List of Assets
We need a word to describe all the tables, blocks and generators (because they are all the same thing from a high level view, tags to be handled. I'll call them Asssets. This screen shows a GridView that lets the user scroll through all of them, filter, search, and choose to execute or edit one.
Here's a link to a GridView control I use, so we're all clear on what that means: Filter Row - DevExpress AJAX Data Grid for ASP.NET

there are other products that do the same thing for PHP, don't get bogged down in who makes it.

3) Edit Asset
This is where the easy-bake editor for a single Asset lives. An Asset as the following attributes: Name, Type, Description, Owner, LastUpdated, and Body. Body is where the magic syntax for tables go, or the big pile of formated text for a Block or Generator go.

Here's the trick, by making the user specificy Type, we do NOT have to expose the user to cryptic syntax like you see at TableSmith's examples. That might be how it is stored, but when you say the Type=Table, the UI can show a nice, easy to use table that the user edits individual fields on, rather than hand-typing the exact syntax we need.

The same for a Block/Generator. We can show a nice WYSIWYG box with buttons to change font, etc. We can also include a "picker" tool to help wade through the pile of Assets to add a tag into the text where the cursor is.

With these 3 screens, a user can start making Assets and test them. They can use it directly from the List screen, or use the Main screen to make something complex.
 

That's not far off what I was imagining. We're more on the same page than not, I think. We're just getting bogged down in terminology. Well, I'm getting bogged down in terminology!

The only major difference is that I didn't see the main screen as an input box to generate stuff, but rather a list of pre-created generators which can be execusted with a button press with an easy link to start creating your own.

I would worry that a box saying "type some syntax in here to use this system" is an offputting thing for the first-time casual user. But "Press this button and get a town description" is inviting.

Regarding things like the GridView whatchamacallit, vBulletin already has a crapload of listing and browsing functionality built in; we can just hook in to that stuff with the bonus that the interface remains familiar to the user.

I like the idea of the field-edited table for casual users. Much less intimidating tha making them type syntax.
 
Last edited:

Remove ads

Top