test_hand.py
5.7 kB · python · 177 lines
1import numpy as np2import pytest34from mrlypy import core, gen, life5from mrlypy.core import Rng, colors, paint, tensor6from mrlypy.math import cell, six, three, two7from mrlypy.num import factor, series89DTYPES = (np.uint8, np.uint16, np.uint32, np.int32)101112def draw(rng):13 return [rng.range(0, 100) for _ in range(10)]141516def test_a_tensor_crosses_both_ways_in_every_dtype():17 for dtype in DTYPES:18 out = tensor.rot90(np.array([[1, 2], [3, 4]], dtype=dtype), 1, (0, 1))19 assert out.dtype == dtype20 assert out.shape == (2, 2)21 assert out.tolist() == [[2, 4], [1, 3]]222324def test_a_tensor_out_owns_the_buffer_it_was_handed():25 out = tensor.rot90(np.zeros((3, 4), dtype=np.uint8), 1, (0, 1))26 assert out.flags.writeable27 assert out.flags.c_contiguous28 assert not isinstance(out.base, np.ndarray)293031def test_a_tensor_in_refuses_a_foreign_dtype_or_a_gapped_layout():32 with pytest.raises(ValueError):33 tensor.rot90(np.zeros((2, 2), dtype=np.float64), 1, (0, 1))34 with pytest.raises(ValueError):35 tensor.rot90(np.zeros((4, 4), dtype=np.uint8)[::2], 1, (0, 1))363738def test_a_rust_value_error_lands_as_a_value_error():39 with pytest.raises(ValueError):40 tensor.rot90(np.zeros((2, 2), dtype=np.uint8), 1, (0, 2))414243def test_a_rust_overflow_lands_as_an_overflow_error():44 assert series.bernoulli(3) == [(1, 1), (-1, 2), (1, 6)]45 with pytest.raises(OverflowError):46 series.bernoulli(33)474849def test_a_mutated_tensor_writes_back_into_its_array():50 grid = np.zeros((2, 2), dtype=np.uint8)51 tensor.put(grid, 3, 9)52 assert grid[1, 1] == 9535455def test_a_cell_crosses_out_as_a_dict_of_arrays():56 c = two.carpet(3, 2)57 assert set(c) == {"types", "colors", "tags"}58 assert c["types"].shape == (9, 9)59 assert c["types"].dtype == np.uint860 assert int(c["types"].sum()) == 6461 assert c["colors"] is None62 assert c["tags"] is None636465def test_a_three_dimensional_cell_keeps_depth_height_width():66 assert three.carpet(3, 1)["types"].shape == (3, 3, 3)676869def test_a_cell_crosses_in_and_a_serde_struct_crosses_out():70 assert two.census(two.carpet(3, 1)) == {71 "fills": 8,72 "voids": 1,73 "perimeter": 16,74 "vertices": 16,75 "edges": 24,76 "euler": 0,77 }787980def test_cell_colors_cross_out_as_rgba_and_back_in():81 painted = cell.paint(two.carpet(3, 1))82 assert painted["colors"].shape == (3, 3, 4)83 assert painted["colors"].dtype == np.uint884 assert painted["colors"][1, 1].tolist() == [255, 255, 255, 255]85 assert two.census(painted)["fills"] == 8868788def test_paint_takes_its_optional_mapping_mode_and_stream():89 c = two.carpet(3, 1)90 custom = {0: [(255, 61, 64, 255)], 1: [(0, 140, 255, 255)]}91 painted = cell.paint(c, custom, "Type")92 assert painted["colors"][1, 1].tolist() == [255, 61, 64, 255]93 assert painted["colors"][0, 0].tolist() == [0, 140, 255, 255]94 assert cell.paint(c, custom, "Random", Rng(2026)) is not None95 with pytest.raises(ValueError):96 cell.paint(c, custom, "Random")979899def test_a_mutated_cell_writes_back_into_its_dict():100 c = two.carpet(3, 1)101 paint.tag(c, "Layers", "Fill", None)102 assert c["tags"] is not None103104105def test_a_cell_dict_that_is_not_a_2d_cell_is_a_value_error():106 with pytest.raises(ValueError):107 two.census({"types": np.zeros((2, 2, 2), dtype=np.uint8)})108 with pytest.raises(ValueError):109 two.census({"tags": np.zeros((2, 2), dtype=np.uint8)})110111112def test_a_tag_layer_crosses_in_beside_its_types():113 c = two.carpet(3, 1)114 c["tags"] = np.ones((3, 3), dtype=np.uint16)115 assert two.census(c)["fills"] == 8116117118def test_a_const_n_function_dispatches_on_the_rank():119 assert cell.models.width(two.carpet(3, 1)) == 3120 assert cell.models.width(three.carpet(3, 1)) == 3121 assert cell.models.rotate(three.carpet(3, 1), 1, (0, 1))["types"].shape == (3, 3, 3)122 with pytest.raises(ValueError):123 cell.models.rotate(three.carpet(3, 1), 1)124 with pytest.raises(ValueError):125 cell.models.depth(two.carpet(3, 1))126127128def test_a_hexagonal_cell_crosses_by_its_four_fields():129 c = six.cut_design(23, 3, 1, 2)130 assert set(c) == {"cell", "projection", "orientation", "start"}131 assert c["projection"] == "Cut"132 assert c["orientation"] == "Horizontal"133 assert c["cell"]["types"].ndim == 2134 assert six.census(c, True)["triangles"] > 0135136137def test_a_code_crosses_as_a_plain_int():138 assert factor.gcd(1071, 462) == 21139 assert factor.gcd(2**127, 2**100) == 2**100140141142def test_a_color_crosses_as_four_channel_bytes():143 assert colors.from_hex("#ff3d40") == (255, 61, 64, 255)144 assert colors.from_hex("#008cff80") == (0, 140, 255, 128)145 with pytest.raises(ValueError):146 colors.from_hex("#nope")147148149def test_pixels_cross_out_as_an_n_by_4_array():150 width, height, pixels = core.unpng(gen.background(1, 2, 2))151 assert pixels.shape == (width * height, 4)152 assert pixels.dtype == np.uint8153 assert core.codec.png(pixels, width, height, 1) == core.codec.png(pixels.tolist(), width, height, 1)154155156def test_a_vec_of_bytes_crosses_as_bytes():157 png = gen.background(1, 2, 2)158 assert isinstance(png, bytes)159 assert png[:8] == b"\x89PNG\r\n\x1a\n"160161162def test_a_class_holds_the_rust_value_and_round_trips_plain_data():163 tile = gen.Tile("Fractal")164 assert tile.group == "Fractal"165 assert gen.Tile.from_dict(tile.to_dict()).to_dict() == tile.to_dict()166 assert gen.Tile.new("Fractal").to_dict() == tile.to_dict()167168169def test_a_python_keyword_grows_a_trailing_underscore():170 assert life.lambda_(30) == 0.5171172173def test_one_seed_replays_one_stream_and_passes_into_rust():174 assert draw(Rng(2026)) == draw(Rng(2026))175 assert draw(Rng(2026)) != draw(Rng(2027))176 drawn = two.noise(3, 1, 0.5, Rng(2026))["types"]177 assert drawn.tolist() == two.noise(3, 1, 0.5, Rng(2026))["types"].tolist()