Specification / technical details


Basic idea

After various experiments regarding traditional text adventure engines, I've settled on a rather simple structure which requires as little code development and programming effort as possible:
↑ top Input check

To prevent any possibilities of hacking, we must not store solutions on the page (in Javascript code) itself. Alhough technically it would be feasible by means of obfuscation, this weren't a real hurdle for clever people, so let's avoid client-side trickery altogether.
And since server-side scripting isn't available, we cannot store solutions on the server (e.g. in PHP code) either.

So, instead of verifying, a different approach must be taken:
↑ top Filenames

In the absence of folders on Buttcharge we're prefixing filenames according to the project they belong to: here we'll be using "ta_" (for 'text adventure').
Then comes a gibberish random looking sequence of chars - derived from the hash - to prevent players from guessing and cheating.
Finally we append ".html" because our chapters are ordinary HTML docs.
Example: ta_280df58l2nhoiu8aduw.html

As of layout, there might also be a central style sheet: ta_layout.css

Images and other media files obey the same pattern, e.g. ta_Rachel.jpg


↑ top Hash calculator

We don't actually need to consume the entire hash to produce unpredictable filenames. In fact, at the time of writing, only the leading 24 digits (12 bytes) are used. After encoding them to a base36 number (which may contain numerals 0-9 and letters a-z), we get a sufficiently long string of approximately 20 characters.
Here you can try it yourself: type in some chars, hit ENTER and see what the calculated filename looks like...

Input:
(Hash: ?)
Filename (base36 format): ta_?.html

Armed with aforementioned algorithm, we now've got a dead simple navigation method which doesn't require additional information like look-up tables, nor special code to explicitly verify stuff, nor any kind of game engine whatsoever.


↑ top Lack of interactivity?

Looking at its simplicity, I'd say this system stands nicely nonetheless; without demanding further computational extensions.
Of course, as opposed to many traditional text adventures, we don't have a repository/inventory feature here (that is, some kind of list or storage where a player can manage collected objects). On the other hand, skimming through my draft ideas, which are mainly intellectual challenges, I don't really see a need for carrying stuff through the plot, since it's more about gaining knowledge than about gaining material goods. Also, picked up objects will probably force me, the author, into thinking of how they might be used in subsequent chapters, thus somehow predefining parts of the plot (e.g. the necessity to meet someone to share or exchange specific objects) and derogating narrative freedom.
So far, I appreciate the fact that both, entertainment and 'gameplay', are solely based on pure text. (Well, give or take... In the context of riddles, there surely will appear images here and there, but I don't consider them interactive elements.)


↑ top Multiple pathways

Conventional adventure interfaces let the player choose from a handful of options, like, 'go north', 'open door' or 'inspect drawer' by clicking buttons or links, giving the impression of free choice or self-determination. In our version there's only 1 control (the input field), but we still can mimic that sort of forking by allowing more than a single solution, each one leading to a different page.


↑ top Forking

Say, the chapter tells you're wandering down a hallway and reach two doors. Someone told you (in a previous chapter) that Hannah and Rachel, two sisters, live here. At the bottom of the page you're asked: 'Who do you like to visit?'
Now you're supposed to type either 'Hannah' or 'Rachel'; in both cases a correspondent page will open, titled e.g. 'In Hannah's Boudoir' or 'Chez Rachel' respectively. Of course, each of them will give you valuable information that's required to successfully follow the general plot, so you must visit them both. However, it isn't relevant in which order you do that.
As you can see, flexibly branching a plot is pretty easy.


↑ top Joining

We don't want to have loose branches or blind alleys in our story. Sure enough, a reader could be stuck somewhere because a riddle is too hard to answer (yet), and he/she has to use the browser's go-back function to return to a previous 'plot crossing' and try another direction. This might happen anyway, so I wouldn't recommend inserting dead ends (pages without input box) intentionally, because it's annoying and frustrating to have come this far and getting blocked by design. Therefor we need a way to reunite forked branches again.
So we've got two chapters, let's take Hannah's and Rachel's pages from above, and both should direct the reader to the same target page, say, 'The Lily Garden'. How could this be accomplished? Luckily there's a quick way out of this dilemma, involving but a tiny math twist:
Let's give both source pages their individual riddles as usual, and let e.g. Hannah's page determine the hash for our Lily Garden chapter.
Since Rachel's riddle (and accordingly its answer) is different, it will produce an unfitting hash. But, without further ado, we can easily 'repair' it by XORing the hash string (more precisely, the bytes it represents) using a matched constant which isn't hard to prepare.
Although a hacking player might spot this constant in the source code, it won't be helpful for any cheating attempts. Problem solved.

Furthermore, by applying this method of hash adjustment we're able to rejoin as many branches as we want. If, for example, the story got split into a handful of parallel plot lines (think of also visiting Bambi, Jezebellah, Ada and Celeste - yes, we're in a brothel), we could unite them all at once into one single stream at any time.


↑ top Hash correction

In order to join (make multiple pages link to the same target chapter), we must manipulate their (diverse) hashes so they deliver the same filename.
As mentioned above, this can be achieved by XORing them with appropriate constants which are deposited in the Javascript code of the particular pages.

Here's a calculator for generating such correction values:
Like in the form above, you can create a target filename by using the 1st input field. Then you can type other stuff in the 'other input' field to see which correction values are being generated (and make the resulting filename equal the aimed one).

1st Input:
(Hash: ?)
Aimed filename: ta_.html
Other Input:
(Hash: ?)
Correction value:
Resulting filename: ta_.html

Apart from joining there's actually another situation where the option of hash correction comes handy: when different answers coincidentally produce the exact same hash - although they're supposed to yield different filenames (to keep the paths of their particular chapters separated).
This is a case where we must avoid an unintended join. The probability this will ever happen is virtually zero, but if it happens, we can use our correction value to repair (anti-join, if you will) one of the hashes.


↑ top Ambiguous answers

As a matter of fact, not only hashes may collide (which fortunately is extremely rare), but also answers themselves (which occurs quite easily). Imagine the following scenario:

The story has 20 chapters. On page 18 there's a hard riddle - e.g. about World War II - with the solution 'Paris'. So far, so good.
Our player is still in the early chapters, say, on page 3, where he's visiting a brothel and has to answer the question of Jezebellah "Where do I come from?". Of course, there's a couple of hints in the text pointing at Brazil - which, by the way, be the correct answer here.
Now assume the player didn't get the hints and starts guessing wildly. Since Jezebellah is a prostitute, he tries 'Las Vegas'. No. Then 'Sodom'. No. Then 'Paris' (whose hash happens to produce the filename for page 19 - remember aforesaid WW2 riddle on page 18?). Bingo! Out of sheer luck, our player just landed in chapter 19 (having skipped 15 pages - pretty much the whole story). Ouch.

Well, one could blame the author for chosing such short and commonplace words like 'Paris'. On the other hand, it's hard to prevent lucky guessing; and similar bloops can also happen due to misspelling or mistyping.
So should we always strive for complicated and long solutions, even multi-word expressions? Isn't there a better, possibly foolproof way? And it turns out... there is.

Each entered answer gets prefixed (e.g. by prepending the chapter title) before hashes are calculated. In the example above, the valid answer on page 3 would then internally be 'JezebellahBrazil'. The answer for page 18 would become 'WorldWarIIParis'.
And now, if 'Paris' is typed on page 3, a hash based on 'JezebellahParis' will be created, which yields a non-existing filename and brings on the 404 error message - exactly as it should be.
No more ambiguity collision. Problem solved.


↑ top Answer variations

In most cases the solution consists of a unique expression, like, a name, a special word/sentence or a number - so the reader would know exactly what to enter once he/she found the solution. Sometimes, however, there still might be various possibilities to give the right answer.
Let's take, for example, the concept of 'third' which could be written as 'third', '#3', '3.', '3rd' or 'the third', just to name a few. In a fair game, all these variations express the same correct answer ('third') and should forward the player to the next chapter.
Also, we must not forget various spellings of names, places or other words (e.g. 'color' vs. 'colour'), let alone case-sensivity.

Even slightest spelling differences will produce totally different hashes, but with only one hash correction value per page we can't cover more than one answer. Hence we need a different approach to handle this.


↑ top Forwarding

The easiest way to relay intermediate pages (that are requested due to alternative answers) is to use the HTML meta tag 'refresh'; to be placed it in the page header like so: <meta http-equiv='refresh' content='0;url=targetpage.html'>.
The rest of the page can be empty, because the user is immediately forwarded to the specified chapter page.
Modern browsers are aware of this refresh and will adjust their browser history accordingly; in other words: hitting the back-button on a target page will send a user back to the calling page (and not to the intermediate page, which would produce a loop).