Created
February 2, 2026 06:13
-
-
Save jpadams/a1afd771410655d29e8d78f609268c1e to your computer and use it in GitHub Desktop.
Cypher script to generate a Dobble / Spot it! deck (full 57 cards, not just 55. not exact correspondence with commercial version)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ======================================================= | |
| // Dobble / Spot It! (order 7 projective plane) generator | |
| // cypher-shell friendly: each semicolon ends a statement, | |
| // and NO variables are expected to persist across statements. | |
| // ======================================================= | |
| // ---------- OPTIONAL CLEAN SLATE (uncomment if desired) ---------- | |
| // MATCH (n) WHERE n:Point OR n:Line OR n:Symbol OR n:Card DETACH DELETE n; | |
| // ======================================================= | |
| // 1) Create 57 Points/Symbols (pointId 0..56) | |
| // ======================================================= | |
| WITH [ | |
| "Anchor", | |
| "Apple", | |
| "Baby bottle", | |
| "Bomb", | |
| "Cactus", | |
| "Candle", | |
| "Taxi car", | |
| "Carrot", | |
| "Chess knight", | |
| "Clock", | |
| "Clown", | |
| "Daisy flower", | |
| "Dinosaur", | |
| "Dolphin", | |
| "Dragon", | |
| "Exclamation point", | |
| "Eye", | |
| "Fire", | |
| "Four leaf clover", | |
| "Ghost", | |
| "Green splats", | |
| "Hammer", | |
| "Heart", | |
| "Ice cube", | |
| "Igloo", | |
| "Key", | |
| "Ladybird (Ladybug)", | |
| "Light bulb", | |
| "Lightning bolt", | |
| "Lock", | |
| "Maple leaf", | |
| "Moon", | |
| "No Entry sign", | |
| "Orange scarecrow man", | |
| "Pencil", | |
| "Purple bird", | |
| "Purple cat", | |
| "Purple dobble hand man", | |
| "Red lips", | |
| "Scissors", | |
| "Skull and crossbones", | |
| "Snowflake", | |
| "Snowman", | |
| "Spider", | |
| "Spider web", | |
| "Sun", | |
| "Sunglasses", | |
| "Target/crosshairs", | |
| "Tortoise", | |
| "Treble clef", | |
| "Tree", | |
| "Water drop", | |
| "Dog", | |
| "Yin and Yang", | |
| "Zebra", | |
| "Question mark", | |
| "Cheese" | |
| ] AS symbols | |
| UNWIND range(0,56) AS pid | |
| WITH pid, | |
| symbols[pid] AS name, | |
| CASE WHEN pid < 49 THEN "affine" ELSE "infinity" END AS kind, | |
| CASE WHEN pid < 49 THEN toInteger(floor(pid / 7.0)) ELSE null END AS x, | |
| CASE WHEN pid < 49 THEN (pid % 7) ELSE null END AS y, | |
| CASE | |
| WHEN pid = 56 THEN "vertical" | |
| WHEN pid >= 49 AND pid <= 55 THEN (pid - 49) | |
| ELSE null | |
| END AS slope | |
| MERGE (p:Point:Symbol {pointId: pid}) | |
| SET p.name = name, | |
| p.kind = kind, | |
| p.x = x, | |
| p.y = y, | |
| p.slope = slope; | |
| // ======================================================= | |
| // 2) Create 57 Lines/Cards (cardId 0..56) | |
| // 0..48 : y = m*x + b where m,b in 0..6 (cid = m*7 + b) | |
| // 49..55 : x = c where c in 0..6 (cid = 49 + c) | |
| // 56 : line_at_infinity | |
| // ======================================================= | |
| UNWIND range(0,56) AS cid | |
| WITH cid, | |
| CASE | |
| WHEN cid < 49 THEN "affine" | |
| WHEN cid < 56 THEN "vertical" | |
| ELSE "infinity" | |
| END AS kind, | |
| CASE WHEN cid < 49 THEN toInteger(floor(cid / 7.0)) ELSE null END AS m, | |
| CASE WHEN cid < 49 THEN (cid % 7) ELSE null END AS b, | |
| CASE WHEN cid >= 49 AND cid <= 55 THEN (cid - 49) ELSE null END AS x, | |
| CASE | |
| WHEN cid < 49 THEN ("y=" + toString(toInteger(floor(cid / 7.0))) + "x+" + toString(cid % 7)) | |
| WHEN cid < 56 THEN ("x=" + toString(cid - 49)) | |
| ELSE "line_at_infinity" | |
| END AS label | |
| MERGE (l:Line:Card {cardId: cid}) | |
| SET l.kind = kind, | |
| l.m = m, | |
| l.b = b, | |
| l.x = x, | |
| l.label = label; | |
| // ======================================================= | |
| // 3) Incidence edges (:Point)-[:ON]->(:Line) | |
| // ======================================================= | |
| // 3a) Affine lines contain 7 affine points | |
| MATCH (l:Line:Card {kind:"affine"}) | |
| WITH l, l.m AS m, l.b AS b | |
| UNWIND range(0,6) AS x | |
| WITH l, m, b, x, ((m*x + b) % 7) AS y | |
| MATCH (p:Point:Symbol {kind:"affine", x:x, y:y}) | |
| MERGE (p)-[:ON]->(l); | |
| // 3b) Each affine line also contains its slope infinity point | |
| MATCH (l:Line:Card {kind:"affine"}) | |
| WITH l, l.m AS m | |
| MATCH (inf:Point:Symbol {kind:"infinity", slope:m}) | |
| MERGE (inf)-[:ON]->(l); | |
| // 3c) Vertical lines contain 7 affine points | |
| MATCH (l:Line:Card {kind:"vertical"}) | |
| WITH l, l.x AS c | |
| UNWIND range(0,6) AS y | |
| MATCH (p:Point:Symbol {kind:"affine", x:c, y:y}) | |
| MERGE (p)-[:ON]->(l); | |
| // 3d) Vertical lines also contain the vertical infinity point | |
| MATCH (l:Line:Card {kind:"vertical"}) | |
| MATCH (inf:Point:Symbol {kind:"infinity", slope:"vertical"}) | |
| MERGE (inf)-[:ON]->(l); | |
| // 3e) The line at infinity contains all 8 infinity points | |
| MATCH (l:Line:Card {kind:"infinity"}) | |
| MATCH (inf:Point:Symbol {kind:"infinity"}) | |
| MERGE (inf)-[:ON]->(l); | |
| // ======================================================= | |
| // 4) Checks | |
| // ======================================================= | |
| // Each card has 8 symbols | |
| MATCH (c:Card)<-[:ON]-(s:Symbol) | |
| WITH c, count(s) AS k | |
| RETURN min(k) AS minSymbolsPerCard, max(k) AS maxSymbolsPerCard; | |
| // Each symbol appears on 8 cards | |
| MATCH (s:Symbol)-[:ON]->(c:Card) | |
| WITH s, count(c) AS k | |
| RETURN min(k) AS minCardsPerSymbol, max(k) AS maxCardsPerSymbol; | |
| // Any two cards share exactly 1 symbol | |
| MATCH (c1:Card)<-[:ON]-(s:Symbol)-[:ON]->(c2:Card) | |
| WHERE c1.cardId < c2.cardId | |
| WITH c1, c2, count(DISTINCT s) AS shared | |
| WITH shared, count(*) AS pairCount | |
| RETURN shared, pairCount | |
| ORDER BY shared; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment