spacetaxi/tools/stsim/compare.py
2026-10-13 13:56:35 -05:00

72 lines
2.9 KiB
Python

#!/usr/bin/env python3
# Compare two ride traces in stsim's line format -- stsim's against the
# real C64's (c64trace.py), or a golden against the current tree -- field
# by field.
#
# compare.py <a.jsonl> <b.jsonl> [maxReports]
# the first maxReports ticks that differ (default 12), with the
# fields that differ, then how often and from when each field does
# compare.py --picture <a.raw> <b.raw> <tick>
# the screen cells, colour cells and glyphs that differ on one tick,
# from the two machines' picture dumps (STSIM_RAW / c64trace's rawOut)
import json
import sys
SCREEN_CELLS = 1000
SCREEN_COLS = 40
CHARSET = 2048
GLYPH_BYTES = 8
RAW_BYTES = SCREEN_CELLS * 2 + CHARSET
def traces(pathA, pathB, maxReports):
a = [json.loads(line) for line in open(pathA)]
b = [json.loads(line) for line in open(pathB)]
counts = {}
first = {}
reports = 0
for i in range(min(len(a), len(b))):
x, y = a[i], b[i]
fields = [k for k in x if x.get(k) != y.get(k)] + [k for k in y if k not in x]
for k in fields:
counts[k] = counts.get(k, 0) + 1
first.setdefault(k, i)
if fields and reports < maxReports:
print('line %d: ' % i + ' '.join('%s: %s vs %s' % (k, x.get(k), y.get(k)) for k in fields))
reports += 1
if len(a) != len(b):
print('lengths differ: %d vs %d lines' % (len(a), len(b)))
if not counts:
print('every common line matches' if len(a) != len(b) else 'identical')
return
print('fields that differ (lines, first line): ' + ', '.join('%s %d @%d' % (k, counts[k], first[k]) for k in sorted(counts, key=first.get)))
def picture(pathA, pathB, tick):
a = open(pathA, 'rb').read()[tick * RAW_BYTES:(tick + 1) * RAW_BYTES]
b = open(pathB, 'rb').read()[tick * RAW_BYTES:(tick + 1) * RAW_BYTES]
for i in range(SCREEN_CELLS):
if a[i] != b[i]:
print('screen row %d col %d: %02X vs %02X' % (i // SCREEN_COLS, i % SCREEN_COLS, a[i], b[i]))
for i in range(SCREEN_CELLS):
if a[SCREEN_CELLS + i] != b[SCREEN_CELLS + i]:
print('colour row %d col %d: %X vs %X' % (i // SCREEN_COLS, i % SCREEN_COLS, a[SCREEN_CELLS + i], b[SCREEN_CELLS + i]))
for ch in range(CHARSET // GLYPH_BYTES):
o = SCREEN_CELLS * 2 + ch * GLYPH_BYTES
if a[o:o + GLYPH_BYTES] != b[o:o + GLYPH_BYTES]:
print('glyph %02X: %s vs %s' % (ch, a[o:o + GLYPH_BYTES].hex(), b[o:o + GLYPH_BYTES].hex()))
def main():
if len(sys.argv) == 5 and sys.argv[1] == '--picture':
picture(sys.argv[2], sys.argv[3], int(sys.argv[4]))
return 0
if len(sys.argv) not in (3, 4):
print('usage: compare.py <a.jsonl> <b.jsonl> [maxReports] | --picture <a.raw> <b.raw> <tick>', file=sys.stderr)
return 2
traces(sys.argv[1], sys.argv[2], int(sys.argv[3]) if len(sys.argv) == 4 else 12)
return 0
if __name__ == '__main__':
sys.exit(main())