11

I am trying to understand the 1969 FOCAL program Hamurabi in detail. The program was originally written for the PDP-8 version of FOCAL, but I am running it on the BK 0010-01 which presumably is a clone of the PDP-11 version.

There are two random number generators in the program which appear to produce the same output (random integers from 0 to 9), but I am trying to work out if they are different in any way (maybe in terms of the distribution of the random numbers). I understand that this is also a mathematics problem, but I haven't done any mathematics since I was 16.

The two exprssions are:

FITR(10*FABS(FRAN()))

FITR(5*FABS(FRAN()*2))

To explain what I do know:

  • FITR () - truncate to an integer (FOCAL by default works with floats to 4 decimal places).
  • FABS () - give the absolute value (turn negative into positive)
  • FRAN () - generate a random number from -0.9999 to 0.9999

I have tried running the two expressions separately to see the output, including taking each step of the expression at the time, but I have not been able to see any obvious difference.

Does it make any difference to multiply the inner expression by 10, or to multiply the expression by 5, while mulitiplying the result of FRAN () by 2?

If they do produce exactly the same result, I suppose the question is why are they done like this? I wonder if it some sort of security through obscurity, which can also be possibly seen in how the program calls subroutines somewhat at random, and somewhat obfuscates the variable names.

8
  • 2
    Mathematically, they're identical. There must be some subtle FOCAL implementation details for edge cases, or FRAN() might not be that random.
    – RonJohn
    May 28, 2023 at 16:10
  • 5
    The simplest answer to "why did they do this" if they results are identical is if the programmer (or programmers) just solved a similar problem in several places, and didn't notice there were subtle differences in the formula... That's something that has happened to me frequently, and with the limited editing back then it's was even more likely back then.
    – dirkt
    May 28, 2023 at 18:52
  • 1
    BTW, added the focal tag - I think it's a valid one. Anyone (with enough rep to do so) who wants to review my usage guidance is welcome to do so. It could no doubt be improved or expanded upon.
    – paxdiablo
    May 28, 2023 at 23:28
  • 1
    You're right, it was on the pdp-8 originally, and only later ported to other pdp systems including the pdp-11, the bk series being a clone of the pdp-11/03. I'll change the tag
    – harlandski
    May 29, 2023 at 0:03
  • 1
    I think, that while hardware was copied, at least some of software was re-written, so you might not have the same interpreter. May 30, 2023 at 7:02

4 Answers 4

13

Not really an answer, but posting as an answer since I need the space to work in.

Working through both cases...

FITR(10 x FABS(FRAN()))

FRAN gives (-0.9999, 0.9999)
FABS gives (0, 0.9999)
x 10 gives (0, 9.9990)
FITR gives (0, 9)

versus

FITR(5 x FABS(FRAN() x 2))

FRAN gives (-0.9999, 0.9999)
x 2  gives (-1.9998, 1.9998)
FABS gives (0, 1.9998)
x 5  gives (0, 9.9990)
FITR gives (0, 9)

The above assumes exact decimal arithmetic, which might be my downfall. But I (a non-FOCAL non-PDP-7 programmer) see no difference in the results.

I don't see any non-randomness rationale behind this. Some PRNGs are known to lack randomness in the low bits, but ignoring the low bits is a matter of division, not multiplication.

Absent any other explanation, I'd suggest maybe this was a consequence of incremental program modification.

4
  • Would have been my first idea as well.
    – Raffzahn
    May 28, 2023 at 22:16
  • 4
    "Not really an answer" - I would posit this is an answer, however speculative you may think it is. Even speculative answers can be considered useful, doubly so on the retro site where so much information has disappeared :-)
    – paxdiablo
    May 28, 2023 at 23:27
  • -1 from me. This answer only re-states what the question already said: that this appears to be a generator for numbers 0-9. But 1. Exact decimal arithmetic is a rather useless model for the purpose 2. Even if the top and bottom ranges are identical, that doesn't necessarily mean the distributions are identical. 3. Even if the distributions are identical, there could still be some subtleties with evaluation order or whatever that cause the generators to produce different random sequences. May 29, 2023 at 7:55
  • 'Could be' any of those, but I'd welcome an explanation as to how that would affect game-playing. FWIW, the floating-point interpreter is described here. Given the nature of the game, I'm a little suspicious of the need for subtle distinctions in random-number generation.
    – dave
    May 29, 2023 at 13:04
5

I'm pretty sure that the answer is that the last line has been modified. There is no difference in function as demonstrated by @another-dave but the second line

08.10 S C=FITR(5*FABS(FRAN()*2))-4

is not original. It's been modified to change the game slightly. In the DEC PDP-8 handbook (pp 11-61, 11-62), it looks like this

08.10 S C=FITR(5*FABS(FRAN()))-1

The computing of that looks like

FRAN gives (-0.9999, 0.9999)
FABS gives (0, 0.9999)
x 5 gives (0, 4.9995)
FITR gives (0, 4)
-1 gives (-1, 3)

The new version, once you add the -4, gives a range of (-4, 5).

My conjecture is that they decided C (whatever quantity it represents) needed to have a bigger range, so they simply multiplied the random interval by 2 and then they discovered it was too weighted to the positive, so they changed the constant.

Yes they could have just changed the 5 to a 10, programmers frequently don't aggregate constants in that way because it is effectively a small amount of documentation.

2
  • 1
    The calculation is called a couple of times in the program, but to start with it is called and then 17 is added to it to give the price of an acre of land (in bushels) for a given year. The reference to another version of the program in the PDP-8 handbook is great, thanks for that!
    – harlandski
    May 29, 2023 at 15:34
  • By the way, what are your reasons for thinking that the version in the DEC PDP-8 handbook is the original? It is certainly shorter and clearer, and it does make sense of the *2 in line 8.10 (as it makes more sense for this to have been added than taken away).
    – harlandski
    May 29, 2023 at 15:44
4

[...] I have not been able to see any obvious difference

I suppose the difference is in how that value is meant to be used, which translates to program maintainability.

See line 5.20 in the original listing. There's some kind of parity test: IF (FITR(C/2)-C/2), which means that the program takes different course depending on whether that random number C is even or odd.

Most probably, the original author wanted even distribution of even numbers, which requires the range of random numbers to be [0, 2*n). So, in order to fool-proof his code and make sure that the upper limit is always an even number, the author devised that cryptic expression: 5*FABS(FRAN()*2). If written like that, it allows fine-tuning the game balance by adjusting that 5 literal, but protects one from breaking (presumably!) something by typing 9*FABS(FRAN()) or 11*FABS(FRAN()).

Online simulation: Try It Online FOCAL-69.

1
  • By the way, I've got through my deep dive into how the program works (converting it to C as I went) and now I understand the purpose of that IF (FITR(C/2) - C2) expression is - it is to randomly decide if rats eat some of the stores or not.
    – harlandski
    May 30, 2023 at 14:31
4

So having carefully studied both the version of the program I first presented, and the one JeremyP drew my attention to in the DEC PDP-8 Handbook, I can give something more of an answer about how these two expressions are used differently in the game.

In both versions of the game, this line of code is identical, and serves an identical purpose, to set the 1 in 10 chance of there being a plague in a given year:

05.40 S Q=FITR(10*FABS(FRAN())); ...

At the beginning of the main loop, Q is then checked, and if it is equal to zero, then the effects of plague are applied.

02.25 ... I (-Q)2.3

As others have pointed out, the other expression differs in the two different versions, and as I noted in comments, it is called at various times when a random number is needed:

DEC PDP-8 Handbook version:

8.10 S C=FITR(5*FABS(FRAN)))+1

Standalone printout version:

8.10 S C=FITR(5*FABS(FRAN)*2))-4

In both programs, the respective version of this line is called separately to:

  • Set the bushel price of an acre of land (03.10)
  • Calculate the annual bushel yield on an acre of land (05.10)
  • Determine whether rats eat any of the harvest (using the algorithm described by Igor G. (05.20)
  • Provide a random element in the number of people who are attracted to the city each year (05.30)

Assuming that the DEC PDP-8 Handbook is the original, then the reason for the otherwise mathematically identical expressions being different in the version I first saw, is that the author of the standalone version modified the code by adding the *2. It should also be noted that the -4 which was then added actually introduces a bug into the program, as it means that the value of C ranges from -4 to 5, which results in some nonsense (a negative number of people being attracted to the city in a given year), and sometimes also the program crashing with a division by 0 error in this line. I have added my comments using FOCAL syntax of C for COMMENT:

5.20 D 8; C Calls random function in 8.10; 
S E=0; C This will mean rats do not eat any grain; 
(FITR(C-2)/C-2)5.3; C On odd numbers, jump to line 05.30, 
skipping the next part and leaving E set to 0; 
S E=S/C; C Rats eat a random proportion of the grain stores.

It is the last expression which can cause a division by zero error.

I suppose it is possible that the standalone version was the original, and it was debugged and had a lot of the verbiage removed, but it seems more likely to me that a user modified the code in an attempt to make the game more challenging by adding the *2 ... -4 but in doing so indavertedly introduced a logical and a coding bug into the program. It does indeed make the game much more challenging, but it also causes some nonsense results and reasonably frequent crashes.

2
  • While you are comparing different versions, have you looked for David Ahl's translation into BASIC?
    – dave
    May 30, 2023 at 17:21
  • Not yet, that's the next step :-)
    – harlandski
    May 30, 2023 at 22:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .