stitch-a-ton-answer/fuzz.py

145 lines
4.4 KiB
Python
Raw Normal View History

2025-11-19 18:06:34 +07:00
#!/usr/bin/env python3
import argparse
import json
import random
import sys
# --- Configuration for Crop Fuzzing ---
MAX_SUM = 0.99
MIN_CROP_SIZE = 0.05
PRECISION = 4
# --- Configuration for Canvas Fuzzing ---
# The desired size of the fuzzed rectangle.
FUZZED_RECT_WIDTH = 12
FUZZED_RECT_HEIGHT = 8
# The boundaries of the entire canvas.
# A=1, B=2, ..., Z=26, AA=27, ..., AE=31
CANVAS_MIN_COL = 1 # 'A'
CANVAS_MIN_ROW = 1
CANVAS_MAX_COL = 31 # 'AE'
CANVAS_MAX_ROW = 55
def num_to_col_str(n):
"""Converts a 1-indexed column number to its spreadsheet-style string.
e.g., 1 -> 'A', 27 -> 'AA'
"""
string = ""
while n > 0:
n, remainder = divmod(n - 1, 26)
string = chr(65 + remainder) + string
return string
def generate_fuzzed_crop_values():
"""
Generates a random offset and size that adhere to the constraint:
offset + size <= MAX_SUM
"""
offset = random.uniform(0.0, MAX_SUM - MIN_CROP_SIZE)
max_size_allowed = MAX_SUM - offset
size = random.uniform(MIN_CROP_SIZE, max_size_allowed)
return round(offset, PRECISION), round(size, PRECISION)
def generate_fuzzed_canvas_rect():
"""
Generates a random canvas_rect of a fixed size (12x8) within the
overall canvas boundary (A1:AE55).
"""
# 1. Determine the valid range for the top-left corner.
# To fit a 12-wide rect inside a 31-wide canvas, the top-left
# column can start at 1 and must end early enough.
# Last possible start col = 31 - 12 + 1 = 20
max_start_col = CANVAS_MAX_COL - FUZZED_RECT_WIDTH + 1
# Same logic for rows.
# Last possible start row = 55 - 8 + 1 = 48
max_start_row = CANVAS_MAX_ROW - FUZZED_RECT_HEIGHT + 1
# 2. Pick a random top-left corner from the valid range.
start_col = random.randint(CANVAS_MIN_COL, max_start_col)
start_row = random.randint(CANVAS_MIN_ROW, max_start_row)
# 3. Calculate the bottom-right corner based on the fuzzed size.
end_col = start_col + FUZZED_RECT_WIDTH - 1
end_row = start_row + FUZZED_RECT_HEIGHT - 1
# 4. Convert numerical columns back to string representation.
start_col_str = num_to_col_str(start_col)
end_col_str = num_to_col_str(end_col)
# 5. Format the final string.
return f"{start_col_str}{start_row}:{end_col_str}{end_row}"
def main():
"""
Main function to read a JSON file, process the data, and either print
to the console or write to an output file.
"""
parser = argparse.ArgumentParser(
description="Fuzzes 'canvas_rect', 'crop_offset', and 'crop_size' in a JSON file.",
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"input_file", help="Path to the input JSON file to be processed."
)
parser.add_argument(
"-o",
"--output_file",
help="Path to the output JSON file. If not provided, the result is printed to the console.",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
print(f"Reading data from '{args.input_file}'...")
data = json.load(f)
except FileNotFoundError:
print(f"Error: The file '{args.input_file}' was not found.", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError:
print(
f"Error: The file '{args.input_file}' is not a valid JSON file.",
file=sys.stderr,
)
sys.exit(1)
except Exception as e:
print(f"An unexpected error occurred: {e}", file=sys.stderr)
sys.exit(1)
# Iterate through each item and update it with fuzzed values
for item in data:
# Fuzz crop offset and size
x_offset, x_size = generate_fuzzed_crop_values()
y_offset, y_size = generate_fuzzed_crop_values()
item["crop_offset"] = [x_offset, y_offset]
item["crop_size"] = [x_size, y_size]
# Fuzz the canvas rect
item["canvas_rect"] = generate_fuzzed_canvas_rect()
# Handle the output
if args.output_file:
try:
with open(args.output_file, "w") as f:
json.dump(data, f, indent=2)
print(f"Successfully wrote fuzzed data to '{args.output_file}'.")
except IOError as e:
print(
f"Error: Could not write to file '{args.output_file}': {e}",
file=sys.stderr,
)
sys.exit(1)
else:
print("\n--- Fuzzed JSON ---")
print(json.dumps(data, indent=2))
if __name__ == "__main__":
main()