From 050da302b8048f8ebf5f8a2f83ea302c7f954669 Mon Sep 17 00:00:00 2001 From: sterni Date: Sun, 20 Dec 2020 14:45:09 +0100 Subject: [PATCH] generate-alt-random-writes: use python random, allow setting a seed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using Python's own PRNG should make the code cleaner and allow for reproducible stimulus files if that is desired via setting --seed (at least for the same versions of the script, changing the kind and/or order of the random calls will of course impact the output in the future). I did the following substitutions: * rand.read(1)[0] % n and struct.unpack('@H', rand.read(2))[0] % n → random.randrange(n) * rand.read(1)[0] → random.randrange(256) * rand.read(n) → [random.randrange(256) for _ in range(n)] (better alternative would have been random.randbytes(n), but is only available for Python >= 3.9, switching to this in the future will impact output) * list[rand.read(1) % len(list)] → random.choice(list) --- scripts/generate-alt-random-writes.py | 200 +++++++++++++------------- 1 file changed, 101 insertions(+), 99 deletions(-) diff --git a/scripts/generate-alt-random-writes.py b/scripts/generate-alt-random-writes.py index 2436b6ff..1f891470 100755 --- a/scripts/generate-alt-random-writes.py +++ b/scripts/generate-alt-random-writes.py @@ -3,6 +3,7 @@ import argparse import enum import fcntl import struct +import random import sys import termios @@ -31,6 +32,7 @@ def main(): parser.add_argument('--attr-italic', action='store_true') parser.add_argument('--attr-underline', action='store_true') parser.add_argument('--sixel', action='store_true') + parser.add_argument('--seed', type=int) opts = parser.parse_args() out = opts.out if opts.out is not None else sys.stdout @@ -61,133 +63,133 @@ def main(): # Enter alt screen out.write('\033[?1049h') - with open('/dev/urandom', 'rb') as rand: - for _ in range(count): - if opts.scroll and rand.read(1)[0] == 0: - out.write('\033[m') + # uses system time or /dev/urandom if available if opt.seed == None + # pin seeding method to make seeding stable across future versions + random.seed(a=opts.seed, version=2) - if opts.scroll_region and rand.read(1)[0] == 0: - top = rand.read(1)[0] % 3 - bottom = rand.read(1)[0] % 3 - out.write(f'\033[{top};{lines - bottom}r') + for _ in range(count): + if opts.scroll and random.randrange(256) == 0: + out.write('\033[m') - lines_to_scroll = rand.read(1)[0] % (lines - 1) - rev = rand.read(1)[0] % 2 - if not rev and rand.read(1)[0] % 2: - out.write(f'\033[{lines};{cols}H') - out.write('\n' * lines_to_scroll) - else: - out.write(f'\033[{lines_to_scroll + 1}{"T" if rev == 1 else "S"}') - continue + if opts.scroll_region and random.randrange(256) == 0: + top = random.randrange(3) + bottom = random.randrange(3) + out.write(f'\033[{top};{lines - bottom}r') - # Generate a random location and a random character - row = rand.read(1)[0] % lines - col = rand.read(1)[0] % cols - c = alphabet[rand.read(1)[0] % len(alphabet)] + lines_to_scroll = random.randrange(lines - 1) + rev = random.randrange(2) + if not rev and random.randrange(2): + out.write(f'\033[{lines};{cols}H') + out.write('\n' * lines_to_scroll) + else: + out.write(f'\033[{lines_to_scroll + 1}{"T" if rev == 1 else "S"}') + continue - repeat = rand.read(1)[0] % (cols - col) + 1 - assert col + repeat <= cols + # Generate a random location and a random character + row = random.randrange(lines) + col = random.randrange(cols) + c = random.choice(alphabet) - color_variant = color_variants[rand.read(1)[0] % len(color_variants)] + repeat = random.randrange((cols - col) + 1) + assert col + repeat <= cols - # Position cursor - out.write(f'\033[{row + 1};{col + 1}H') + color_variant = random.choice(color_variants) - if color_variant in [ColorVariant.REGULAR, ColorVariant.BRIGHT]: - do_bg = rand.read(1)[0] % 2 - base = 40 if do_bg else 30 - base += 60 if color_variant == ColorVariant.BRIGHT else 0 + # Position cursor + out.write(f'\033[{row + 1};{col + 1}H') - idx = rand.read(1)[0] % 8 - out.write(f'\033[{base + idx}m') + if color_variant in [ColorVariant.REGULAR, ColorVariant.BRIGHT]: + do_bg = random.randrange(2) + base = 40 if do_bg else 30 + base += 60 if color_variant == ColorVariant.BRIGHT else 0 - elif color_variant == ColorVariant.CUBE: - do_bg = rand.read(1)[0] % 2 - base = 48 if do_bg else 38 + idx = random.randrange(8) + out.write(f'\033[{base + idx}m') - idx = rand.read(1)[0] % 256 - if rand.read(1)[0] % 2: - # Old-style - out.write(f'\033[{base};5;{idx}m') - else: - # New-style (sub-parameter based) - out.write(f'\033[{base}:2:5:{idx}m') + elif color_variant == ColorVariant.CUBE: + do_bg = random.randrange(2) + base = 48 if do_bg else 38 - elif color_variant == ColorVariant.RGB: - do_bg = rand.read(1)[0] % 2 - base = 48 if do_bg else 38 - rgb = rand.read(3) + idx = random.randrange(256) + if random.randrange(2): + # Old-style + out.write(f'\033[{base};5;{idx}m') + else: + # New-style (sub-parameter based) + out.write(f'\033[{base}:2:5:{idx}m') - if rand.read(1)[0] % 2: - # Old-style - out.write(f'\033[{base};2;{rgb[0]};{rgb[1]};{rgb[2]}m') - else: - # New-style (sub-parameter based) - out.write(f'\033[{base}:2::{rgb[0]}:{rgb[1]}:{rgb[2]}m') + elif color_variant == ColorVariant.RGB: + do_bg = random.randrange(2) + base = 48 if do_bg else 38 - if opts.attr_bold and rand.read(1)[0] % 5 == 0: - out.write('\033[1m') - if opts.attr_italic and rand.read(1)[0] % 5 == 0: - out.write('\033[3m') - if opts.attr_underline and rand.read(1)[0] % 5 == 0: - out.write('\033[4m') + # use list comprehension in favor of randbytes(n) + # which is only available for Python >= 3.9 + rgb = [random.randrange(256) for _ in range(3)] - out.write(c * repeat) + if random.randrange(2): + # Old-style + out.write(f'\033[{base};2;{rgb[0]};{rgb[1]};{rgb[2]}m') + else: + # New-style (sub-parameter based) + out.write(f'\033[{base}:2::{rgb[0]}:{rgb[1]}:{rgb[2]}m') - do_sgr_reset = rand.read(1)[0] % 2 - if do_sgr_reset: - reset_actions = ['\033[m', '\033[39m', '\033[49m'] - idx = rand.read(1)[0] % len(reset_actions) - out.write(reset_actions[idx]) + if opts.attr_bold and random.randrange(5) == 0: + out.write('\033[1m') + if opts.attr_italic and random.randrange(5) == 0: + out.write('\033[3m') + if opts.attr_underline and random.randrange(5) == 0: + out.write('\033[4m') + + out.write(c * repeat) + + do_sgr_reset = random.randrange(2) + if do_sgr_reset: + reset_actions = ['\033[m', '\033[39m', '\033[49m'] + out.write(random.choice(reset_actions)) # Leave alt screen out.write('\033[m\033[r\033[?1049l') - with open('/dev/urandom', 'rb') as rand: - if opts.sixel: - # The sixel 'alphabet' - sixels = '?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' + if opts.sixel: + # The sixel 'alphabet' + sixels = '?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' - for _ in range(200): - # Offset image - out.write(' ' * (rand.read(1)[0] % (cols // 2))) + for _ in range(200): + # Offset image + out.write(' ' * (random.randrange(cols // 2))) - # Begin sixel - out.write('\033Pq') + # Begin sixel + out.write('\033Pq') - # Set up 256 random colors - for idx in range(256): - # param 2: 1=HLS, 2=RGB. - # param 3/4/5: HLS/RGB values in range 0-100 - # (except 'hue' which is 0..360) - out.write(f'#{idx};2;{rand.read(1)[0] % 101};{rand.read(1)[0] % 101};{rand.read(1)[0] % 101}') + # Set up 256 random colors + for idx in range(256): + # param 2: 1=HLS, 2=RGB. + # param 3/4/5: HLS/RGB values in range 0-100 + # (except 'hue' which is 0..360) + out.write(f'#{idx};2;{random.randrange(101)};{random.randrange(101)};{random.randrange(101)}') - # Randomize image width/height - six_height = struct.unpack('@H', rand.read(2))[0] % (height // 2) - six_width = struct.unpack('@H', rand.read(2))[0] % (width // 2) + # Randomize image width/height + six_height = random.randrange(height // 2) + six_width = random.randrange(width // 2) - # Sixel size. Without this, sixels will be - # auto-resized on cell-boundaries. We expect programs - # to emit this sequence since otherwise you cannot get - # correctly sized images. - out.write(f'"0;0;{six_width};{six_height}') + # Sixel size. Without this, sixels will be + # auto-resized on cell-boundaries. We expect programs + # to emit this sequence since otherwise you cannot get + # correctly sized images. + out.write(f'"0;0;{six_width};{six_height}') - for row in range(six_height // 6): # Each sixel is 6 pixels - # Choose a random color - out.write(f'#{rand.read(1)[0] % 256}') + for row in range(six_height // 6): # Each sixel is 6 pixels + # Choose a random color + out.write(f'#{random.randrange(256)}') - if rand.read(1)[0] == 999999999999: - assert False - out.write(f'!{six_width}{sixels[rand.read(1)[0] % len(sixels)]}') - else: - for col in range(six_width): - out.write(f'{sixels[rand.read(1)[0] % len(sixels)]}') + for col in range(six_width): + out.write(f'{random.choice(sixels)}') - # Next line - out.write('-') + # Next line + out.write('-') - # End sixel - out.write('\033\\') + # End sixel + out.write('\033\\') if __name__ == '__main__':