I was recently playing a game, and someone advised that if you're after a particular status effect you can keep surrendering to one of the bosses. (you get one at random if you surrender in battle; each monster has 1-4 options). But their method for getting there quickly was an interesting one: walk around until you get a battle. If it's not a monster that offers the ending you want, undo and repeat. Once you have the monster you want, surrender. If the result isn't the one you want, undo and do it again.

Since then, I've been wondering if there could be a way to prevent the abuse of 'undo' to redo random stuff.

Not sure if there's really a point; I want the game to be fun. But it would be nice if it wasn't so easy to 'cheat'. So, here's my solution (pseudocode):

Random Number function
As well as the range of numbers to select, this will have a parameter for 'reason'. This would be a string like attackroll_goblin or Wizard_Nigel_drops. In the case of PickOneString, it probably makes sense to use the list of options as the reason.

RandomNumber does:

  • If there is a number in game.rng_pending with the same reason, choose it (and remove it)
  • Otherwise, call GetRandomInt
  • Store the number in game.rng_generated
    • This is a list of dictionaries. Each dict contains a random number, its range, and its reason.
    • To prevent file bloating, you could remove items from the beginning of the list if it's over a certain length
  • Then return the number

The 'undo' command will:

  • Copy rng_pending and rng_generated into local variables (which aren't affected by transactions)
  • call undo
  • Compare the local variables to the list attributes.
    • Entries in rng_generated before the undo that are no longer there should be added to rng_generated unless they're already there
    • Then any entries that were in rng_pending before the undo but aren't now should be added.

This basically means that when the game generates a random number for some specific purpose (treasure, combat, etc), it records it in a log. When you undo, those stored numbers will be put into a queue, so that they'll be used the next time you roll for that thing. You presumably don't need to save numbers for purely cosmetic things like using {random: in the text processor to taunt the player, but it does mean that you can't keep undoing and attacking the same monster again until you get a critical hit. And (unlike a random queue without the reasons), you can't respond to a fumble by undoing and skipping a turn so an enemy gets your bad luck.

What do you think? I'm pretty sure I'm thinking about this too much; but it would be nice to have an anti-cheating feature that doesn't disable 'undo' entirely.

Perhaps UNDO is itself a 'cheat'? I don't think I've encountered a case where a designer has incorporated it in game play? Indeed, as you know, UNDO in standard Quest is broken so perhaps most designers don't even think about it?

Out of curiousity, I tried UNDO in The Mansion (2004, Quest 3), with the following surprising result:

> n
The glass paned door is locked. If you had a weapon, you could smash it open, or maybe there's a key somewhere around.

> undo
You pick it up.

I think there is a place for undo in games. I know I use the up/down arrows a lot when repeating commands; but sometimes memory is imperfect and you enter the wrong thing. I think 'undo' and 'oops' are reasonable if you mistyped a command; or hit enter before completing a list of objects. (Especially as I'm in the habit of only typing the first few letters of an object's name, for speed. Sometimes it's possible for a typo to select completely the wrong object.

Very much a personal thing. Using it to cheese the random number generator feels like cheating to me. Correcting a typo seems like intended behaviour. Trying an option and then going back, I'm not sure about… maybe depends on the game. Somewhere in between the other two.

Wouldn't it be easier to randomly populate an enemy dictionary at game start and then access it with an index variable? With Undo then only the index variable must be reset.

What do you put in the dictionary? All the random numbers for the game?

And that's got me thinking about another useful thing you could do.

If you have a custom function that's used whenever you generate a random number, you could have it call something like JS.recordRandom(). Have a function which does nothing on the desktop player (as it's not needed), but records the random numbers used into a big array. The array also stores the commands that the player has entered.

Then, if the web player is disconnected (wifi signal issues, server fell over, timeout, or whatever), these arrays are saved to LocalStorage. The JS on the client side pops up a message "you have been disconnected from the server. Reconnect?" And when you reconnect, it reloads the game from the last save.

However, the UI Initialisation script now checks LocalStorage for an array of saved commands and random numbers. If found, they can be splatted back to the server. your rng_pending is populated with all the random numbers, and the commands from the previous attempt are splatted back into commandqueue.

Quest can replay the game from your last save point, using a saved copy of all the random numbers you entered so that it's guaranteed to play out the same way. Would enable you to work around any stability issues with the server, and allow players to continue from where they were up to after a timeout or other disconnection.

What do you put in the dictionary? All the random numbers for the game?

It depends on what you need the random numbers for. If you want to randomly spawn monsters, at the start of the game the monsters will be randomly added to a monster dictionary.

Log in to post a reply.