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()