Repository: PermutaTriangle/Tilings

Issue:
AssertionError: rule not in the spec and not empty
```
AssertionError: rule not in the spec and not empty
+-+-+
|1|\|
+-+-+
|\| |
+-+-+
1: Av(0123)
\: Av(01)
\: Av+(01)
Crossing obstructions:
0123: (0, 0), (0, 1), (0, 1), (0, 1)
0123: (0, 0), (0, 1), (0, 1), (1, 1)
0123: (0, 1), (0, 1), (0, 1), (1, 1)
Requirement 0:
0: (1, 1)
Assumption 0:
can count points in cell (0, 0)
```

To recreate, on `insert-factorable-reqs` branch:
```python
import requests
from comb_spec_searcher import CombinatorialSpecification
from tilings.tilescope import TileScopePack

uri = "https://api.permpal.com/garpur_run/627c8c4a4afd988318e59567"
data = requests.get(uri).json()
pack = TileScopePack.from_dict(data["pack"])
spec = CombinatorialSpecification.from_dict(data["specification"])
```

Discussion:

Patch:
diff --git a/tilings/strategies/factor.py b/tilings/strategies/factor.py
index 694cc83..1b13955 100644
--- a/tilings/strategies/factor.py
+++ b/tilings/strategies/factor.py
@@ -260,6 +260,22 @@ class FactorWithInterleavingStrategy(FactorStrategy):
         self.tracked = tracked
         self.cols, self.rows = self.interleaving_rows_and_cols(self.partition)
 
+    def to_jsonable(self) -> dict:
+        d = super().to_jsonable()
+        d["tracked"] = self.tracked
+        return d
+
+    def __repr__(self) -> str:
+        args = ", ".join(
+            [
+                f"partition={self.partition}",
+                f"ignore_parent={self.ignore_parent}",
+                f"workable={self.workable}",
+                f"tracked={self.tracked}",
+            ]
+        )
+        return f"{self.__class__.__name__}({args})"
+
     def is_two_way(self, comb_class: Tiling) -> bool:  # type: ignore
         return self.is_reversible(comb_class)
 


Test patch:
diff --git a/tests/strategies/test_encoding.py b/tests/strategies/test_encoding.py
index b5584ed..d46b725 100644
--- a/tests/strategies/test_encoding.py
+++ b/tests/strategies/test_encoding.py
@@ -244,6 +244,26 @@ def partition_ignoreparent_workable(strategy):
     ]
 
 
+def partition_ignoreparent_workable_tracked(strategy):
+    return [
+        strategy(
+            partition=partition,
+            ignore_parent=ignore_parent,
+            workable=workable,
+            tracked=tracked,
+        )
+        for partition, ignore_parent, workable, tracked in product(
+            (
+                [[(2, 1), (0, 1)], [(1, 0)]],
+                (((0, 0), (0, 2)), ((0, 1),), ((3, 3), (4, 3))),
+            ),
+            (True, False),
+            (True, False),
+            (True, False),
+        )
+    ]
+
+
 def gps_ignoreparent(strategy):
     return [
         strategy(gps=gps, ignore_parent=ignore_parent)
@@ -376,8 +396,8 @@ strategy_objects = (
     + subreqs_partial_ignoreparent_dirs(RequirementPlacementFactory)
     + [SymmetriesFactory(), BasicVerificationStrategy(), EmptyCellInferralFactory()]
     + partition_ignoreparent_workable(FactorStrategy)
-    + partition_ignoreparent_workable(FactorWithInterleavingStrategy)
-    + partition_ignoreparent_workable(FactorWithMonotoneInterleavingStrategy)
+    + partition_ignoreparent_workable_tracked(FactorWithInterleavingStrategy)
+    + partition_ignoreparent_workable_tracked(FactorWithMonotoneInterleavingStrategy)
     + ignoreparent(DatabaseVerificationStrategy)
     + ignoreparent(LocallyFactorableVerificationStrategy)
     + ignoreparent(ElementaryVerificationStrategy)