quantik-core 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ """
2
+ Quantik Core - High-performance game state manipulation library.
3
+
4
+ This library provides the foundational components for building Quantik game engines,
5
+ Monte Carlo simulations, and AI analysis tools.
6
+ """
7
+
8
+ from .core import State, VERSION, FLAG_CANON, D4, permute16, ALL_SHAPE_PERMS
9
+
10
+ __version__ = "0.1.0"
11
+ __author__ = "Mauro Berlanda"
12
+
13
+ __all__ = [
14
+ "State",
15
+ "VERSION",
16
+ "FLAG_CANON",
17
+ "D4",
18
+ "permute16",
19
+ "ALL_SHAPE_PERMS",
20
+ ]
File without changes
quantik_core/core.py ADDED
@@ -0,0 +1,174 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Tuple
3
+ import itertools
4
+ import struct
5
+
6
+ # --- versioning/flags --------------------------------------------------------
7
+ VERSION = 1
8
+ FLAG_CANON = 1 << 1 # bit1
9
+
10
+ # --- board indexing ----------------------------------------------------------
11
+ def rc_to_i(r: int, c: int) -> int: return r * 4 + c
12
+ def i_to_rc(i: int) -> Tuple[int, int]: return divmod(i, 4)
13
+
14
+ def build_perm(fn):
15
+ m = [0]*16
16
+ for i in range(16):
17
+ r, c = i_to_rc(i)
18
+ r2, c2 = fn(r, c)
19
+ m[i] = rc_to_i(r2, c2)
20
+ return m
21
+
22
+ # 8 D4 symmetries
23
+ D4 = [
24
+ ("id", build_perm(lambda r,c:(r, c ))),
25
+ ("rot90", build_perm(lambda r,c:(c, 3-r))),
26
+ ("rot180", build_perm(lambda r,c:(3-r, 3-c))),
27
+ ("rot270", build_perm(lambda r,c:(3-c, r ))),
28
+ ("reflV", build_perm(lambda r,c:(r, 3-c))),
29
+ ("reflH", build_perm(lambda r,c:(3-r, c ))),
30
+ ("reflD", build_perm(lambda r,c:(c, r ))),
31
+ ("reflAD", build_perm(lambda r,c:(3-c, 3-r))),
32
+ ]
33
+
34
+ # --- precomputed LUT: 8 × 65,536 --------------------------------------------
35
+ # perm16[S][mask] -> transformed 16-bit mask
36
+ def _build_perm16_lut() -> List[List[int]]:
37
+ tables: List[List[int]] = []
38
+ for _, mapping in D4:
39
+ t = [0]*65536
40
+ for x in range(65536):
41
+ y = 0
42
+ # scatter bits by mapping[i] in tight loop
43
+ m = x
44
+ i = 0
45
+ while m:
46
+ if m & 1:
47
+ y |= 1 << mapping[i]
48
+ i += 1
49
+ m >>= 1
50
+ # finish remaining zeros if any
51
+ while i < 16:
52
+ # (no-op; just advance)
53
+ i += 1
54
+ t[x] = y
55
+ tables.append(t)
56
+ return tables
57
+
58
+ _perm16 = _build_perm16_lut() # ~1.0 MB RAM, fast and worth it
59
+
60
+ # Convenience function for external use
61
+ def permute16(mask: int, mapping: List[int]) -> int:
62
+ """Apply a 16-element permutation to a 16-bit mask."""
63
+ result = 0
64
+ for i in range(16):
65
+ if (mask >> i) & 1:
66
+ result |= 1 << mapping[i]
67
+ return result
68
+
69
+ ALL_SHAPE_PERMS = list(itertools.permutations(range(4))) # 24 tuples
70
+
71
+ @dataclass(frozen=True)
72
+ class State:
73
+ # bitboards in order C0S0..C0S3, C1S0..C1S3 (each uint16)
74
+ bb: Tuple[int, int, int, int, int, int, int, int]
75
+
76
+ @staticmethod
77
+ def empty(): return State((0,0,0,0,0,0,0,0))
78
+
79
+ # ----- binary core (18 bytes: B B 8H) ------------------------------------
80
+ def pack(self, flags: int = 0) -> bytes:
81
+ return struct.pack("<BB8H", VERSION, flags, *self.bb)
82
+
83
+ @staticmethod
84
+ def unpack(data: bytes) -> "State":
85
+ if len(data) < 18: raise ValueError("Buffer too small for v1 core (18 bytes).")
86
+ ver, flags, *rest = struct.unpack("<BB8H", data[:18])
87
+ if ver != VERSION: raise ValueError(f"Unsupported version {ver}")
88
+ bb = tuple(int(x) & 0xFFFF for x in rest)
89
+ return State(bb) # flags ignored in state; carried in header
90
+
91
+ # ----- human-friendly (QFEN) ---------------------------------------------
92
+ SHAPE_LETTERS = "ABCD"
93
+
94
+ def to_qfen(self) -> str:
95
+ grid = []
96
+ for r in range(4):
97
+ row = []
98
+ for c in range(4):
99
+ i = rc_to_i(r,c)
100
+ ch = "."
101
+ for color in (0,1):
102
+ for s in range(4):
103
+ if (self.bb[color*4 + s] >> i) & 1:
104
+ letter = State.SHAPE_LETTERS[s]
105
+ ch = letter if color == 0 else letter.lower()
106
+ row.append(ch)
107
+ grid.append("".join(row))
108
+ return "/".join(grid)
109
+
110
+ @staticmethod
111
+ def from_qfen(qfen: str) -> "State":
112
+ parts = [p.strip() for p in qfen.replace(" ", "").split("/")]
113
+ if len(parts) != 4 or any(len(p) != 4 for p in parts):
114
+ raise ValueError("QFEN must be 4 ranks of 4 chars separated by '/'")
115
+ bb = [0]*8
116
+ letter_to_shape = {ch:i for i,ch in enumerate(State.SHAPE_LETTERS)}
117
+ for r in range(4):
118
+ for c in range(4):
119
+ ch = parts[r][c]
120
+ if ch == ".": continue
121
+ color = 0 if ch.isupper() else 1
122
+ s = letter_to_shape[ch.upper()]
123
+ bb[color*4 + s] |= 1 << rc_to_i(r,c)
124
+ return State(tuple(bb))
125
+
126
+ # ----- canonicalization (uses LUT) ---------------------------------------
127
+ def canonical_payload(self) -> bytes:
128
+ best = None
129
+ B = [[self.bb[c*4 + s] for s in range(4)] for c in range(2)]
130
+ for s_idx, _ in enumerate(D4):
131
+ lut = _perm16[s_idx]
132
+ # geometry
133
+ G0 = [lut[B[0][s]] for s in range(4)]
134
+ G1 = [lut[B[1][s]] for s in range(4)]
135
+ for color_swap in (0,1):
136
+ C0, C1 = (G0, G1) if color_swap == 0 else (G1, G0)
137
+ for perm in ALL_SHAPE_PERMS:
138
+ flat = [C0[perm[0]], C0[perm[1]], C0[perm[2]], C0[perm[3]],
139
+ C1[perm[0]], C1[perm[1]], C1[perm[2]], C1[perm[3]]]
140
+ candidate = struct.pack("<8H", *flat)
141
+ if best is None or candidate < best:
142
+ best = candidate
143
+ return best # 16 bytes
144
+
145
+ def canonical_key(self) -> bytes:
146
+ return bytes([VERSION, FLAG_CANON]) + self.canonical_payload()
147
+
148
+ # ----- CBOR wrappers (portable, self-describing) -------------------------
149
+ # { "v":1, "canon":bool, "bb": h'16bytes', ? "mc":uint, ? "meta":{...} }
150
+ def to_cbor(self, canon: bool = False, mc: int | None = None, meta: dict | None = None) -> bytes:
151
+ try:
152
+ import cbor2 # pip install cbor2
153
+ except ImportError:
154
+ raise RuntimeError("Please install cbor2 (pip install cbor2)")
155
+ payload = struct.pack("<8H", *self.bb)
156
+ m = {"v": VERSION, "canon": bool(canon), "bb": payload}
157
+ if mc is not None: m["mc"] = int(mc)
158
+ if meta: m["meta"] = meta
159
+ return cbor2.dumps(m)
160
+
161
+ @staticmethod
162
+ def from_cbor(data: bytes) -> "State":
163
+ try:
164
+ import cbor2
165
+ except ImportError:
166
+ raise RuntimeError("Please install cbor2 (pip install cbor2)")
167
+ m = cbor2.loads(data)
168
+ if m.get("v") != VERSION: raise ValueError("Unsupported CBOR version")
169
+ bb = m.get("bb")
170
+ if not isinstance(bb, (bytes, bytearray)) or len(bb) != 16:
171
+ raise ValueError("CBOR field 'bb' must be 16 bytes")
172
+ vals = struct.unpack("<8H", bb)
173
+ return State(tuple(int(x) & 0xFFFF for x in vals))
174
+
File without changes
quantik_core/move.py ADDED
File without changes
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.4
2
+ Name: quantik-core
3
+ Version: 0.1.0
4
+ Summary: High-performance core utilities for Quantik game state manipulation
5
+ Author-email: Mauro Berlanda <mauro.berlanda@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mauroberlanda/quantik-core-py
8
+ Project-URL: Repository, https://github.com/mauroberlanda/quantik-core-py
9
+ Project-URL: Documentation, https://quantik-core-py.readthedocs.io
10
+ Project-URL: Bug Tracker, https://github.com/mauroberlanda/quantik-core-py/issues
11
+ Keywords: quantik,board-games,game-ai,monte-carlo,bitboards,game-engine,mcts,game-theory,combinatorial-games
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Games/Entertainment :: Board Games
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
+ Requires-Dist: hypothesis>=6.0; extra == "dev"
31
+ Requires-Dist: black>=23.0; extra == "dev"
32
+ Requires-Dist: flake8>=6.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.0; extra == "dev"
34
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
35
+ Provides-Extra: cbor
36
+ Requires-Dist: cbor2>=5.4.0; extra == "cbor"
37
+ Provides-Extra: docs
38
+ Requires-Dist: sphinx>=5.0; extra == "docs"
39
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
40
+ Provides-Extra: benchmark
41
+ Requires-Dist: pytest-benchmark>=4.0; extra == "benchmark"
42
+ Provides-Extra: all
43
+ Requires-Dist: quantik-core[benchmark,cbor,dev,docs]; extra == "all"
44
+ Dynamic: license-file
45
+
46
+ # Quantik Core
47
+
48
+ A high-performance Python library for manipulating Quantik game states, optimized for Monte Carlo simulations, game analysis, and AI engines.
49
+
50
+ ## What is Quantik?
51
+
52
+ Quantik is an elegant 4×4 abstract strategy game where players compete to complete lines with all four unique shapes.
53
+
54
+ ### Game Rules
55
+
56
+ - **Board**: 4×4 grid (16 squares)
57
+ - **Pieces**: 4 different shapes (A, B, C, D) in 2 colors (one per player)
58
+ - **Objective**: Be the first to complete a **row**, **column**, or **2×2 zone** containing all four different shapes
59
+ - **Gameplay**:
60
+ - Players alternate placing one of their remaining pieces on an empty square
61
+ - A piece cannot be placed if the opponent already has the same shape in the target square's row, column, or 2×2 zone
62
+ - Colors don't matter for winning - only the presence of all four shapes in a line
63
+
64
+ ### Example Victory
65
+
66
+ ```
67
+ A B C D ← Row with all 4 shapes = WIN!
68
+ . . . .
69
+ . . . .
70
+ . . . .
71
+ ```
72
+
73
+ ## Features
74
+
75
+ This library provides the core foundation for building:
76
+
77
+ - **Monte Carlo Tree Search (MCTS)** engines
78
+ - **Game analysis** and position evaluation systems
79
+ - **AI training** and recommendation engines
80
+ - **Opening book** generation and endgame databases
81
+ - **Statistical analysis** of game patterns
82
+ - **Game engines** and tournament systems
83
+ - **Research tools** for combinatorial game theory
84
+
85
+ **Current Implementation:**
86
+ - **State Representation**: Complete bitboard-based game state management
87
+ - **Serialization**: Binary, QFEN, and CBOR formats
88
+ - **Canonicalization**: Symmetry-aware position normalization
89
+ - **Move Generation**: Coming in next release
90
+ - **Game Logic**: Win detection and move validation (planned)
91
+
92
+ ## Core Capabilities
93
+
94
+ - **Blazing Fast Operations**: Bitboard-based representation enables O(1) move generation and win detection
95
+ - **Compact Memory Footprint**: Game states fit in just 16 bytes with optional 18-byte canonical serialization
96
+ - **Symmetry Normalization**: Automatic canonicalization under rotations, reflections, color swaps, and shape relabeling
97
+ - **Cross-Language Compatibility**: Binary format designed for interoperability with Go, Rust, and other engines
98
+ - **Human-Readable Format**: QFEN (Quantik FEN) notation for debugging and documentation
99
+ - **Self-Describing Serialization**: CBOR-based format for robust data exchange
100
+
101
+ ## Installation
102
+
103
+ ```bash
104
+ pip install quantik-core
105
+ ```
106
+
107
+ ## Quick Start
108
+
109
+ ```python
110
+ from quantik_core import State
111
+
112
+ # Create an empty game state
113
+ state = State.empty()
114
+
115
+ # Create a position using QFEN notation
116
+ state = State.from_qfen("A.../..b./.c../...D")
117
+
118
+ # Convert to human-readable format
119
+ qfen = state.to_qfen()
120
+ print(f"Position: {qfen}") # Output: A.../..b./.c../...D
121
+
122
+ # Get canonical representation for symmetry analysis
123
+ canonical_key = state.canonical_key()
124
+ print(f"Canonical key: {canonical_key.hex()}")
125
+
126
+ # Serialize to binary format (18 bytes)
127
+ binary_data = state.pack()
128
+ restored_state = State.unpack(binary_data)
129
+
130
+ # Serialize to CBOR for cross-language compatibility
131
+ cbor_data = state.to_cbor(canon=True, meta={"game_id": 123})
132
+ restored_from_cbor = State.from_cbor(cbor_data)
133
+ ```
134
+
135
+ ## Performance
136
+
137
+ - **State Operations**: Bitboard-based representation enables fast position manipulation
138
+ - **Canonicalization**: <1µs per position with precomputed lookup tables
139
+ - **Memory Usage**: 16 bytes per game state + 1MB for transformation LUTs
140
+ - **Serialization**: 18-byte binary format, human-readable QFEN, or self-describing CBOR
141
+
142
+ ## Use Cases
143
+
144
+ ### Position Analysis and Canonicalization
145
+ ```python
146
+ from quantik_core import State
147
+
148
+ # Create different equivalent positions
149
+ pos1 = State.from_qfen("A.../..../..../....")
150
+ pos2 = State.from_qfen("..../..../..../.a..") # Rotated + color swapped
151
+
152
+ # Both have the same canonical representation
153
+ assert pos1.canonical_key() == pos2.canonical_key()
154
+ ```
155
+
156
+ ### Database Storage and Retrieval
157
+ ```python
158
+ # Use canonical keys as database indices
159
+ positions_db = {}
160
+ canonical_key = state.canonical_key()
161
+ positions_db[canonical_key] = {"eval": 0.75, "visits": 1000}
162
+ ```
163
+
164
+ ### Cross-Language Data Exchange
165
+ ```python
166
+ # Save position with metadata for other engines
167
+ data = state.to_cbor(
168
+ canon=True,
169
+ mc=5000, # Monte Carlo simulations
170
+ meta={"depth": 12, "engine": "quantik-py-v1"}
171
+ )
172
+
173
+ # Binary format for high-performance applications
174
+ binary = state.pack() # Just 18 bytes
175
+ ```
176
+
177
+ ## Technical Details
178
+
179
+ - **Representation**: 8 disjoint 16-bit bitboards (one per color-shape combination)
180
+ - **Symmetries**: Dihedral group D4 (8 rotations/reflections) × color swap × shape permutations = 384 total
181
+ - **Serialization**: Versioned binary format with little-endian 16-bit words
182
+ - **Canonicalization**: Lexicographically minimal representation across symmetry orbit
183
+
184
+ ## Contributing
185
+
186
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
187
+
188
+ ## License
189
+
190
+ MIT License - see [LICENSE](LICENSE) for details.
191
+
192
+ ## Citation
193
+
194
+ If you use this library in research, please cite:
195
+ ```bibtex
196
+ @software{quantik_core,
197
+ title={Quantik Core: High-Performance Game State Manipulation},
198
+ author={Mauro Berlanda},
199
+ year={2025},
200
+ url={https://github.com/mberlanda/quantik-core-py}
201
+ }
202
+ ```
@@ -0,0 +1,10 @@
1
+ quantik_core/__init__.py,sha256=5HvOlxTlTPyRUWmUsmO0PLeo3bJCRk_ltoNuzJRJ3kM,450
2
+ quantik_core/constants.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ quantik_core/core.py,sha256=XATOKchOzn__k60EWvEi-Fz_M-CGI5YCFGZSG1AjIlE,6537
4
+ quantik_core/exceptions.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ quantik_core/move.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ quantik_core-0.1.0.dist-info/licenses/LICENSE,sha256=92OE8T2tcKwZGSfkFTl-dyV8vJ_FI1HCM5ZHjiB3tOI,1071
7
+ quantik_core-0.1.0.dist-info/METADATA,sha256=gmg3lEuCVU-NF1iAw177Tyh9BuF8xR8zKX5dSwgnAaI,7205
8
+ quantik_core-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ quantik_core-0.1.0.dist-info/top_level.txt,sha256=m2JZsf9DeQBB24TxtvX9NjacmzBSaZ52a8IFj0XuvsY,13
10
+ quantik_core-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mauro Berlanda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ quantik_core