Anatomy of a Random Number Generator
Tales from the realms of software archeology.
Recently, I returned my interest to Spacewar!, the first digital video game, particularly to differences between early versions, like Spacewar 2B, and the better known, later versions, like 3.1 and thereafter. One of these differences, particularly of interest here, is in the random number generator, implemented by a macro named "random
" (what else?).
Before we dive right into it, a few words on the computer this code ran on, the DEC PDP-1:
The PDP-1 (Programmed Data Processor -1) was introduced by DEC in 1959 (first production models PDP-1C were delivered in 1961) as one of the first fully transistorized computers. It was somewhat a commercial version of MIT’s experimental TX-0, the first fully transistorized computer (rivaling for the title with Vienna’s “Mailüfterl”, which started earlier, but finished later, due to scarce resources.) Aimed specifically at realtime computing, the PDP-1 was an amazing piece of pro-sumer equipment: While still consisting of a hefty 3-compartments cabinet, it plugged into a common wall socket for power at just 120 W and could be turned on by a simple flip of a single switch. Notably, it was also one of the first commercial machines to come with a visual display, the Type 30 CRT. In short, “world’s first toy computer” (© J.M. Graetz).
Technically, the PDP-1 was an 18-bit words stored program computer with a rather nice instruction set (instruction words consisting of a 5-bit instruction part, a 12-bit address part, and a universal indirect addressing bit inbetween; for details see here). Like the TX-0, it used one’s complement arithmetics, meaning, negative numbers were represented by just all bits flipped. While this saved the extra operation of incrementing the value in another step, as in two’s complement representation primarily used today, it came at the price of an additional value of negative zero (all bits high).
The preliminaries delivered, let’s have a look at the code in question.
It’s a macro, inspiredly named "random
", in the format of the PDP-1 Macro assembler. The keyword (pseudo-instruction) "define
" starts the definition, followed by the name and an optional parameter-list, the body containing the actual instructions, and a final "terminate
", here abbreviated to "
. Comments (here by yours truly) start with a slash, a preceding back-slash represents an over-strike, used to mark an automatic assembler variable. Parentheses (the optional closing part missing here), indicate a constant value. Numerical values are in octal notation.term
"
/spacewar 2b, 25 mar 62 /spacewar 3.1, 24 sep 62 define define random N random lac N lac ran rar 1s rar 1s xor (335671 xor (355670 add (335671 add (355670 dac N dac ran term term /useage random \ran random
Let’s have a look at the instructions:
lac Y
– load accumulatorC(AC) ← C(Y)
Loads the contents of the given address (Y) into the accumulator (AC).rar n
– rotate accumulator right.C(AC) ← (C(AC) >> n) | ((C(AC) & (2n−1)) << (18−n))
Rotates the contents of the accumulator right by the number of bit positions provided. This may be up to 9 bits (half a word-length), "1s
" is an assembler constant representing a single bit position.xor Y
– exlusive orC(AC) ← C(AC) XOR C(Y)
Applies an exclusive-OR operation with the contents of the given address to the accumulator.add Y
– addC(AC) ← C(AC) + C(Y)
Adds (with carry) the contents of the given address to the value in the accumulator.dac Y
– deposit accumulatorC(Y) ← C(AC)
Deposits (stores) the value in the accumulator in the given memory location. (The contents of AC remains unchanged; here pointed out, because the later PDP-8 resets AC to zero, when stored.)
From an algorithmic perspective, the macro
- loads the random number
- rotates it right by a bit position
- applies an XOR with a constant value
- adds the same constant to this
- and stores the result
While this is true for both versions, there are minor differences. Let’s have another look:
/spacewar 2b, 25 mar 62 /spacewar 3.1, 24 sep 62 define define random N random lac N lac ran rar 1s rar 1s xor (335671 xor (355670 add (335671 add (355670 dac N dac ran term term /useage random \ran random
While there are minor differences in the constants, which we will ignore for the moment, the major difference is in the definition of the memory location used for the random number: In Spacewar 2B it’s a parameter, in version 3.1 it’s a hard-coded label. Hence, the earlier version may be applied to various locations (we may assume that this was once a general purpose routine that came from elsewhere), but the later, final version uses a single, fixed one. — Why would you do this? Especially, since editing any of the numerous calls of the macro in the source code wasn’t exactly a joy with the tools available then. Clearly, you wouldn’t do this just for fun!
Was there anything wrong with it?
While the two versions result (apart from the constant used) in the same object code, the single difference is in the way, the memory location used to store the random number is defined in each of the programs: In Spacewar 2B it’s an automatic assembler variable, which will be located after the object code loaded into memory from tape. In Spacewar 3.1 it’s a label to a location in the actual program, containing an initial value of (positive) zero. Could this make a difference? Meaning, in Spacewar 3.1 the random number starts from a known seed value of zero, while in Spacewar 2B it could have been anything, considering that the PDP-1 used nonvolatile core-memory. E.g., an instruction used in the program before Spacewar was loaded. What could go wrong?
I guess, any routine that shifts and scrambles a value which is eventually fed back to it qualifies as a polynomial counter or LFSR (Linear Feedback Shift Register), meaning, it cycles over a fixed, but nonlinear sequence of values starting from a known seed value. That is, as long as it doesn’t “fall into the pit” of a minor cycle (which may in the worst case consist just of single value, repeated over and over again). Mind that Spacewar 3.1 introduces a well defined seed value, namely plus zero.
However, if we start from a random value, not included in the set of numbers produced by the random number generator, anything could happen — including falling into the “pit” of a minor cycle. Was there such a minor cycle? Let’s assume, there was a particular program which, when run befor Spacewar, caused the RNG to break, because of the particular value left in the memory location used for "\ran
".
What could be the worst, most noticeable effect of the random number generator having stopped working in Spacewar? Probably we could live with the exhaust flames being of a constant length and an explosion will happen only that often. So what would may be that obvious that you had to address this edge case?
Did the sun stop spinning in early versions of spacewar?
Empirical Investigations
Let’s investigate the series of numbers produced by the macro "random
". As we’ve seen, the random number is produced by a simple bitwise rotation to the right and a scrambling operation, which consists of first applying an XOR with a constant and than adding the same constant to this, again. This is a bit peculiar, since an addition is essentially an XOR operation and a ripple carry applied to it. Since two consecutive XOR operation cancel each other out, this leaves us just with the carry operation, which would have been applied, if the constant had been added. How would you come up with this?
Anyway, the series produced by the random number generator may not exceed an 18-bit space, which is a viable range to be explored empirically on a modern computer. Let’s run it as implemented in Spacewar 3.1:
States of ran starting from 0: iteration operation binary octal ran (seed) 000000000000000000 (000000) (1) rar (>> 1) => 000000000000000000 (000000) xor 355670 => 011101101110111000 (355670) adc 355670 => 111011011101110000 (733560) (2) rar (>> 1) => 011101101110111000 (355670) xor 355670 => 000000000000000000 (000000) adc 355670 => 011101101110111000 (355670) (3) rar (>> 1) => 001110110111011100 (166734) xor 355670 => 010011011001100100 (233144) adc 355670 => 110001001000011100 (611034) (4) rar (>> 1) => 011000100100001110 (304416) xor 355670 => 000101001010110110 (051266) adc 355670 => 100010111001101110 (427156) (5) rar (>> 1) => 010001011100110111 (213467) xor 355670 => 001100110010001111 (146217) adc 355670 => 101010100001000111 (524107) (6) rar (>> 1) => 110101010000100011 (652043) xor 355670 => 101000111110011011 (507633) adc 355670 => 000110101101010100 (065524) (7) rar (>> 1) => 000011010110101010 (032652) xor 355670 => 011110111000010010 (367022) adc 355670 => 111100100111001010 (744712) (8) rar (>> 1) => 011110010011100101 (362345) xor 355670 => 000011111101011101 (037535) adc 355670 => 100001101100010101 (415425) ... (261,651) rar (>> 1) => 110110111000111001 (667071) xor 355670 => 101011010110000001 (532601) adc 355670 => 001001000100111010 (110472) (261,652) rar (>> 1) => 000100100010011101 (044235) xor 355670 => 011001001100100101 (311445) adc 355670 => 110110111011011101 (667335) (261,653) rar (>> 1) => 111011011101101110 (733556) xor 355670 => 100110110011010110 (466326) adc 355670 => 000100100010001111 (044217) (261,654) rar (>> 1) => 100010010001000111 (422107) xor 355670 => 111111111111111111 (777777) adc 355670 => 011101101110111000 (355670) ! 355670 seen as result of iteration #2 → loop to (3).
So this will produce a period of 261,654 distinctive values, eventually returning to the beginning, but skipping the rather problematic zero and the immediate production resulting from it. Moreover, the final value of octal 355670 (also known as the reset value) is the same as the constant used for scrambling! Considering that there are just 490 values out of the 262,144 distinct 18-bit numbers not included in this set, we may deem this a rather nice random generator.
However, there’s a pecularity, associated with the specific constant used. Octal 355670 is in binary notation 011101101110111000
, meaning, besides the shifting operation, no scrambling will be applied to the lowest 3 bits, as may be observed in the above listing. That is, there’s also a pecularity to the addition, which introduces a bit of extra variety: Using one’s complement, a) we have to adjust for minus zero (777777
→ 000000
) and b) we have to adjust for any overflow in the sign position by an extra increment (as may be observed in iteration #6).
emulation of one’s complement addition (18 bits) function adc(value) { ac = ac + value; // check overflow of sign bits (to bit 18) if (ac & 0o1000000) ac = (ac + 1) & 0o777777; // correct negative zero if (ac == 0o777777) ac = 0; } thus 101000111110011011 // 507633 + 011101101110111000 // 355670 = 1000110101101010011 // 1065523 but adc( 101000111110011011, // 507633 011101101110111000 // 355670 ) => 000110101101010100 // 065524
So, let’s have a look at how the values outside this set behave, since 490 isn’t that an excessive number:
(But still prohibitively extensive for results to be favorably presented as scrollable list.)
out-of-sequence seeds:
seed: 000000000000000011 (000003) 448 iterations, returns at 777745
seed: 000000001001101011 (001153) 448 iterations, returns at 775025
seed: 000000001011001000 (001310) 448 iterations, returns at 774557
seed: 000000100111001010 (004712) 448 iterations, returns at 765523
seed: 000000111010101111 (007257) 448 iterations, returns at 751235
seed: 000001011111000111 (013707) 448 iterations, returns at 747555
seed: 000001110100100001 (016441) 448 iterations, returns at 632641
seed: 000010000001101001 (020151) 448 iterations, returns at 637021
seed: 000010001010000001 (021201) 448 iterations, returns at 635341
seed: 000010010100101011 (022453) 448 iterations, returns at 622625
seed: 000010011101101001 (023551) 448 iterations, returns at 620021
seed: 000010011111110000 (023760) 448 iterations, returns at 627437
seed: 000010100001010000 (024120) 448 iterations, returns at 627137
seed: 000010101111011011 (025733) 448 iterations, returns at 613465
seed: 000010110000111000 (026070) 448 iterations, returns at 613617
seed: 000010110010100011 (026243) 448 iterations, returns at 613245
seed: 000010111001101110 (027156) 448 iterations, returns at 611033
seed: 000010111100100011 (027443) 448 iterations, returns at 610645
seed: 000011000010010001 (030221) 448 iterations, returns at 617301
seed: 000011000111101010 (030752) 448 iterations, returns at 615423
seed: 000011001010101000 (031250) 448 iterations, returns at 615257
seed: 000011001011101010 (031352) 16 iterations, returns at 614423
seed: 000011001110111011 (031673) 7 iterations, returns at 603565
seed: 000011010101011110 (032536) 448 iterations, returns at 602073
seed: 000011011010010110 (033226) 448 iterations, returns at 601313
seed: 000011011011101011 (033353) 448 iterations, returns at 600425
seed: 000011100101011111 (034537) 448 iterations, returns at 606075
seed: 000011101010101100 (035254) 448 iterations, returns at 605227
seed: 000011101011010000 (035320) 448 iterations, returns at 604537
seed: 000011101111011000 (035730) 448 iterations, returns at 673517
seed: 000011110110001101 (036615) 448 iterations, returns at 672331
seed: 000100001011110011 (041363) 448 iterations, returns at 674405
seed: 000100100010001011 (044213) 448 iterations, returns at 667325
seed: 000100100011110101 (044365) 448 iterations, returns at 666411
seed: 000100100110000000 (044600) 8 iterations, returns at 666377
seed: 000100101011110101 (045365) 448 iterations, returns at 664411
seed: 000100111000101100 (047054) 448 iterations, returns at 651627
seed: 000100111101111111 (047577) 448 iterations, returns at 650375
seed: 000101000000011010 (050032) 448 iterations, returns at 657663
seed: 000101000001101111 (050157) 448 iterations, returns at 657035
seed: 000101001000110001 (051061) 448 iterations, returns at 655601
seed: 000101001101101011 (051553) 16 iterations, returns at 654025
seed: 000101010101100000 (052540) 448 iterations, returns at 642077
seed: 000101101010111010 (055272) 448 iterations, returns at 644563
seed: 000101111110111001 (057671) 448 iterations, returns at 537561
seed: 000110000011100111 (060347) 448 iterations, returns at 536455
seed: 000110010011101101 (062355) 448 iterations, returns at 522431
seed: 000110101010111011 (065273) 448 iterations, returns at 524565
seed: 000110101101101011 (065553) 448 iterations, returns at 524025
seed: 000110111111101111 (067757) 5 iterations, returns at 517435
seed: 000111000010010011 (070223) 448 iterations, returns at 517305
seed: 000111010010000111 (072207) 4 iterations, returns at 503355
seed: 000111011111001101 (073715) 448 iterations, returns at 507531
seed: 000111100001111001 (074171) 448 iterations, returns at 507361
seed: 000111101010010011 (075223) 448 iterations, returns at 505305
seed: 000111110011010011 (076323) 448 iterations, returns at 572505
seed: 000111110100111011 (076473) 448 iterations, returns at 572165
seed: 000111111100011010 (077432) 448 iterations, returns at 570663
seed: 000111111101001000 (077510) 7 iterations, returns at 570157
seed: 001000000000100001 (100041) 448 iterations, returns at 577641
seed: 001000000001011110 (100136) 448 iterations, returns at 577073
seed: 001000010100110010 (102462) 448 iterations, returns at 562603
seed: 001000010101110001 (102561) 448 iterations, returns at 562001
seed: 001000011101100011 (103543) 448 iterations, returns at 560045
seed: 001000100111100001 (104741) 448 iterations, returns at 565441
seed: 001000101000111100 (105074) 448 iterations, returns at 565167
seed: 001000110111101010 (106752) 448 iterations, returns at 551423
seed: 001001000101001010 (110512) 448 iterations, returns at 556123
seed: 001001001000011101 (111035) 448 iterations, returns at 555671
seed: 001001010110100101 (112645) 448 iterations, returns at 542251
seed: 001001010111010010 (112722) 448 iterations, returns at 541503
seed: 001001010111111110 (112776) 8 iterations, returns at 541773
seed: 001001011111101011 (113753) 448 iterations, returns at 547425
seed: 001001110011000000 (116300) 448 iterations, returns at 432577
seed: 001001110100100111 (116447) 448 iterations, returns at 432655
seed: 001010001011111011 (121373) 448 iterations, returns at 434765
seed: 001010011101011001 (123531) 448 iterations, returns at 420061
seed: 001010100011001101 (124315) 448 iterations, returns at 426531
seed: 001010101100110111 (125467) 448 iterations, returns at 424615
seed: 001010110111010010 (126722) 448 iterations, returns at 411503
seed: 001010111111001111 (127717) 448 iterations, returns at 417535
seed: 001011000001000101 (130105) 16 iterations, returns at 417151
seed: 001011000010000110 (130206) 448 iterations, returns at 417353
seed: 001011001110111010 (131672) 448 iterations, returns at 403563
seed: 001011001111110011 (131763) 448 iterations, returns at 403405
seed: 001011001111111101 (131775) 448 iterations, returns at 403771
seed: 001011010111010111 (132727) 448 iterations, returns at 401515
seed: 001011110101000001 (136501) 448 iterations, returns at 472141
seed: 001100001000000010 (141002) 448 iterations, returns at 475743
seed: 001100001011111000 (141370) 448 iterations, returns at 474417
seed: 001100011010101100 (143254) 448 iterations, returns at 461227
seed: 001100101001001101 (145115) 448 iterations, returns at 465131
seed: 001100101111011000 (145730) 448 iterations, returns at 453517
seed: 001100101111011001 (145731) 448 iterations, returns at 453461
seed: 001100110000101010 (146052) 448 iterations, returns at 453623
seed: 001100111001010010 (147122) 448 iterations, returns at 451103
seed: 001101001011111100 (151374) 448 iterations, returns at 454767
seed: 001101001110001010 (151612) 448 iterations, returns at 454323
seed: 001101011001011000 (153130) 448 iterations, returns at 441117
seed: 001101011101010110 (153526) 448 iterations, returns at 440113
seed: 001101011110111110 (153676) 448 iterations, returns at 447573
seed: 001101111110000001 (157601) 448 iterations, returns at 330341
seed: 001110000101101100 (160554) 448 iterations, returns at 336027
seed: 001110001111010011 (161723) 448 iterations, returns at 323505
seed: 001110010010011001 (162231) 448 iterations, returns at 323261
seed: 001110011000010011 (163023) 448 iterations, returns at 321705
seed: 001110011011011100 (163334) 448 iterations, returns at 320467
seed: 001110111101011001 (167531) 448 iterations, returns at 310061
seed: 001111000000100101 (170045) 448 iterations, returns at 317651
seed: 001111001001001100 (171114) 448 iterations, returns at 315127
seed: 001111001001110111 (171167) 448 iterations, returns at 315015
seed: 001111010000010111 (172027) 448 iterations, returns at 303715
seed: 001111010100100111 (172447) 448 iterations, returns at 302655
seed: 001111110000110100 (176064) 448 iterations, returns at 373607
seed: 001111110010010111 (176227) 448 iterations, returns at 373315
seed: 001111110100011100 (176434) 448 iterations, returns at 372667
seed: 001111110110101010 (176652) 448 iterations, returns at 372223
seed: 010000000000000001 (200001) 448 iterations, returns at 377741
seed: 010000000000101101 (200055) 16 iterations, returns at 377631
seed: 010000000010101001 (200251) 448 iterations, returns at 377221
seed: 010000000110110001 (200661) 448 iterations, returns at 376201
seed: 010000001101100000 (201540) 448 iterations, returns at 374077
seed: 010000010010100010 (202242) 448 iterations, returns at 363243
seed: 010000100100100010 (204442) 448 iterations, returns at 366643
seed: 010000111101101111 (207557) 448 iterations, returns at 350035
seed: 010001000110111110 (210676) 448 iterations, returns at 355573
seed: 010001001000111110 (211076) 448 iterations, returns at 355173
seed: 010001010100110100 (212464) 448 iterations, returns at 342607
seed: 010001010111011111 (212737) 448 iterations, returns at 341475
seed: 010001100001001101 (214115) 448 iterations, returns at 347131
seed: 010001101110111001 (215671) 448 iterations, returns at 233561
seed: 010001101111000100 (215704) 16 iterations, returns at 233547
seed: 010001101111101101 (215755) 448 iterations, returns at 233431
seed: 010001110000100011 (216043) 448 iterations, returns at 233645
seed: 010001110111001100 (216714) 448 iterations, returns at 231527
seed: 010001111001101111 (217157) 448 iterations, returns at 231035
seed: 010001111001110100 (217164) 448 iterations, returns at 231007
seed: 010010000100101111 (220457) 448 iterations, returns at 236635
seed: 010010001000010001 (221021) 448 iterations, returns at 235701
seed: 010010010001000101 (222105) 448 iterations, returns at 223151
seed: 010010010011001010 (222312) 448 iterations, returns at 222523
seed: 010010010101010011 (222523) 448 iterations, returns at 222105
seed: 010010011001101001 (223151) 448 iterations, returns at 221021
seed: 010010011011101000 (223350) 448 iterations, returns at 220457
seed: 010010101010100001 (225241) 1 iterations, returns at 225241
seed: 010010110100010000 (226420) 448 iterations, returns at 212737
seed: 010011000001001000 (230110) 448 iterations, returns at 217157
seed: 010011000101100010 (230542) 448 iterations, returns at 216043
seed: 010011001000000111 (231007) 448 iterations, returns at 215755
seed: 010011001000011101 (231035) 448 iterations, returns at 215671
seed: 010011001101010111 (231527) 448 iterations, returns at 214115
seed: 010011011100011001 (233431) 448 iterations, returns at 200661
seed: 010011011101100111 (233547) 16 iterations, returns at 200055
seed: 010011011101110001 (233561) 448 iterations, returns at 200001
seed: 010011011110100101 (233645) 448 iterations, returns at 200251
seed: 010011011111001000 (233710) 448 iterations, returns at 207557
seed: 010011101111000001 (235701) 448 iterations, returns at 273541
seed: 010011110110011101 (236635) 448 iterations, returns at 272271
seed: 010011111111000010 (237702) 448 iterations, returns at 277543
seed: 010100000111110001 (240761) 448 iterations, returns at 275401
seed: 010100011000010111 (243027) 448 iterations, returns at 261715
seed: 010100011110001001 (243611) 448 iterations, returns at 260321
seed: 010100100001001000 (244110) 448 iterations, returns at 267157
seed: 010100100011100010 (244342) 448 iterations, returns at 266443
seed: 010100101000101100 (245054) 448 iterations, returns at 265627
seed: 010100110001111101 (246175) 448 iterations, returns at 253371
seed: 010100110101011011 (246533) 448 iterations, returns at 252065
seed: 010100111111001010 (247712) 16 iterations, returns at 257523
seed: 010101000100000000 (250400) 448 iterations, returns at 256777
seed: 010101001011100000 (251340) 448 iterations, returns at 254477
seed: 010101010000110101 (252065) 448 iterations, returns at 243611
seed: 010101010001101100 (252154) 448 iterations, returns at 243027
seed: 010101011011111001 (253371) 448 iterations, returns at 240761
seed: 010101100011001110 (254316) 448 iterations, returns at 246533
seed: 010101100100111111 (254477) 448 iterations, returns at 246175
seed: 010101110100001100 (256414) 448 iterations, returns at 132727
seed: 010101110111111010 (256772) 448 iterations, returns at 131763
seed: 010101110111111111 (256777) 448 iterations, returns at 131775
seed: 010101111101010011 (257523) 16 iterations, returns at 130105
seed: 010110000011010001 (260321) 448 iterations, returns at 136501
seed: 010110001111001101 (261715) 448 iterations, returns at 123531
seed: 010110011001111110 (263176) 448 iterations, returns at 121373
seed: 010110100000011000 (264030) 448 iterations, returns at 127717
seed: 010110100111011100 (264734) 448 iterations, returns at 125467
seed: 010110101110010111 (265627) 448 iterations, returns at 124315
seed: 010110110000000110 (266006) 448 iterations, returns at 113753
seed: 010110110100100011 (266443) 448 iterations, returns at 112645
seed: 010110111001101111 (267157) 448 iterations, returns at 111035
seed: 010111000011100100 (270344) 448 iterations, returns at 116447
seed: 010111001111000010 (271702) 448 iterations, returns at 103543
seed: 010111010010111001 (272271) 448 iterations, returns at 102561
seed: 010111011101100001 (273541) 448 iterations, returns at 100041
seed: 010111101100000001 (275401) 448 iterations, returns at 104741
seed: 010111110011100100 (276344) 448 iterations, returns at 172447
seed: 010111110101101100 (276554) 448 iterations, returns at 172027
seed: 010111111000111100 (277074) 448 iterations, returns at 171167
seed: 010111111101100011 (277543) 448 iterations, returns at 170045
seed: 011000000110101100 (300654) 448 iterations, returns at 176227
seed: 011000010001101010 (302152) 448 iterations, returns at 163023
seed: 011000010110101101 (302655) 448 iterations, returns at 162231
seed: 011000011000001010 (303012) 448 iterations, returns at 161723
seed: 011000011111001101 (303715) 448 iterations, returns at 167531
seed: 011001000000110001 (310061) 448 iterations, returns at 157601
seed: 011001101000001101 (315015) 448 iterations, returns at 145731
seed: 011001101001010111 (315127) 448 iterations, returns at 145115
seed: 011001110010000110 (316206) 448 iterations, returns at 033353
seed: 011001111000011110 (317036) 7 iterations, returns at 031673
seed: 011001111110101001 (317651) 448 iterations, returns at 030221
seed: 011010000100110111 (320467) 448 iterations, returns at 036615
seed: 011010001011010000 (321320) 448 iterations, returns at 034537
seed: 011010001111000101 (321705) 448 iterations, returns at 023551
seed: 011010010011100110 (322346) 448 iterations, returns at 022453
seed: 011010011010110001 (323261) 448 iterations, returns at 021201
seed: 011010011101000101 (323505) 448 iterations, returns at 020151
seed: 011010011111100010 (323742) 448 iterations, returns at 027443
seed: 011010100110100010 (324642) 448 iterations, returns at 026243
seed: 011010101000001110 (325016) 448 iterations, returns at 025733
seed: 011010110000010100 (326024) 448 iterations, returns at 013707
seed: 011011000011100001 (330341) 448 iterations, returns at 016441
seed: 011011011001000110 (333106) 448 iterations, returns at 001153
seed: 011011011101110010 (333562) 448 iterations, returns at 000003
seed: 011011100010101000 (334250) 448 iterations, returns at 007257
seed: 011011110000010111 (336027) 448 iterations, returns at 073715
seed: 011011110110110100 (336664) 4 iterations, returns at 072207
seed: 011011111110101010 (337652) 448 iterations, returns at 070223
seed: 011100000011011110 (340336) 448 iterations, returns at 076473
seed: 011100000110001010 (340612) 448 iterations, returns at 076323
seed: 011100001010101010 (341252) 448 iterations, returns at 075223
seed: 011100001100111101 (341475) 448 iterations, returns at 074171
seed: 011100010110000111 (342607) 448 iterations, returns at 062355
seed: 011100011110000100 (343604) 448 iterations, returns at 060347
seed: 011100100000001000 (344010) 5 iterations, returns at 067757
seed: 011100100111000110 (344706) 448 iterations, returns at 065553
seed: 011100101010011110 (345236) 448 iterations, returns at 065273
seed: 011100110111000110 (346706) 16 iterations, returns at 051553
seed: 011100111001011001 (347131) 448 iterations, returns at 051061
seed: 011100111101001000 (347510) 448 iterations, returns at 050157
seed: 011101000000011101 (350035) 448 iterations, returns at 057671
seed: 011101011001111010 (353172) 448 iterations, returns at 041363
seed: 011101011111000000 (353700) 448 iterations, returns at 047577
seed: 011101101001111011 (355173) 448 iterations, returns at 045365
seed: 011101101101111011 (355573) 448 iterations, returns at 044365
seed: 011101101110110110 (355666) 448 iterations, returns at 044213
seed: 011101110010101010 (356252) 448 iterations, returns at 733224
seed: 011110000000001010 (360012) 7 iterations, returns at 737724
seed: 011110010111111100 (362774) 448 iterations, returns at 721770
seed: 011110011001110110 (363166) 448 iterations, returns at 721014
seed: 011110011010100011 (363243) 448 iterations, returns at 721246
seed: 011110011010110100 (363264) 448 iterations, returns at 721210
seed: 011110100111010100 (364724) 448 iterations, returns at 725510
seed: 011110101011001110 (365316) 448 iterations, returns at 724534
seed: 011110110000111110 (366076) 448 iterations, returns at 713174
seed: 011110110110100011 (366643) 448 iterations, returns at 712246
seed: 011111000010100010 (370242) 448 iterations, returns at 717244
seed: 011111001001110110 (371166) 448 iterations, returns at 715014
seed: 011111010010010011 (372223) 448 iterations, returns at 703306
seed: 011111010110110111 (372667) 448 iterations, returns at 702216
seed: 011111011011001101 (373315) 448 iterations, returns at 700532
seed: 011111011110000111 (373607) 448 iterations, returns at 700356
seed: 011111100000111111 (374077) 448 iterations, returns at 707176
seed: 011111100101010110 (374526) 448 iterations, returns at 706114
seed: 011111110010000001 (376201) 448 iterations, returns at 773342
seed: 011111110111001010 (376712) 448 iterations, returns at 771524
seed: 011111111010010001 (377221) 448 iterations, returns at 771302
seed: 011111111110011001 (377631) 16 iterations, returns at 770262
seed: 011111111111100001 (377741) 448 iterations, returns at 777442
seed: 100000001101001101 (401515) 448 iterations, returns at 774132
seed: 100000011100000101 (403405) 448 iterations, returns at 760752
seed: 100000011101110011 (403563) 448 iterations, returns at 760006
seed: 100000011111111001 (403771) 448 iterations, returns at 767762
seed: 100000100101011100 (404534) 448 iterations, returns at 766070
seed: 100001000111011000 (410730) 8 iterations, returns at 755460
seed: 100001001101000011 (411503) 448 iterations, returns at 754146
seed: 100001100110010000 (414620) 448 iterations, returns at 746300
seed: 100001110100010000 (416420) 448 iterations, returns at 632700
seed: 100001111001101001 (417151) 16 iterations, returns at 631022
seed: 100001111011101011 (417353) 448 iterations, returns at 630426
seed: 100001111101011101 (417535) 448 iterations, returns at 630072
seed: 100010000000110001 (420061) 448 iterations, returns at 637602
seed: 100010010110110010 (422662) 448 iterations, returns at 622204
seed: 100010100110001101 (424615) 448 iterations, returns at 626332
seed: 100010101110000100 (425604) 448 iterations, returns at 624350
seed: 100010110000001010 (426012) 448 iterations, returns at 613724
seed: 100010110101011001 (426531) 448 iterations, returns at 612062
seed: 100010111111001010 (427712) 448 iterations, returns at 617524
seed: 100011000101101010 (430552) 448 iterations, returns at 616024
seed: 100011000110111010 (430672) 5 iterations, returns at 615564
seed: 100011010101111111 (432577) 448 iterations, returns at 602376
seed: 100011010110101101 (432655) 448 iterations, returns at 602232
seed: 100011100101101000 (434550) 448 iterations, returns at 606020
seed: 100011100111110101 (434765) 448 iterations, returns at 605412
seed: 100011111100101000 (437450) 448 iterations, returns at 670620
seed: 100100000001001011 (440113) 448 iterations, returns at 677126
seed: 100100001001001111 (441117) 448 iterations, returns at 675136
seed: 100100111101111011 (447573) 448 iterations, returns at 650366
seed: 100101000111001010 (450712) 448 iterations, returns at 655524
seed: 100101001001000011 (451103) 448 iterations, returns at 655146
seed: 100101011100110001 (453461) 448 iterations, returns at 640602
seed: 100101011101001111 (453517) 448 iterations, returns at 640136
seed: 100101011110010011 (453623) 448 iterations, returns at 640306
seed: 100101100001111000 (454170) 448 iterations, returns at 647360
seed: 100101100010110000 (454260) 448 iterations, returns at 647200
seed: 100101100011010011 (454323) 448 iterations, returns at 646506
seed: 100101100111000110 (454706) 448 iterations, returns at 645554
seed: 100101100111110111 (454767) 448 iterations, returns at 645416
seed: 100101110111100100 (456744) 448 iterations, returns at 531450
seed: 100101111110000100 (457604) 448 iterations, returns at 530350
seed: 100110001010010111 (461227) 448 iterations, returns at 535316
seed: 100110011010010010 (463222) 448 iterations, returns at 521304
seed: 100110011111110110 (463766) 8 iterations, returns at 527414
seed: 100110101001011001 (465131) 448 iterations, returns at 525062
seed: 100111000000000010 (470002) 448 iterations, returns at 517744
seed: 100111010001100001 (472141) 448 iterations, returns at 503042
seed: 100111010110011010 (472632) 448 iterations, returns at 502264
seed: 100111100001100110 (474146) 448 iterations, returns at 507054
seed: 100111100100001111 (474417) 448 iterations, returns at 506736
seed: 100111101111100011 (475743) 448 iterations, returns at 573446
seed: 101000010010110100 (502264) 448 iterations, returns at 563210
seed: 101000011000100010 (503042) 448 iterations, returns at 561644
seed: 101000011011101101 (503355) 4 iterations, returns at 560432
seed: 101000101011000101 (505305) 448 iterations, returns at 564552
seed: 101000110111011110 (506736) 448 iterations, returns at 551474
seed: 101000111000101100 (507054) 448 iterations, returns at 551630
seed: 101000111011110001 (507361) 448 iterations, returns at 550402
seed: 101000111101011001 (507531) 448 iterations, returns at 550062
seed: 101001111011000101 (517305) 448 iterations, returns at 430552
seed: 101001111100011101 (517435) 5 iterations, returns at 430672
seed: 101001111111100100 (517744) 448 iterations, returns at 437450
seed: 101010001011000100 (521304) 448 iterations, returns at 434550
seed: 101010010100011001 (522431) 448 iterations, returns at 422662
seed: 101010100000010101 (524025) 448 iterations, returns at 427712
seed: 101010100101110101 (524565) 448 iterations, returns at 426012
seed: 101010101000110010 (525062) 448 iterations, returns at 425604
seed: 101010111100001100 (527414) 8 iterations, returns at 410730
seed: 101011000011101000 (530350) 448 iterations, returns at 416420
seed: 101011001100101000 (531450) 448 iterations, returns at 414620
seed: 101011101011001110 (535316) 448 iterations, returns at 404534
seed: 101011110100101101 (536455) 448 iterations, returns at 472632
seed: 101011111101110001 (537561) 448 iterations, returns at 470002
seed: 101100001101000011 (541503) 448 iterations, returns at 474146
seed: 101100001111111011 (541773) 8 iterations, returns at 463766
seed: 101100010010101001 (542251) 448 iterations, returns at 463222
seed: 101100111100010101 (547425) 448 iterations, returns at 450712
seed: 101101000000110010 (550062) 448 iterations, returns at 457604
seed: 101101000100000010 (550402) 448 iterations, returns at 456744
seed: 101101001100010011 (551423) 448 iterations, returns at 454706
seed: 101101001100111100 (551474) 448 iterations, returns at 454170
seed: 101101001110011000 (551630) 448 iterations, returns at 454260
seed: 101101101110111001 (555671) 448 iterations, returns at 333562
seed: 101101110001010011 (556123) 448 iterations, returns at 333106
seed: 101110000000100101 (560045) 448 iterations, returns at 337652
seed: 101110000100011010 (560432) 4 iterations, returns at 336664
seed: 101110001110100100 (561644) 448 iterations, returns at 334250
seed: 101110010000000001 (562001) 448 iterations, returns at 323742
seed: 101110010110000011 (562603) 448 iterations, returns at 322346
seed: 101110011010001000 (563210) 448 iterations, returns at 321320
seed: 101110100101101010 (564552) 448 iterations, returns at 326024
seed: 101110101001110111 (565167) 448 iterations, returns at 325016
seed: 101110101100100001 (565441) 448 iterations, returns at 324642
seed: 101111000001101111 (570157) 7 iterations, returns at 317036
seed: 101111000110110011 (570663) 448 iterations, returns at 316206
seed: 101111010001110101 (572165) 448 iterations, returns at 303012
seed: 101111010101000101 (572505) 448 iterations, returns at 302152
seed: 101111011100100110 (573446) 448 iterations, returns at 300654
seed: 101111111000111011 (577073) 448 iterations, returns at 371166
seed: 101111111110100001 (577641) 448 iterations, returns at 370242
seed: 110000000100010101 (600425) 448 iterations, returns at 376712
seed: 110000001011001011 (601313) 448 iterations, returns at 374526
seed: 110000010000111011 (602073) 448 iterations, returns at 363166
seed: 110000010010011010 (602232) 448 iterations, returns at 363264
seed: 110000010011111110 (602376) 448 iterations, returns at 362774
seed: 110000011101110101 (603565) 7 iterations, returns at 360012
seed: 110000100101011111 (604537) 448 iterations, returns at 366076
seed: 110000101010010111 (605227) 448 iterations, returns at 365316
seed: 110000101100001010 (605412) 448 iterations, returns at 364724
seed: 110000110000010000 (606020) 448 iterations, returns at 353700
seed: 110000110000111101 (606075) 448 iterations, returns at 353172
seed: 110001000110100101 (610645) 448 iterations, returns at 356252
seed: 110001001000011011 (611033) 448 iterations, returns at 355666
seed: 110001010000110010 (612062) 448 iterations, returns at 343604
seed: 110001011010100101 (613245) 448 iterations, returns at 341252
seed: 110001011100110101 (613465) 448 iterations, returns at 340612
seed: 110001011110001111 (613617) 448 iterations, returns at 340336
seed: 110001011111010100 (613724) 448 iterations, returns at 347510
seed: 110001100100010011 (614423) 16 iterations, returns at 346706
seed: 110001101010101111 (615257) 448 iterations, returns at 345236
seed: 110001101100010011 (615423) 448 iterations, returns at 344706
seed: 110001101101110100 (615564) 5 iterations, returns at 344010
seed: 110001110000010100 (616024) 448 iterations, returns at 233710
seed: 110001111011000001 (617301) 448 iterations, returns at 230542
seed: 110001111101010100 (617524) 448 iterations, returns at 230110
seed: 110010000000010001 (620021) 448 iterations, returns at 237702
seed: 110010010010000100 (622204) 448 iterations, returns at 223350
seed: 110010010110010101 (622625) 448 iterations, returns at 222312
seed: 110010100011101000 (624350) 448 iterations, returns at 226420
seed: 110010110011011010 (626332) 448 iterations, returns at 212464
seed: 110010111001011111 (627137) 448 iterations, returns at 211076
seed: 110010111100011111 (627437) 448 iterations, returns at 210676
seed: 110011000000111010 (630072) 448 iterations, returns at 217164
seed: 110011000100010110 (630426) 448 iterations, returns at 216714
seed: 110011001000010010 (631022) 16 iterations, returns at 215704
seed: 110011010110100001 (632641) 448 iterations, returns at 202242
seed: 110011010111000000 (632700) 448 iterations, returns at 201540
seed: 110011101011100001 (635341) 448 iterations, returns at 204442
seed: 110011111000010001 (637021) 448 iterations, returns at 271702
seed: 110011111110000010 (637602) 448 iterations, returns at 270344
seed: 110100000001011110 (640136) 448 iterations, returns at 277074
seed: 110100000011000110 (640306) 448 iterations, returns at 276554
seed: 110100000110000010 (640602) 448 iterations, returns at 276344
seed: 110100010000111111 (642077) 448 iterations, returns at 263176
seed: 110100100101110011 (644563) 448 iterations, returns at 266006
seed: 110100101100001110 (645416) 448 iterations, returns at 264734
seed: 110100101101101100 (645554) 448 iterations, returns at 264030
seed: 110100110101000110 (646506) 448 iterations, returns at 252154
seed: 110100111010000000 (647200) 448 iterations, returns at 251340
seed: 110100111011110000 (647360) 448 iterations, returns at 250400
seed: 110101000011110110 (650366) 448 iterations, returns at 256414
seed: 110101000011111101 (650375) 448 iterations, returns at 256772
seed: 110101001110010111 (651627) 448 iterations, returns at 254316
seed: 110101100000010101 (654025) 16 iterations, returns at 247712
seed: 110101101001100110 (655146) 448 iterations, returns at 245054
seed: 110101101101010100 (655524) 448 iterations, returns at 244110
seed: 110101101110000001 (655601) 448 iterations, returns at 244342
seed: 110101111000011101 (657035) 448 iterations, returns at 131672
seed: 110101111110110011 (657663) 448 iterations, returns at 130206
seed: 110110100100001001 (664411) 448 iterations, returns at 126722
seed: 110110110011111111 (666377) 8 iterations, returns at 112776
seed: 110110110100001001 (666411) 448 iterations, returns at 112722
seed: 110110111011010101 (667325) 448 iterations, returns at 110512
seed: 110111000110010000 (670620) 448 iterations, returns at 116300
seed: 110111010011011001 (672331) 448 iterations, returns at 102462
seed: 110111011101001111 (673517) 448 iterations, returns at 100136
seed: 110111100100000101 (674405) 448 iterations, returns at 106752
seed: 110111101001011110 (675136) 448 iterations, returns at 105074
seed: 110111111001010110 (677126) 448 iterations, returns at 171114
seed: 111000000011101110 (700356) 448 iterations, returns at 176434
seed: 111000000100100101 (700445) 448 iterations, returns at 176652
seed: 111000000101011010 (700532) 448 iterations, returns at 176064
seed: 111000010010001110 (702216) 448 iterations, returns at 163334
seed: 111000011011000110 (703306) 448 iterations, returns at 160554
seed: 111000101111001011 (705713) 448 iterations, returns at 153526
seed: 111000110000011111 (706037) 448 iterations, returns at 153676
seed: 111000110001001100 (706114) 448 iterations, returns at 153130
seed: 111000111000110101 (707065) 448 iterations, returns at 151612
seed: 111000111001111110 (707176) 448 iterations, returns at 151374
seed: 111001010010100110 (712246) 448 iterations, returns at 143254
seed: 111001011001110001 (713161) 448 iterations, returns at 141002
seed: 111001011001111100 (713174) 448 iterations, returns at 141370
seed: 111001100001001001 (714111) 448 iterations, returns at 147122
seed: 111001100101100101 (714545) 448 iterations, returns at 146052
seed: 111001101000001100 (715014) 448 iterations, returns at 145730
seed: 111001110010101011 (716253) 448 iterations, returns at 033226
seed: 111001110011001111 (716317) 448 iterations, returns at 032536
seed: 111001111010000101 (717205) 16 iterations, returns at 031352
seed: 111001111010100100 (717244) 448 iterations, returns at 031250
seed: 111001111100000101 (717405) 448 iterations, returns at 030752
seed: 111010001000001100 (721014) 448 iterations, returns at 035730
seed: 111010001010001000 (721210) 448 iterations, returns at 035320
seed: 111010001010100110 (721246) 448 iterations, returns at 035254
seed: 111010001111111000 (721770) 448 iterations, returns at 023760
seed: 111010100001000111 (724107) 448 iterations, returns at 027156
seed: 111010100101011100 (724534) 448 iterations, returns at 026070
seed: 111010101101001000 (725510) 448 iterations, returns at 024120
seed: 111011011010010100 (733224) 448 iterations, returns at 001310
seed: 111011101100010101 (735425) 448 iterations, returns at 004712
seed: 111011111111010100 (737724) 7 iterations, returns at 077510
seed: 111011111111101101 (737755) 448 iterations, returns at 077432
seed: 111100110011000000 (746300) 448 iterations, returns at 052540
seed: 111100111101101101 (747555) 448 iterations, returns at 050032
seed: 111101001010011101 (751235) 448 iterations, returns at 055272
seed: 111101100001100110 (754146) 448 iterations, returns at 047054
seed: 111101101100110000 (755460) 8 iterations, returns at 044600
seed: 111110000000000110 (760006) 448 iterations, returns at 737755
seed: 111110000111101010 (760752) 448 iterations, returns at 735425
seed: 111110101101010011 (765523) 448 iterations, returns at 724107
seed: 111110110000111000 (766070) 448 iterations, returns at 713161
seed: 111110111111110010 (767762) 448 iterations, returns at 717405
seed: 111111000010110010 (770262) 16 iterations, returns at 717205
seed: 111111000110010111 (770627) 448 iterations, returns at 716317
seed: 111111000110100101 (770645) 448 iterations, returns at 716253
seed: 111111001011000010 (771302) 448 iterations, returns at 714545
seed: 111111001101010100 (771524) 448 iterations, returns at 714111
seed: 111111011011100010 (773342) 448 iterations, returns at 700445
seed: 111111100001011010 (774132) 448 iterations, returns at 707065
seed: 111111100101101111 (774557) 448 iterations, returns at 706037
seed: 111111101000010101 (775025) 448 iterations, returns at 705713
seed: 111111111100100010 (777442) 448 iterations, returns at 770645
seed: 111111111100101011 (777453) 448 iterations, returns at 770627
seed: 111111111111100101 (777745) 448 iterations, returns at 777453
seed: 111111111111111111 (777777) >500 (777777 = minus zero)
This behavior is really interesting: There are 41 seed values with a rather short period, while all the other ones (except negative zero) loop over exactly 448 iterations. Moreover, there’s one particular seed value, which will result in a period of just 1, meaning, it will produce the same value over and over again, namely octal 225241
. Notably, this is also a valid PDP-1 instruction, as in "lio 5241
" (load contents of location 5241 into the IO register). — Do we have a viable candidate? Was this, why the code was changed?
(Note: Spacewar 2B by its constant of 335671
produces a shorter period of 144,760 distinctive values, still plenty.)
Unexpected Answers
Time to ask those, who ought to know. So I presented the question, “Did the sun stop spinning in early versions of Spacewar?” (along with a brief explanation) to Lyle Bickley (CHM PDP-1 Restoration Team), who — as always eager to help — forwarded it in turn to those, who really ought to know, namely Steve Russell and Peter Samson.
While Steve Russell didn’t recall any particulars about this (I guess, if there had been such an error, he surely would remember this), Peter Samson didn’t have much to say on the particular question, as well, but shared some really helpful insights. As it turns out — and often it happens to do so — everything is completely different.
Actually, I wasn’t far off assuming there being a bit more complex history to this macro. According to Peter Samson, it comes from the TX-0, and he also directed to a particular memo, TX-0 Computer, M-5001-19-1, “Some Useful Micro- and Macro-Instructions” (MIT, Aug. 23, 1961), where we find this very macro on page 4.
Now, Wes Clark’s TX-0 (built in oversight of further DEC founder Ken Olsen) was an 18-bit machine with a 16-bit address bus. If you do the math, there are just 2 bits left for any instruction codes. Therefor, the TX-0 relied heavily on micro-programming, meaning, any of the few instructions would parse bits of the address part in a fixed sequence, in order to apply specific operations according to those bits. Since the sequence was fixed, these micro-instructions could be combined in a sensible manner, by simply adding them into a compound instruction word. (The PDP-1 does this as well for some instructions, e.g., the shift and operating instruction group, which "rar
" is a member of, was implemented in this fashion.) As a side effect of this, complex instructions were available as simple atoms of partial operations.
An example of this was the addition operation, which consists of a basic addition step without any carry (AKA partial add, or short “pad”), followed by a second step applying the ripple carry in another step. (Both were derived individually from the contents of AC and the “live register” [LR] holding the operand.) Therefor, there was "add
", performing the partial addition (same as XOR), and "cry
" performing the carry step.
Now we also get an idea, where this peculiar carry-only algorithm for scrambling came from, namely the "cry
" instruction of the TX-0! (This was also particularly pointed out by Peter Samson.)
Here is the source code for the TX-0, as printed in the memo (p. 4):
The instruction ran (= cry+cry-opr), when used with an appropriate number in the live register, can be used to produce a sequence of pseudo-random numbers. In particular, the instructions cla add t llr (355670 ran sto t can be used repeatedly to generate a sequence of approximately 250,000 random 18-bit words in register t (eventually, a previous computed value is obtained and the sequence repeats). Plus zero is a good choice for the initial contents of t. Minus zero should not be used.
Now, this may need some further explanations:
As we have seen, micro-instructions may be combined in complex instructions, so-called macro-instructions. Micro-instructions are ordered in groups, which are the parent instructions. Commonly, the assembler assigns both the instruction code of the group and the one for the particular micro instructions to those symbols, meaning, if we combine them, we have to substract the code of the group, otherwise the value will overflow. Therefor, when combining instructions "cry+cry
" we have to substract the group code "opr
" (for operation group), comprised in each of them, once. However, in this particular case it isn’t about simple micro-programming, since by adding the same instruction twice, the instruction won’t be performed twice, but rather result in a unique instruction code, in this case "ran
", which apparently combines both the carry and a rotate/shift operation in a dedicated RNG step.
(Note: Quite similarly, in early PDP-1s, before the automatic hardware multiply/divide became standard, there were dedicated instructions for multiply and divide steps, each combining a partial operation with a shift operation.)
As for the other instructions, "cla
" clears the accumulator and "add
" (here without cry
) is used to load a value into the accumulator. (Since we cleared it before, adding to it will just transfer the value and no carry may arise.) "llr
" (load live register) and "sto
" (store) should be straight forward.
It is the same code as the one found in Spacewar 3.1, but written for an other instruction set. Like the code in Spacewar 3.1, it uses the constant 355670
and a static label. (While the TX-0 uses a label "t
", Spacewar rather uses the TX-0 instruction code "ran
" in some kind of cargo cult.) Counter-intuitively, the later version is more true to the original than the earlier one in Spacewar 2B.
With all questions solved regarding Spacewar 3.1, we‘re left with another one: Why was this different in Spacewar 2B?
Maybe, the constant 335671
(binary 011011101110111001
) was found useful for its more symmetrical bit pattern, also applying some scrambling to the lowest 3 bits, but eventually rejected for lesser properties, like its shorter period? Notably, the automatic assembler variable "\ran
" ist still uninitialized in Spacewar 2B, leaving the seed value to chance. Considering the shorter period and a third of the total range of 18-bit values being outside the set produced by this RGN with seed 0
, there may have been some concerns. (Especially, if we compare this with the behavior of out-of-sequence seeds with the variant used in Spacewar 3.1.) Maybe, this was, why the programmer(s) returned to the original form as found in the TX-0 memo? — We don’t know.
However, we are warned to infer too much from too little, which is really the moral of this story.
There is famous quote by Bert Brecht, found in the epilogue of The Good Person of Szechwan.
In German, it’s
Wir stehen selbst enttäuscht und sehn betroffen
Den Vorhang zu und alle Fragen offen.
often reduced to the second line, meaning, “[we see] the curtain closed and all questions [remaining] open”. Great to be used on any occasions, where there is a cliff-hanger without a follow-up or pay-off, thus just a bare cliff.
(The official English version isn‘t really as suitable for this, being, “With consternation / We see the curtain closed, the plot unended.”)
Discuss/comment on Google+…
Disscuss/comment on Hacker News… (Oops, HN front page.)