Thank you again! I love how you tokenize code over a serial cable - if I understand that correctly, it means the computer that will have the compiled code doesn’t need to have the source code in memory at the same time; that’s the role of the other computer. This may imply that we’d have at least a bit more space to play with, both on the source code side and on the resulting tokenized side. Brilliant!
The YouTube video that I made is not so much about the code or documentation about the program per se, but rather it is an excessively long demo on how to use the resulting program. Put another way, the video does not require the viewer to know anything about coding or how to read source code. Instead, it attempts to demo and illustrate how to use the various dice mechanic systems it provides. Cheers and again, thanks! Steve -- Greetings from Steve Baker (he/him/his) “Gravity brings me down…” > On Feb 13, 2024, at 9:10 PM, B 9 <[email protected]> wrote: > > You have a video for documentation? That rocks. You could show a youtu.be > <http://youtu.be/> link when people hit '?'. (Heck, you could even show a QR > code to the video using Reverse Video spaces for black squares. Of course, > that'd require a very clever compression algorithm.) > > You're right about the space crunch. I tokenized (compiled) DICE.DO over a > serial cable to my Tandy 200 using LOAD "COM:98NE1". On the PC side I sent > the .do file at 19200 and then hit Ctrl-Z. I was glad it ran at all since the > max ram for a T200 program is only 24KB. > > --b9 > > P.S. While I am biased toward developing on the Model T, I also wrote a > script which can do the tokenization (convert .DO to .BA) on a UNIX box. > https://github.com/hackerb9/tokenize > > > On Tuesday, February 13, 2024, Steve Baker <[email protected] > <mailto:[email protected]>> wrote: > > Thank you so very much! :-) > > This listserv continues to be amazing — the technical knowledge, the desire > > to explore, the willingness to share… it’s so helpful! > > Regarding the Dice Box app, I did make an excessively long, self-absorbed > > video on how to use it (I even did the music myself, LOL) … > > https://youtu.be/mIptQC7AHac > > > > It’d be awesome to add some pizazz to the app yet I’m not sure if there’s > > any memory remaining. As it stands now, in my maxed-out T102s I can barely > > compile it with only the source file in memory and if I add a few more > > bytes it’ll be unable to compile. Maybe there’s some different ways to > > compile the app so the memory issue won’t be an issue? Hmmm… > > Cheers and again, thanks, > > Steve > > > > -- > > Greetings from Steve Baker (he/him/his) > > “Gravity brings me down…” > > > > > > > > On Feb 13, 2024, at 4:25 PM, B 9 <[email protected] > > <mailto:[email protected]>> wrote: > > Oh, now I feel silly. I hadn't known what "Standard Expression" meant. It > > sounds like you've already implemented exactly what I was imagining and I > > just didn't recognize it. > > > > With that wish fulfilled in advance, I think Dice Box is well-nigh perfect. > > The only possible other things one could add to Dice Box would be frills: > > fancy graphics, a help system, or F-key labels for common rolls. None of > > which Dice Box actually needs, but might be fun. > > > > Ken Petit: Is your AsciiPixels graphics library fast enough to animate a > > small sprite, like a die tumbling across the screen? > > > > --b9 > > > > On Tuesday, February 13, 2024, Steve Baker <[email protected] > > <mailto:[email protected]>> wrote: > >> Hello again! :-) > >> No worries at all, and thanks for your thoughtful feedback and > >> suggestions! The “simple mode” idea is a good one… I will fire up a > >> background process to think about that a bit more. > >> Currently, to do a simple roll the user selects menu option 1 (standard > >> expression) and then types “d20” or “6d6” and gets the result. Hitting > >> enter after that first roll will perform another roll of the same kind > >> (e.g., d20 or 6d6). In effect, if one were to pass the Model T to the next > >> player, all they’d need to do is press enter to perform the same roll, etc. > >> Cheers, > >> Steve > >> > >> -- > >> Greetings from Steve Baker (he/him/his) > >> “Gravity brings me down…” > >> > >> > >> > >> On Feb 13, 2024, at 3:55 AM, B 9 <[email protected] > >> <mailto:[email protected]>> wrote: > >> (Oops, I know your name is "Steve". I don't know why I typed "Steven".) > >> > >> On Tue, Feb 13, 2024 at 12:53 AM B 9 <[email protected] > >> <mailto:[email protected]>> wrote: > >>> > >>> Thanks for the walkthrough, Steven! While adding in the fast counters > >>> wouldn't hurt (probably), I don't think Dice Box needs algorithmic > >>> improvement now that I understand how it works. > >>> > >>> First, it's pretty clever to get more randomness from the users at each > >>> dice roll (`ZX`), not kludgy at all. > >>> > >>> Second, you used the extra randomness in the right way. My first instinct > >>> would have been to reseed the Random Number Generator (RNG) as Anderson > >>> suggests — using `POKE` or with `RND(-`_x_``)` — but that would have > >>> actually made the dice rolls *less* uniformly distributed because it > >>> depends upon the distribution of your time measurements. (Seeding an RNG > >>> should only be done once in a program.) > >>> > >>> My second instinct would have also been wrong: modify the result from > >>> `RND(1)` algebraically or using modulo arithmetic. Again, that would skew > >>> the distribution unless extremely carefully programmed. You simply used > >>> the time interval to skip ahead in the RNG by a few steps, which is > >>> perfectly valid and leverages Bill Gates' hard work of building a > >>> mathematically correct RNG. > >>> > >>> The only algorithmic improvement I can think of would be to use Lloyd's > >>> method of calling RND(1) in a loop with `INKEY$()` on the splash screen. > >>> That would (perhaps) help as it increments the RNG by about 80 steps per > >>> second, giving more possible starting states for the first die roll than > >>> measurements in seconds. > >>> > >>> In fact, you could skip measuring seconds all together if you replaced > >>> `INPUT$(1)` with a loop like, > >>> > >>> Q=RND(1): IF INKEY$="" GOTO 10 > >>> > >>> —B > >>> P.S. If you do start fixing up Dice Box, one feature which I'd personally > >>> use would be the option for a "simple mode" instead of picking a > >>> "system". I imagine a prompt where one could type "d20" to roll a > >>> twenty-sided die, "6d6" to roll 6 six-sided dice, or "d1" to flip a coin. > >>> (Hitting enter would roll the same dice again until something different > >>> is typed. Q to quit.) Or maybe it would be menu driven so one could pass > >>> the Model T to a player and ask them to press a key to roll the dice. The > >>> exact time they hit the key determines the result. > >>> > >>> > >>> On Mon, Feb 12, 2024 at 1:07 PM Steve Baker <[email protected] > >>> <mailto:[email protected]>> wrote: > >>>> > >>>> Greetings again! > >>>> > >>>> Thanks for your note (below, last week) about my RPG dice roller program > >>>> and for the insightful follow-up about the Model T randomizer > >>>> capabilities! I am not a particularly well-versed programmer and > >>>> undertook the dice project to (a) goof around with the Model T, (b) > >>>> familiarize myself with how to code it, and (c) to have something cool > >>>> to take with me on game nights! Through that lens, I had a lot of fun > >>>> and was able to accomplish what I set out to do. > >>>> As you suspected, I did indeed find my inspiration for the randomizer > >>>> function from the excellent "Programming Tips, Peeks, and Pokes” book by > >>>> Tony Anderson; the specific section that is most relevant here is > >>>> Chapter 13, document pages 29-31 (in the PDF, it is pages 31-33): > >>>> > >>>> https://archive.org/details/ProgrammingTipsPeeksAndPokesForTheTandyPortableComputers/page/n31/mode/2up > >>>> > >>>> In particular, this paragraph was helpful for me (from around the middle > >>>> of page 30): > >>>> > >>>> You can "reseed" the random number generator yourself at the beginning > >>>> of the program by asking for the user to choose a number between 1 and > >>>> 100, or by using the seconds digits from the clock, or by reading one of > >>>> the two running counters in high memory, then poking the value into the > >>>> memory seed location at 64634 (100 and 102) or 63277 (200). > >>>> > >>>> My approach only extended this by reseeding things based on the clock’s > >>>> seconds digits when the user interacted with the app. (In hindsight, I > >>>> could’ve explored those two running counters in high memory instead of, > >>>> or in addition to, the seconds digits). My loose logic was that they’d > >>>> likely be unable to pull off two dice rolls within the same second, as > >>>> it takes them a bit of time to choose what to roll, watch the results, > >>>> do whatever they’re going to do with the results (e.g., playing RPGs), > >>>> etc. > >>>> > >>>> Here’s the current version of my DICE.DO source code: > >>>> > >>>> http://www.club100.org/memfiles/index.php?action=downloadfile&filename=DICE.DO&directory=Steve%20Baker& > >>>> > >>>> Let’s start with the important bits from the above source code that show > >>>> how I chose to leverage the tips from Tony’s book. Basically it’s a > >>>> three-part play: > >>>> 1. First, a one-time step to setup a variable ZZ with the current > >>>> seconds digits when the code first runs > >>>> 2. As the user interacts with the program, it resets a second variable > >>>> ZY to the current seconds digits — my assumption was that ZY will be > >>>> different from ZZ at least most of the time, as there’s of course a > >>>> chance it could coincidentally be the same seconds digits as ZZ on > >>>> occasion. > >>>> 3. Finally, a third variable ZX fetches the current seconds digits > >>>> again when we’re actually rolling a dice — it resets every time a dice > >>>> is rolled, which could be several times (for instance, a 3d20 request > >>>> triggers this routine three separate times to roll the d20 dice once > >>>> each time, etc.) > >>>> So, my use of the randomizer is three-deep, in that we have ZZ that is > >>>> set once, ZY that is set when they use the menu, and ZX that is set with > >>>> each dice roll. I put the code the does the heavy lifting toward the > >>>> front of the program so it’s faster to access during runtime, etc. The > >>>> following elaborates on those three steps with some actual code snippets. > >>>> * First step — one-time initialization of variable ZZ (via the > >>>> “GOSUB8900" call) > >>>> There’s a big chunk of variable initialization stuff at the end of the > >>>> code (I read that having the “only use once” initialization stuff at the > >>>> back of the program is more efficient). That chunk begins on line 8900. > >>>> First, ZZ gets the seconds digits from the clock when the app > >>>> initializes: > >>>> 8900ZZ=VAL(RIGHT$(TIME$,2)): … the other initialization stuff > >>>> * Second step — setting variable ZY with each menu interaction (via the > >>>> “GOSUB45” call) > >>>> When we search for “GOSUB45” in the code, we see that it is executed > >>>> when the user interacts with the menu, e.g. makes a selection from the > >>>> set of choices. > >>>> 45ZY=VAL(RIGHT$(TIME$,2)):RETURN > >>>> * Third step — the loop that sets variable ZX and uses the three > >>>> variable in a loop to actually pluck out a number (via the “GOSUB42” > >>>> call) > >>>> When we search for “GOSUB42” in the code, we see that it is executed for > >>>> each roll of the dice. In the subroutine, LZ is the loop index and Q is > >>>> used to pop the randomizer function to burn off some quantity of random > >>>> numbers (we don’t keep Q). The global variable DI is set before the roll > >>>> with the number of sides for the dice (e.g., DI=10 for a d10 dice, 12 > >>>> for a d12, etc.). The global variable RL is short for “roll” as in dice > >>>> roll result, and this subroutine modifies that variable with the > >>>> resulting roll. > >>>> 42ZX=VAL(RIGHT$(TIME$,2)):FORLZ=1TO(ZX+ZZ+(RND(1)*ZY)):Q=RND(1):NEXTLZ:IFDI>1THENRL=INT(RND(1)*DI)+1:RETURN > >>>> 43IFRND(1)>=0.5THENRL=1:RETURNELSERL=0:RETURN > >>>> … and line 43 is used for dice rolls that are either 1 or 0 as in true > >>>> or false or hit/miss (those absolute times when it is either yes or no). > >>>> We get to line 43 when the final if statement in line 42, where it looks > >>>> to see if DI is greater than one, fails. Put another way, when DI is set > >>>> to 1 then we’re looking for a d1 roll. In this situation line 43 looks > >>>> at the random number and if it’s greater than or equal to 0.5 then it’s > >>>> yes/true/success/etc., and if it’s less than 0.5 then it’s > >>>> no/false/failure. > >>>> Line 42 is not particularly efficient and I figured that’s alright > >>>> because my use of the randomizer is heavily dependent on the seconds > >>>> digits as well on as the user themselves. My intent was not to > >>>> intentionally stall for time, so to speak, but “good" numbers over > >>>> lighting-fast responses. > >>>> One interesting thought is that by depending on the user’s interaction > >>>> to finalize the random number, it replicates the act of them shaking the > >>>> dice. At least to me, the vision of them influencing the selection of > >>>> their random number is like they are basically shaping their destiny, > >>>> LOL. > >>>> Looking back on all this with a few years’ perspective, perhaps hitting > >>>> those two running counters (maybe with grabbing the seconds digits a > >>>> time or two) might be simple and efficient. One of these days (years?) I > >>>> may revisit the code to streamline it a bit now that I know a lot more. > >>>> Given all this, I’m sure there’s probably a way to instrument something > >>>> to test the RNG uniformity via chi squared or equivalent tests. Just not > >>>> sure where or how to put that into the code, given this kludgy > >>>> three-part algorithm is quite user-dependent (their actions trigger most > >>>> of it). > >>>> My apologies for the delay in this response (I’d forgotten about the > >>>> football stuff this past weekend and I ended up having plans both days). > >>>> Hopefully this helps a little bit at least? It was fun to revisit the > >>>> code after so long (well, four years at least) and the memories of > >>>> writing it. > >>>> Cheers and thanks, > >>>> Steve > >>>> PS, I am still (very slowly) photographing and inventorying my T102s, > >>>> T200s, T600s, peripherals, and the like. to offer for sale to this > >>>> group. My hope is to sell fewer, larger lots of gear rather than > >>>> one-offs, but will be open to special situations and whatnot. As they > >>>> say, stay tuned! > >>>> > >>>> > >>>> -- > >>>> Greetings from Steve Baker (he/him/his) > >>>> “Gravity brings me down…” > >>>> > >>>> > >>>> > >>>> > >>>> On Feb 8, 2024, at 8:08 PM, B 9 <[email protected] > >>>> <mailto:[email protected]>> wrote: > >>>> > >>>> On Tue, Dec 26, 2023 at 4:36 PM Steve Baker <[email protected] > >>>> <mailto:[email protected]>> wrote: > >>>> > >>>> That was one of the hardest parts about writing that app — I did a lot > >>>> of research about tips and tricks, and have a few nested randomizer > >>>> routines in there to compensate. While I have not done a statistical > >>>> study with a significant sample set, it kicks out dice rolls that are > >>>> very random, and the folks that I play with feel comfortable with its > >>>> results. The source code is available and I’d be happy to highlight the > >>>> key bits (some upfront prep early in the code and most of the randomizer > >>>> functions are towards the back, etc). > >>>> > >>>> Steve, > >>>> > >>>> I was curious how nesting the randomizer had affected the quality of the > >>>> randomization, so I poked around at the source code for DICEBOX. While I > >>>> had a lot of fun trying to weave my way through the tightly crunched > >>>> code, in the end I wasn't able to extract just the randomizer. Could you > >>>> help me grok it? Once the routine is extracted, I think your RNG might > >>>> be useful for anyone else who wants to reuse your work. > >>>> > >>>> I've already written code that can do a check of RNG uniformity via the > >>>> χ² (chi squared) test on a Model T. (See attachment). It rolls a D sided > >>>> die N times and returns a probability p that the RNG is selecting every > >>>> number equally. I just need your routine at line 10000 which takes > >>>> variable D (number of sides) as input and outputs variable R (random > >>>> integer from 1 to D, inclusive). > >>>> > >>>> —b9 > >>>> > >>>> P.S. On the Tandy 200, DICEBOX needs a line 15 GOTO 100 or it gives an > >>>> error. > >>>> > >>>> > >>>> --- CUT HERE --- > >>>> > >>>> 0 GOTO 1000 REM RNG Tester by hackerb9 > >>>> 1 DATA 0.000, 0.000, 0.001, 0.004, 0.016, 2.706, 3.841, 5.024, 6.635, > >>>> 7.879 > >>>> 2 DATA 0.010, 0.020, 0.051, 0.103, 0.211, 4.605, 5.991, 7.378, 9.210, > >>>> 10.597 > >>>> 3 DATA 0.072, 0.115, 0.216, 0.352, 0.584, 6.251, 7.815, 9.348, 11.345, > >>>> 12.838 > >>>> 4 DATA 0.207, 0.297, 0.484, 0.711, 1.064, 7.779, 9.488, 11.143, 13.277, > >>>> 14.860 > >>>> 5 DATA 0.412, 0.554, 0.831, 1.145, 1.610, 9.236, 11.070, 12.833, 15.086, > >>>> 16.750 > >>>> 6 DATA 0.676, 0.872, 1.237, 1.635, 2.204, 10.645, 12.592, 14.449, > >>>> 16.812, 18.548 > >>>> 7 DATA 0.989, 1.239, 1.690, 2.167, 2.833, 12.017, 14.067, 16.013, > >>>> 18.475, 20.278 > >>>> 8 DATA 1.344, 1.646, 2.180, 2.733, 3.490, 13.362, 15.507, 17.535, > >>>> 20.090, 21.955 > >>>> 9 DATA 1.735, 2.088, 2.700, 3.325, 4.168, 14.684, 16.919, 19.023, > >>>> 21.666, 23.589 > >>>> 10 DATA 2.156, 2.558, 3.247, 3.940, 4.865, 15.987, 18.307, 20.483, > >>>> 23.209, 25.188 > >>>> 11 DATA 2.603, 3.053, 3.816, 4.575, 5.578, 17.275, 19.675, 21.920, > >>>> 24.725, 26.757 > >>>> 12 DATA 3.074, 3.571, 4.404, 5.226, 6.304, 18.549, 21.026, 23.337, > >>>> 26.217, 28.300 > >>>> 13 DATA 3.565, 4.107, 5.009, 5.892, 7.042, 19.812, 22.362, 24.736, > >>>> 27.688, 29.819 > >>>> 14 DATA 4.075, 4.660, 5.629, 6.571, 7.790, 21.064, 23.685, 26.119, > >>>> 29.141, 31.319 > >>>> 15 DATA 4.601, 5.229, 6.262, 7.261, 8.547, 22.307, 24.996, 27.488, > >>>> 30.578, 32.801 > >>>> 16 DATA 5.142, 5.812, 6.908, 7.962, 9.312, 23.542, 26.296, 28.845, > >>>> 32.000, 34.267 > >>>> 17 DATA 5.697, 6.408, 7.564, 8.672, 10.085, 24.769, 27.587, 30.191, > >>>> 33.409, 35.718 > >>>> 18 DATA 6.265, 7.015, 8.231, 9.390, 10.865, 25.989, 28.869, 31.526, > >>>> 34.805, 37.156 > >>>> 19 DATA 6.844, 7.633, 8.907, 10.117, 11.651, 27.204, 30.144, 32.852, > >>>> 36.191, 38.582 > >>>> 20 DATA 7.434, 8.260, 9.591, 10.851, 12.443, 28.412, 31.410, 34.170, > >>>> 37.566, 39.997 > >>>> 21 DATA 8.034, 8.897, 10.283, 11.591, 13.240, 29.615, 32.671, 35.479, > >>>> 38.932, 41.401 > >>>> 22 DATA 8.643, 9.542, 10.982, 12.338, 14.041, 30.813, 33.924, 36.781, > >>>> 40.289, 42.796 > >>>> 23 DATA 9.260, 10.196, 11.689, 13.091, 14.848, 32.007, 35.172, 38.076, > >>>> 41.638, 44.181 > >>>> 24 DATA 9.886, 10.856, 12.401, 13.848, 15.659, 33.196, 36.415, 39.364, > >>>> 42.980, 45.559 > >>>> 25 DATA 10.520, 11.524, 13.120, 14.611, 16.473, 34.382, 37.652, 40.646, > >>>> 44.314, 46.928 > >>>> 26 DATA 11.160, 12.198, 13.844, 15.379, 17.292, 35.563, 38.885, 41.923, > >>>> 45.642, 48.290 > >>>> 27 DATA 11.808, 12.879, 14.573, 16.151, 18.114, 36.741, 40.113, 43.195, > >>>> 46.963, 49.645 > >>>> 28 DATA 12.461, 13.565, 15.308, 16.928, 18.939, 37.916, 41.337, 44.461, > >>>> 48.278, 50.993 > >>>> 29 DATA 13.121, 14.256, 16.047, 17.708, 19.768, 39.087, 42.557, 45.722, > >>>> 49.588, 52.336 > >>>> 30 DATA 13.787, 14.953, 16.791, 18.493, 20.599, 40.256, 43.773, 46.979, > >>>> 50.892, 53.672 > >>>> 40 DATA 20.707, 22.164, 24.433, 26.509, 29.051, 51.805, 55.758, 59.342, > >>>> 63.691, 66.766 > >>>> 50 DATA 27.991, 29.707, 32.357, 34.764, 37.689, 63.167, 67.505, 71.420, > >>>> 76.154, 79.490 > >>>> 60 DATA 35.534, 37.485, 40.482, 43.188, 46.459, 74.397, 79.082, 83.298, > >>>> 88.379, 91.952 > >>>> 70 DATA 43.275, 45.442, 48.758, 51.739, 55.329, 85.527, 90.531, 95.023, > >>>> 100.425, 104.215 > >>>> 80 DATA 51.172, 53.540, 57.153, 60.391, 64.278, 96.578, 101.879, > >>>> 106.629, 112.329, 116.321 > >>>> 90 DATA 59.196, 61.754, 65.647, 69.126, 73.291, 107.565, 113.145, > >>>> 118.136, 124.116, 128.299 > >>>> 100 DATA 67.328, 70.065, 74.222, 77.929, 82.358, 118.498, 124.342, > >>>> 129.561, 135.807, 140.169 > >>>> 110 REM Calculate p from df and X2 > >>>> 120 IF DF<1 THEN DF=1: PRINT "Using df =" DF > >>>> 130 IF DF>100 THEN DF=100: PRINT "Using df =" DF > >>>> 140 IF DF>30 AND (DF MOD 10) > 0 THEN DF=DF\10: PRINT "Using df =" DF > >>>> 150 ' Select a DATA line to read from > >>>> 151 IF DF=1 THEN RESTORE 1 > >>>> 152 IF DF=2 THEN RESTORE 2 > >>>> 153 IF DF=3 THEN RESTORE 3 > >>>> 154 IF DF=4 THEN RESTORE 4 > >>>> 155 IF DF=5 THEN RESTORE 5 > >>>> 156 IF DF=6 THEN RESTORE 6 > >>>> 157 IF DF=7 THEN RESTORE 7 > >>>> 158 IF DF=8 THEN RESTORE 8 > >>>> 159 IF DF=9 THEN RESTORE 9 > >>>> 160 IF DF=10 THEN RESTORE 10 > >>>> 161 IF DF=11 THEN RESTORE 11 > >>>> 162 IF DF=12 THEN RESTORE 12 > >>>> 163 IF DF=13 THEN RESTORE 13 > >>>> 164 IF DF=14 THEN RESTORE 14 > >>>> 165 IF DF=15 THEN RESTORE 15 > >>>> 166 IF DF=16 THEN RESTORE 16 > >>>> 167 IF DF=17 THEN RESTORE 17 > >>>> 168 IF DF=18 THEN RESTORE 18 > >>>> 169 IF DF=19 THEN RESTORE 19 > >>>> 170 IF DF=20 THEN RESTORE 20 > >>>> 171 IF DF=21 THEN RESTORE 21 > >>>> 172 IF DF=22 THEN RESTORE 22 > >>>> 173 IF DF=23 THEN RESTORE 23 > >>>> 174 IF DF=24 THEN RESTORE 24 > >>>> 175 IF DF=25 THEN RESTORE 25 > >>>> 176 IF DF=26 THEN RESTORE 26 > >>>> 177 IF DF=27 THEN RESTORE 27 > >>>> 178 IF DF=28 THEN RESTORE 28 > >>>> 179 IF DF=29 THEN RESTORE 29 > >>>> 180 IF DF=30 THEN RESTORE 30 > >>>> 181 IF DF=40 THEN RESTORE 40 > >>>> 182 IF DF=50 THEN RESTORE 50 > >>>> 183 IF DF=60 THEN RESTORE 60 > >>>> 184 IF DF=70 THEN RESTORE 70 > >>>> 185 IF DF=80 THEN RESTORE 80 > >>>> 186 IF DF=90 THEN RESTORE 90 > >>>> 187 IF DF=100 THEN RESTORE 100 > >>>> 210 REM p value for columns in data > >>>> 220 A(0)=1 > >>>> 230 A(1)=.995: A(2)=.990: A(3)=.975 > >>>> 240 A(4)=.950: A(5)=.900: A(6)=.100 > >>>> 250 A(7)=.050: A(8)=.025: A(9)=.010 > >>>> 260 A(10)=.005 > >>>> 300 REM X2 is chi square, M is alpha > >>>> 310 FOR T=1 TO 10 > >>>> 320 READ M > >>>> 330 IF X2 > M THEN NEXT > >>>> 350 P=A(T-1) > >>>> 390 RETURN > >>>> 990 REM By hackerb9 February 2024. > >>>> 999 ' > >>>> 1000 REM Chi Squared statistic to > >>>> 1001 REM check for uniformity of RNG. > >>>> 1002 ' Note: This does NOT check for > >>>> 1003 ' independence. For that, use > >>>> 1004 ' autocorrelation, gap, poker > >>>> 1005 ' or other "diehard" tests. > >>>> 1010 ES$=CHR$(27) 'Screen Esc sequences > >>>> 1020 PRINT ES$"U"; ' Turn off labels > >>>> 1030 CLS > >>>> 1038 ' d is number of histogram bins > >>>> 1039 ' or, equivalently, sides of a die. > >>>> 1040 D=6 > >>>> 1050 DIM X(D) > >>>> 1059 ' n is number of times to roll. > >>>> 1060 N=100 > >>>> 1069 ' is the count expected in a bin > >>>> 1070 E=N/D > >>>> 1099 ' > >>>> 1100 REM Roll dice and show histogram bins. > >>>> 1120 PRINT"Rolling a";D;"sided die";N;"times"; > >>>> 1130 FOR I=1 TO N > >>>> 1140 GOSUB 10000 ' R = random(D) > >>>> 1150 X(R)=X(R)+1 > >>>> 1160 TT=(R-1)*4: GOSUB 1550 > >>>> 1170 PRINT X(R); > >>>> 1180 NEXT I > >>>> 1190 TT=D*4:GOSUB 1550: PRINT > >>>> 1199 ' > >>>> 1200 REM Calculate chi square statistic > >>>> 1201 ' Chi-Square = sum{(x(i)-E)^2 / E} > >>>> 1210 FOR I=1 TO D: IF X(I)<5 THEN PRINT "Results may be inaccurate due > >>>> to insufficient trials (n ="N", d ="D")": ELSE NEXT > >>>> 1230 X2=0: DF=-1 > >>>> 1240 FOR I=1 TO D > >>>> 1250 X2 = X2 + ( (X(I) - E)^2 / E) > >>>> 1260 IF X(I) > 0 THEN DF=DF+1 > >>>> 1270 NEXT I > >>>> 1280 PRINT "Chi square is";X2 > >>>> 1290 PRINT "Degrees of Freedom is";df > >>>> 1299 ' > >>>> 1300 REM Set p (percentage points) by > >>>> 1301 REM looking up x2 (chi square) > >>>> 1302 REM at df (degree of freedom) in > >>>> 1303 REM the chisq distribution table. > >>>> 1310 GOSUB 110 > >>>> 1399 ' > >>>> 1400 REM Interpret result > >>>> 1410 PRINT "p =" P*100 "%" > >>>> 1420 PRINT"This Random Number Generator" > >>>> 1430 IF P>0.95 THEN PRINT "is too regular. (BAD)": END > >>>> 1440 IF P<0.05 THEN PRINT "is not uniformly distributed. (BAD)": END > >>>> 1450 PRINT "seems uniformly distributed." > >>>> 1460 END > >>>> 1499 ' > >>>> 1500 'Move cursor to Row AA and Col BB > >>>> 1510 PRINT ES$"Y"CHR$(32+AA)CHR$(32+BB); > >>>> 1520 RETURN > >>>> 1550 REM Move cursor to position TT > >>>> 1560 AA=TT\40+1:BB=TT MOD 40:GOSUB 1500 > >>>> 1570 RETURN > >>>> 9999 ' > >>>> 10000 REM RANDOM NUMBER GENERATOR > >>>> 10001 ' Input D, integer > >>>> 10002 ' Output R, integer > >>>> 10003 ' 1..D, inclusive > >>>> 10010 R=INT(D*RND(1))+1 > >>>> 10090 RETURN > >>>> > >>>> > >>>> > >>>> <x2.ba <http://x2.ba/>> > >> > >> > >
