Ok, here is the big question... -norand?

I’d like to switch the random function of TADS to a deterministic non-random function that will repeat exactly from game to game.

There are two reasons for this. One is that, with highly active NPCs running around causing trouble, it’d be helpful if user results were repeatable. You’d know that the walkthrough would work correctly and so forth.

The other is that, along with some kind of UNDO N-TURNS command, I think I’d be well positioned to make a time travel game.

Conrad.

–In other words,

  1. How do I use -norand from within the IDE, and make it stick when I release a game?

and

  1. Will this effect things like “<>”?

Conrad.

If you run the game in the debugger it should disable the call to randomize() that seeds the RNG. I’ll look into doing this for a distributable game.

There is no such guarantee. The only guarantee you get is that you get the same results in the same interpreter, the same binary of that interpreter and the same platform where you run that binary. So don’t do it. The results are different between platforms and interpreters.

If you want consistency across all platforms and interpreters, you need to write your own number generator. It’s not difficult, since the algorithms are well documented. You’d just pick the easiest one, since cryptographic security is not a concern here.

Ok,

I know basically how to make a pseudo-random number generator – I did it when I was a kid, using remainders of repeated divisions, as I recall.

But how do I splice that into TAD’s random feature? For example, in picking a string for display? – Or do I just have to do everything random the long way again?

Conrad.

The issue I didn’t think about here, is that rand() is an intrinsic function and therefore you can’t use the “replace” or “modify” keyword on it to provide your own implementation. This leaves you with only one way to deal with this, and it’s not pretty: you would have to modify all calls to rand() in the library with calls to your custom random function. rand() is used five times in “lib/_main.t” and six times in “lib/adv3/misc.t” (also in lib/webui.t, but that’s of no consequence).

The best solution here would be a design change in the TADS library itself, where every intrinsic function has a non-intrinsic wrapper where “modify” and “replace” works. TADS code would then use those wrappers instead of doing direct calls to the intrinsics.

I’m entirely out of my depth here.

–Can I do a writearound, where I define my own fuction, pseudRand, and only use it in my own code? Will the TADS random function sneak up on the action of my game world when I’m not looking?

Conrad.

Code in the TADS library uses rand(). In your own code, you can use whatever you want of course.

Can I get more easily to the -norand behavior than the rand() behavior?

C.

OK, I found an easy way to do this. Give me a few minutes to finalize a working example.

Very cool!

waits patiently

Instead of modifying the library all over the place, you can put a small modification in “include/tadsgen.h”. Change this line:

    rand(x, ...);

to:

    rand_intrinsic(x, ...);

(“rand_intrinsic” is just a suggestion; you can rename it to whatever you want.)
Now somewhere in your game, provide your own rand() function. An implementation that simply calls the default rand() (now “rand_intrinsic()”), would be:

rand(x, ...)
{
    local argList = [];
    for (local i = 1; i <= argcount; ++i) {
        argList.append(getArg(i));
    }
    return rand_intrinsic(x, argList...);
}

Make sure to clean and rebuild your project after this.

Of course the whole point is to use your own RNG. So make sure your rand() follows the behavior that is expected from that function (as documented in tadsgen.h.)

Mm… looking up the documentation on this, I see this is a project to keep me busy for awhile! (But one that should be attainable for my skill level.)

rand() is quite a flexible little function, isn’t it?

I’ll be glad to make these changes… but can you explain to me what they do?

I ask because I had misunderstood this code the first few times I looked at it. As I now understand it, this is what’s happening?

The old rand() is now renamed to rand_intrinsic().

The function label “rand()” is now taken over by a brand-new function, which I must write. That function currently calls the old random function, whose maiden name was “rand()” and which is newly renamed “rand_intrinsic().”

–Does it matter where in the code I write the new “rand()” function?

I greatly appreciate the help, RealNC.

Conrad.

Conrad, I think I may be mistaken about the issue. I mean not the above code; that works. I mean the core issue, where I posted in the previous page that “the results are different between platforms and interpreters.” I’m not so sure about this anymore. TADS 3 comes with two RNGs (LCG and ISAAC), but only one is actually enabled in interpreters. Of course that still leaves the problem of how to suppress the initialization of the RNG with a random seed from inside a game.

I’ll try to ask Mike Roberts about this. I’ll probably point him to this thread.

As for your questions about how the code works, you are correct. And it doesn’t matter where you implement your rand() function.

OK, thanks for the heads up – I don’t know when I would have started working on the new rand() function, but I’ll hold off to hear back.

Still interesting to see how it’s done.

Conrad.

RealNC, would it be simpler to create a -norand “game” that generates a few long random numbers, with lost of digits, interpret it for release, and pass around the game file to see how it runs on different interpreters?

Conrad.

The point is that you can’t create a “norand” game. The RNG is part of the interpreter, not the game.

Mike did reply, but it turns out this thing is a bit of a can of worms because having the same pseudorandom number sequence actually doesn’t help; when you UNDO and then repeat the last action, the results will be different because the RNG is transient. Also, its current state is not accessible by the game.

I’ll keep you posted about how this turns out. The idea right now is to introduce an argument to the randomize() function from tadsgen.h that takes a seed value. Passing a custom seed means the numbers will be identical everywhere. Additional changes will then need to be made for the game to be able to access RNG state for UNDO and SAVE.

Ohhh, I see.

Well… I imagine I can satisfice with a new pseudoRand() function, that works off the game turn number, and make sure all NPC-state-changing code are pseudo-random in this sense…

I’ll work on that angle while waiting to hear whether you guys think of something tighter.

I imagine the NPCs won’t do anything random without me specifically telling them to, and I’ll just manage <> so it doesn’t make substantative changes.

Conrad.

Uh… ok, I just posted to Yahoo Answers and asked for a non-recursive, non-iterative pseudo-random number generator.

I pretended to be a girl (so I’d be sure to get an answer)…

I instantly got this formula:

uint get_random()
{
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w; /* 32-bit result */
}

…which looks recursive to me. But I don’t really savvy C code. Will this map x -> a random y?

Conrad.

ps–I also got a weird invitation to another “answers” community, by someone who was also pretending to be a girl.

Because there are definitely no actual girls on the internet?