File size: 4,825 Bytes
feb08d1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | """
Unit tests for all transforms in itt_solver.transforms.
Usage:
python tests/test_transforms.py
40 tests covering: Kronecker, mirror tiles, upscale, downscale, stack,
rotate, reflect, color ops, gravity, crop, transpose, shifted tile,
fill enclosed.
"""
import numpy as np
from itt_solver import transforms as tr
INP = np.array([[0,7,7],[7,7,7],[0,7,7]], dtype=float)
tests_passed = 0
tests_failed = 0
def check(name, condition):
global tests_passed, tests_failed
if condition:
print(f" ✅ {name}")
tests_passed += 1
else:
print(f" ❌ {name}")
tests_failed += 1
print("=== Kronecker Self-Similar ===")
T = tr.KroneckerSelfSimilar()
out = T.apply(INP)
check("Output shape is 9x9", out.shape == (9, 9))
check("σ=0 vs known target", np.array_equal(out, np.kron((INP!=0).astype(float), INP)))
print("\n=== KroneckerSelfSimilarInv ===")
T = tr.KroneckerSelfSimilarInv()
out = T.apply(INP)
check("Output shape is 9x9", out.shape == (9, 9))
print("\n=== MirrorTileH ===")
T = tr.MirrorTileH()
out = T.apply(INP)
check("Shape is 3x6", out.shape == (3, 6))
check("Left half is input", np.array_equal(out[:, :3], INP))
check("Right half is fliplr(input)", np.array_equal(out[:, 3:], np.fliplr(INP)))
print("\n=== MirrorTileV ===")
T = tr.MirrorTileV()
out = T.apply(INP)
check("Shape is 6x3", out.shape == (6, 3))
check("Top half is input", np.array_equal(out[:3, :], INP))
check("Bottom half is flipud(input)", np.array_equal(out[3:, :], np.flipud(INP)))
print("\n=== MirrorTile4Way ===")
T = tr.MirrorTile4Way()
out = T.apply(INP)
check("Shape is 6x6", out.shape == (6, 6))
print("\n=== Upscale 2x ===")
T = tr.Upscale(2)
out = T.apply(INP)
check("Shape is 6x6", out.shape == (6, 6))
check("Top-left 2x2 block is INP[0,0]", np.all(out[:2, :2] == INP[0, 0]))
print("\n=== Upscale 3x ===")
T = tr.Upscale(3)
out = T.apply(INP)
check("Shape is 9x9", out.shape == (9, 9))
check("Top-left 3x3 block is INP[0,0]", np.all(out[:3, :3] == INP[0, 0]))
print("\n=== Downscale 2x ===")
T = tr.Downscale(2)
big = np.kron(INP, np.ones((2, 2)))
out = T.apply(big)
check("Downscale of upscaled recovers original", np.array_equal(out, INP))
print("\n=== StackH 3 ===")
T = tr.StackH(3)
out = T.apply(INP)
check("Shape is 3x9", out.shape == (3, 9))
check("First third is input", np.array_equal(out[:, :3], INP))
print("\n=== StackV 3 ===")
T = tr.StackV(3)
out = T.apply(INP)
check("Shape is 9x3", out.shape == (9, 3))
check("First third is input", np.array_equal(out[:3, :], INP))
print("\n=== Rotate 90/180/270 ===")
for k in [1, 2, 3]:
T = tr.Rotate(k)
out = T.apply(INP)
check(f"Rotate_{90*k} matches np.rot90", np.array_equal(out, np.rot90(INP, k)))
print("\n=== Reflect h/v ===")
T = tr.Reflect('h')
check("Reflect_h matches flipud", np.array_equal(T.apply(INP), np.flipud(INP)))
T = tr.Reflect('v')
check("Reflect_v matches fliplr", np.array_equal(T.apply(INP), np.fliplr(INP)))
print("\n=== RetainColor ===")
T = tr.RetainColor(7)
out = T.apply(INP)
check("Only 7s remain", np.all(out[INP == 7] == 7))
check("Non-7 positions are 0", np.all(out[INP != 7] == 0))
print("\n=== RemoveColor ===")
T = tr.RemoveColor(7)
out = T.apply(INP)
check("7s are removed", np.all(out[INP == 7] == 0))
check("0s stay 0", np.all(out[INP == 0] == 0))
print("\n=== InvertColors ===")
T = tr.InvertColors()
out = T.apply(INP)
check("0→7 swap", np.all(out[INP == 0] == 7))
check("7→0 swap", np.all(out[INP == 7] == 0))
print("\n=== GravityDown ===")
T = tr.GravityDown()
col_in = np.array([[0,7,0],[0,0,7],[7,0,0]], dtype=float)
out = T.apply(col_in)
check("Col 0: 7 at bottom", out[2, 0] == 7 and out[0, 0] == 0 and out[1, 0] == 0)
check("Col 1: 7 at bottom", out[2, 1] == 7 and out[0, 1] == 0)
print("\n=== GravityUp ===")
T = tr.GravityUp()
out = T.apply(col_in)
check("Col 0: 7 at top", out[0, 0] == 7 and out[1, 0] == 0 and out[2, 0] == 0)
print("\n=== CropToContent ===")
T = tr.CropToContent()
padded = np.array([[0,0,0,0],[0,7,7,0],[0,7,7,0],[0,0,0,0]], dtype=float)
out = T.apply(padded)
check("Crops to 2x2", out.shape == (2, 2))
check("All 7s", np.all(out == 7))
print("\n=== Transpose ===")
T = tr.Transpose()
out = T.apply(INP)
check("Shape is transposed", out.shape == (3, 3))
check("Values match transpose", np.array_equal(out, INP.T))
print("\n=== ShiftedTile ===")
T = tr.tile_to_target_shifted(shift=(1, 1), tile_factor=3)
out = T.apply(INP)
check("Shape is 9x9", out.shape == (9, 9))
check("Differs from vanilla tile", not np.array_equal(out, np.tile(INP, (3, 3))))
print("\n=== FillEnclosedHarmonic ===")
T = tr.FillEnclosedHarmonic()
enclosed = np.array([[7,7,7],[7,0,7],[7,7,7]], dtype=float)
out = T.apply(enclosed)
check("Center hole filled", out[1, 1] == 7)
print(f"\n{'='*50}")
print(f"Results: {tests_passed} passed, {tests_failed} failed")
|