From d918dd367d8e5524f18869b132d355857fb53794 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Tue, 16 Mar 2021 03:01:14 -0700 Subject: [PATCH] Added CLI options -random-num-generations, -random-pixel-size, and -random-delay to WolframAutomata. --- hacks/WolframAutomata/WolframAutomata.c | 78 +++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/hacks/WolframAutomata/WolframAutomata.c b/hacks/WolframAutomata/WolframAutomata.c index b6da506..35482df 100644 --- a/hacks/WolframAutomata/WolframAutomata.c +++ b/hacks/WolframAutomata/WolframAutomata.c @@ -298,22 +298,83 @@ WolframAutomata_init(Display * dpy, Window win) state->bg = gcv.background = get_pixel_resource(state->dpy, xgwa.colormap, "background", "Background"); state->gc = XCreateGC(state->dpy, state->win, GCForeground, &gcv); - state->delay_microsec = get_integer_resource(state->dpy, "delay-usec", "Integer"); - if (state->delay_microsec < 0) state->delay_microsec = 0; - - state->pixel_size = get_integer_resource(state->dpy, "pixel-size", "Integer"); + /* Set the size of each simulated cell as NxN pixels for pixel_size=N. */ + if (get_boolean_resource(state->dpy, "random-pixel-size", "Boolean")) { + /* Although we are choosing the pixel size 'randomly', a truly random */ + /* selection would bias toward large numbers since there are more of */ + /* them. To avoid this, we select a random number for a bit shift, */ + /* resulting in a pixel size of 1, 2, 4, 8, 16 or 32, equally likely. */ + state->pixel_size = 1 << (random() % 6); + } else { + state->pixel_size = get_integer_resource(state->dpy, "pixel-size", "Integer"); + } if (state->pixel_size < 1) state->pixel_size = 1; if (state->pixel_size > state->xlim) state->pixel_size = state->xlim; state->number_of_cells = state->xlim / state->pixel_size; // TODO: Do we want to enforce that number_of_cells > 0? + /* Set the delay (in microseconds) between simulation of each generation */ + /* of the simulation, also known as the delay between calls to */ + /* WolframAutomata_draw(), which simulates one generation per call. */ + if (get_boolean_resource(state->dpy, "random-delay", "Boolean")) { + /* When randomly setting the delay, the problem is to avoid being too */ + /* fast or too slow, as well as ensuring slower speeds are chosen */ + /* with the same likelihood as faster speeds, as perceived by a */ + /* human. By empirical observation, we note that for 1x1 up to 4x4 */ + /* pixel cell sizes, values for state->delay_microsec between */ + /* 2048 (2^11) and 16556 (2^14) produce pleasant scroll rates. To */ + /* maintain this appearance, we bitshift state->pixel_size down until */ + /* it is a maximum of 4x4 pixels in size, record how many bitshifts */ + /* took place, and then shift our valid window for */ + /* state->delay_microsec up by an equal number of bitshifts. For */ + /* example, if state->pixel_size=9, then it takes one right shift to */ + /* reach state->pixel_size=4. Thus, the valid window for */ + /* state->delay_microsec becomes 4096 (2^12) up to 32768 (2^15). */ + size_t pixel_shift_range = 1; + size_t pixel_size_temp = state->pixel_size; + while (pixel_size_temp > 4) { + pixel_size_temp >>= 1; + pixel_shift_range++; + } + /* In the below line, '3' represents the total range, namely '14-11' */ + /* from '2^14' and '2^11' as the endpoints. Similarly, the '11' in */ + /* the below line represents the starting point of this range, from */ + /* the exponent in '2^11'. */ + state->delay_microsec = 1 << ((random() % 3) + 11 + pixel_shift_range); + } else { + state->delay_microsec = get_integer_resource(state->dpy, "delay-usec", "Integer"); + } + if (state->delay_microsec < 0) state->delay_microsec = 0; + + /* Set the number of generations to simulate before wiping the simulation */ + /* and re-running with new settings. */ + if (get_boolean_resource(state->dpy, "random-num-generations", "Boolean")) { + /* By empirical observation, keep the product */ + /* state->num_generations * state->pixel_size */ + /* below 10,000 to avoid BadAlloc errors from the X server due to */ + /* requesting an enormous pixmap. This value works on both a 12 core */ + /* Xeon with 108 GiB of RAM and a Sun Ultra 2 with 2 GiB of RAM. */ + state->num_generations = random() % (10000 / state->pixel_size); + /* Ensure selected value is large enough to at least fill the screen. */ + /* Cast to avoid overflow. */ + if ((long)state->num_generations * (long)state->pixel_size < state->ylim) { + state->num_generations = (state->ylim / state->pixel_size) + 1; + } + } else { + state->num_generations = get_integer_resource(state->dpy, "num-generations", "Integer"); + } /* The minimum number of generations is 2 since we must allocate enough */ /* space to hold the seed generation and at least one pass through */ /* WolframAutomata_draw(), which is where we check whether or not we've */ /* reached the end of the pixmap. */ - state->num_generations = get_integer_resource(state->dpy, "num-generations", "Integer"); if (state->num_generations < 0) state->num_generations = 2; + /* The maximum number of generations is pixel_size dependent. This is a */ + /* soft limit and may be increased if you have plenty of RAM (and a */ + /* cooperative X server). The value 10,000 was determined empirically. */ + if ((long)state->num_generations * (long)state->pixel_size > 10000) { + state->num_generations = 10000 / state->pixel_size; + } /* Time to figure out which rule to use for this simulation. */ /* We ignore any weirdness resulting from the following cast since every */ @@ -450,6 +511,9 @@ static const char * WolframAutomata_defaults[] = { "*rule-random: False", "*population-density: 50", "*population-single: False", + "*random-delay: False", + "*random-pixel-size: False", + "*random-num-generations: False", 0 }; @@ -465,6 +529,10 @@ static XrmOptionDescRec WolframAutomata_options[] = { { "-rule-random", ".rule-random", XrmoptionNoArg, "True" }, { "-population-density", ".population-density", XrmoptionSepArg, 0 }, { "-population-single", ".population-single", XrmoptionNoArg, "True" }, + { "-random-delay", ".random-delay", XrmoptionNoArg, "True" }, + { "-random-pixel-size", ".random-pixel-size", XrmoptionNoArg, "True" }, + { "-random-num-generations", ".random-num-generations", XrmoptionNoArg, "True" }, + { 0, 0, 0, 0 } }; -- 2.20.1