trianglengin 2.0.1__cp312-cp312-musllinux_1_2_i686.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.
Files changed (61) hide show
  1. trianglengin/__init__.py +35 -0
  2. trianglengin/config/README.md +38 -0
  3. trianglengin/config/__init__.py +8 -0
  4. trianglengin/config/display_config.py +47 -0
  5. trianglengin/config/env_config.py +62 -0
  6. trianglengin/core/__init__.py +10 -0
  7. trianglengin/cpp/CMakeLists.txt +42 -0
  8. trianglengin/cpp/bindings.cpp +211 -0
  9. trianglengin/cpp/config.h +28 -0
  10. trianglengin/cpp/game_state.cpp +327 -0
  11. trianglengin/cpp/game_state.h +73 -0
  12. trianglengin/cpp/grid_data.cpp +239 -0
  13. trianglengin/cpp/grid_data.h +78 -0
  14. trianglengin/cpp/grid_logic.cpp +125 -0
  15. trianglengin/cpp/grid_logic.h +30 -0
  16. trianglengin/cpp/shape_logic.cpp +100 -0
  17. trianglengin/cpp/shape_logic.h +28 -0
  18. trianglengin/cpp/structs.h +40 -0
  19. trianglengin/game_interface.py +222 -0
  20. trianglengin/py.typed +0 -0
  21. trianglengin/trianglengin_cpp.cpython-312-i386-linux-musl.so +0 -0
  22. trianglengin/trianglengin_cpp.cpython-37m-i386-linux-gnu.so +0 -0
  23. trianglengin/ui/README.md +35 -0
  24. trianglengin/ui/__init__.py +21 -0
  25. trianglengin/ui/app.py +107 -0
  26. trianglengin/ui/cli.py +123 -0
  27. trianglengin/ui/config.py +44 -0
  28. trianglengin/ui/interaction/README.md +44 -0
  29. trianglengin/ui/interaction/__init__.py +19 -0
  30. trianglengin/ui/interaction/debug_mode_handler.py +72 -0
  31. trianglengin/ui/interaction/event_processor.py +49 -0
  32. trianglengin/ui/interaction/input_handler.py +89 -0
  33. trianglengin/ui/interaction/play_mode_handler.py +156 -0
  34. trianglengin/ui/visualization/README.md +42 -0
  35. trianglengin/ui/visualization/__init__.py +58 -0
  36. trianglengin/ui/visualization/core/README.md +51 -0
  37. trianglengin/ui/visualization/core/__init__.py +16 -0
  38. trianglengin/ui/visualization/core/colors.py +115 -0
  39. trianglengin/ui/visualization/core/coord_mapper.py +85 -0
  40. trianglengin/ui/visualization/core/fonts.py +65 -0
  41. trianglengin/ui/visualization/core/layout.py +77 -0
  42. trianglengin/ui/visualization/core/visualizer.py +248 -0
  43. trianglengin/ui/visualization/drawing/README.md +49 -0
  44. trianglengin/ui/visualization/drawing/__init__.py +43 -0
  45. trianglengin/ui/visualization/drawing/grid.py +213 -0
  46. trianglengin/ui/visualization/drawing/highlight.py +31 -0
  47. trianglengin/ui/visualization/drawing/hud.py +43 -0
  48. trianglengin/ui/visualization/drawing/previews.py +181 -0
  49. trianglengin/ui/visualization/drawing/shapes.py +46 -0
  50. trianglengin/ui/visualization/drawing/utils.py +23 -0
  51. trianglengin/utils/__init__.py +9 -0
  52. trianglengin/utils/geometry.py +62 -0
  53. trianglengin/utils/types.py +10 -0
  54. trianglengin-2.0.1.dist-info/METADATA +250 -0
  55. trianglengin-2.0.1.dist-info/RECORD +61 -0
  56. trianglengin-2.0.1.dist-info/WHEEL +5 -0
  57. trianglengin-2.0.1.dist-info/entry_points.txt +2 -0
  58. trianglengin-2.0.1.dist-info/licenses/LICENSE +22 -0
  59. trianglengin-2.0.1.dist-info/top_level.txt +1 -0
  60. trianglengin.libs/libgcc_s-f3fb5a36.so.1 +0 -0
  61. trianglengin.libs/libstdc++-d2a021ba.so.6.0.32 +0 -0
@@ -0,0 +1,181 @@
1
+ import logging
2
+
3
+ # Guard UI imports
4
+ try:
5
+ import pygame
6
+
7
+ # Import DisplayConfig from the correct UI location
8
+ from trianglengin.ui.config import DisplayConfig
9
+
10
+ from ..core import colors # Relative import
11
+ from .shapes import draw_shape # Relative import
12
+ from .utils import get_triangle_points # Relative import
13
+ except ImportError as e:
14
+ raise ImportError(
15
+ "UI components require 'pygame'. Install with 'pip install trianglengin[ui]'."
16
+ ) from e
17
+
18
+ # Use absolute imports for core components
19
+ from trianglengin.config import EnvConfig
20
+ from trianglengin.game_interface import Shape
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def render_previews(
26
+ surface: pygame.Surface,
27
+ shapes: list[Shape | None], # Core Shape
28
+ area_topleft: tuple[int, int],
29
+ _mode: str,
30
+ env_config: EnvConfig, # Core EnvConfig
31
+ display_config: DisplayConfig, # UI DisplayConfig
32
+ selected_shape_idx: int = -1,
33
+ ) -> dict[int, pygame.Rect]:
34
+ """Renders shape previews in their area. Returns dict {index: screen_rect}."""
35
+ surface.fill(colors.PREVIEW_BG_COLOR)
36
+ preview_rects_screen: dict[int, pygame.Rect] = {}
37
+ num_slots = env_config.NUM_SHAPE_SLOTS
38
+ pad = display_config.PREVIEW_PADDING
39
+ inner_pad = display_config.PREVIEW_INNER_PADDING
40
+ border = display_config.PREVIEW_BORDER_WIDTH
41
+ selected_border = display_config.PREVIEW_SELECTED_BORDER_WIDTH
42
+
43
+ if num_slots <= 0:
44
+ return {}
45
+
46
+ total_pad_h = (num_slots + 1) * pad
47
+ available_h = surface.get_height() - total_pad_h
48
+ slot_h = available_h / num_slots if num_slots > 0 else 0
49
+ slot_w = surface.get_width() - 2 * pad
50
+ current_y = float(pad)
51
+
52
+ for i in range(num_slots):
53
+ slot_rect_local = pygame.Rect(pad, int(current_y), int(slot_w), int(slot_h))
54
+ # Calculate absolute screen rect for collision detection later
55
+ slot_rect_screen = slot_rect_local.move(area_topleft)
56
+ preview_rects_screen[i] = slot_rect_screen
57
+
58
+ shape: Shape | None = shapes[i] if i < len(shapes) else None
59
+ is_selected = selected_shape_idx == i
60
+ border_width = selected_border if is_selected else border
61
+ border_color = (
62
+ colors.PREVIEW_SELECTED_BORDER if is_selected else colors.PREVIEW_BORDER
63
+ )
64
+ # Draw border onto the local surface
65
+ pygame.draw.rect(surface, border_color, slot_rect_local, border_width)
66
+
67
+ if shape:
68
+ # Calculate drawing area inside the border and padding
69
+ draw_area_w = slot_w - 2 * (border_width + inner_pad)
70
+ draw_area_h = slot_h - 2 * (border_width + inner_pad)
71
+ if draw_area_w > 0 and draw_area_h > 0:
72
+ min_r, min_c, max_r, max_c = shape.bbox()
73
+ shape_rows = max_r - min_r + 1
74
+ # Effective columns considering 0.75 width factor for triangles
75
+ shape_cols_eff = (
76
+ (max_c - min_c + 1) * 0.75 + 0.25 if shape.triangles else 1
77
+ )
78
+ # Calculate scale to fit shape within draw area
79
+ scale_w = (
80
+ draw_area_w / shape_cols_eff if shape_cols_eff > 0 else draw_area_w
81
+ )
82
+ scale_h = draw_area_h / shape_rows if shape_rows > 0 else draw_area_h
83
+ cell_size = max(1.0, min(scale_w, scale_h)) # Use smallest scale
84
+
85
+ # Calculate centered top-left position for drawing the shape
86
+ shape_render_w = shape_cols_eff * cell_size
87
+ shape_render_h = shape_rows * cell_size
88
+ draw_topleft_x = (
89
+ slot_rect_local.left
90
+ + border_width
91
+ + inner_pad
92
+ + (draw_area_w - shape_render_w) / 2
93
+ )
94
+ draw_topleft_y = (
95
+ slot_rect_local.top
96
+ + border_width
97
+ + inner_pad
98
+ + (draw_area_h - shape_render_h) / 2
99
+ )
100
+ # Draw the shape onto the local surface
101
+ draw_shape(
102
+ surface,
103
+ shape,
104
+ (int(draw_topleft_x), int(draw_topleft_y)),
105
+ cell_size,
106
+ _is_selected=is_selected,
107
+ origin_offset=(-min_r, -min_c), # Adjust drawing origin
108
+ )
109
+ current_y += slot_h + pad
110
+ return preview_rects_screen
111
+
112
+
113
+ def draw_placement_preview(
114
+ surface: pygame.Surface,
115
+ shape: Shape, # Core Shape
116
+ r: int,
117
+ c: int,
118
+ is_valid: bool,
119
+ cw: float,
120
+ ch: float,
121
+ ox: float,
122
+ oy: float,
123
+ ) -> None:
124
+ """Draws a semi-transparent shape snapped to the grid using pre-calculated parameters."""
125
+ if not shape or not shape.triangles:
126
+ return
127
+ if cw <= 0 or ch <= 0:
128
+ return
129
+
130
+ alpha = 100
131
+ base_color = (
132
+ colors.PLACEMENT_VALID_COLOR if is_valid else colors.PLACEMENT_INVALID_COLOR
133
+ )
134
+ color: colors.ColorRGBA = (base_color[0], base_color[1], base_color[2], alpha)
135
+ # Create a temporary surface for alpha blending
136
+ temp_surface = pygame.Surface(surface.get_size(), pygame.SRCALPHA)
137
+ temp_surface.fill((0, 0, 0, 0)) # Transparent background
138
+
139
+ for dr, dc, is_up in shape.triangles:
140
+ tri_r, tri_c = r + dr, c + dc
141
+ # Use helper to get points relative to grid origin
142
+ pts = get_triangle_points(tri_r, tri_c, is_up, ox, oy, cw, ch)
143
+ pygame.draw.polygon(temp_surface, color, pts)
144
+
145
+ # Blit the temporary surface onto the main surface
146
+ surface.blit(temp_surface, (0, 0))
147
+
148
+
149
+ def draw_floating_preview(
150
+ surface: pygame.Surface,
151
+ shape: Shape, # Core Shape
152
+ screen_pos: tuple[int, int],
153
+ ) -> None:
154
+ """Draws a semi-transparent shape floating at the screen position."""
155
+ if not shape or not shape.triangles:
156
+ return
157
+
158
+ cell_size = 20.0 # Fixed size for floating preview
159
+ alpha = 100
160
+ color: colors.ColorRGBA = (shape.color[0], shape.color[1], shape.color[2], alpha)
161
+ # Create a temporary surface for alpha blending
162
+ temp_surface = pygame.Surface(surface.get_size(), pygame.SRCALPHA)
163
+ temp_surface.fill((0, 0, 0, 0)) # Transparent background
164
+
165
+ # Center the shape around the screen_pos
166
+ min_r, min_c, max_r, max_c = shape.bbox()
167
+ center_r = (min_r + max_r) / 2.0
168
+ center_c = (min_c + max_c) / 2.0
169
+
170
+ for dr, dc, is_up in shape.triangles:
171
+ # Calculate position relative to the center of the shape bbox
172
+ pt_x = screen_pos[0] + (dc - center_c) * (cell_size * 0.75)
173
+ pt_y = screen_pos[1] + (dr - center_r) * cell_size
174
+ # Use helper to get points relative to (0,0) for local drawing
175
+ rel_pts = get_triangle_points(0, 0, is_up, 0, 0, cell_size, cell_size)
176
+ # Translate points to the calculated screen position
177
+ pts = [(px + pt_x, py + pt_y) for px, py in rel_pts]
178
+ pygame.draw.polygon(temp_surface, color, pts)
179
+
180
+ # Blit the temporary surface onto the main surface
181
+ surface.blit(temp_surface, (0, 0))
@@ -0,0 +1,46 @@
1
+ # Guard UI imports
2
+ try:
3
+ import pygame
4
+
5
+ from ..core import colors # Relative import
6
+ from .utils import get_triangle_points # Relative import
7
+ except ImportError as e:
8
+ raise ImportError(
9
+ "UI components require 'pygame'. Install with 'pip install trianglengin[ui]'."
10
+ ) from e
11
+
12
+ # Use absolute imports for core components
13
+ from trianglengin.game_interface import Shape
14
+
15
+
16
+ def draw_shape(
17
+ surface: pygame.Surface,
18
+ shape: Shape, # Core Shape
19
+ topleft: tuple[int, int],
20
+ cell_size: float,
21
+ _is_selected: bool = False, # Keep underscore if not used visually
22
+ origin_offset: tuple[int, int] = (0, 0),
23
+ ) -> None:
24
+ """Draws a single shape onto a surface."""
25
+ if not shape or not shape.triangles or cell_size <= 0:
26
+ return
27
+
28
+ shape_color = shape.color
29
+ border_color = colors.GRAY # Use a defined border color
30
+ cw = cell_size
31
+ ch = cell_size # Assuming square aspect ratio for cells in preview
32
+
33
+ for dr, dc, is_up in shape.triangles:
34
+ # Adjust relative coords by origin offset (used for centering in previews)
35
+ adj_r, adj_c = dr + origin_offset[0], dc + origin_offset[1]
36
+ # Calculate top-left corner of the bounding box for this triangle
37
+ tri_x = topleft[0] + adj_c * (cw * 0.75)
38
+ tri_y = topleft[1] + adj_r * ch
39
+ # Use helper to get points relative to (0,0) for local drawing
40
+ # then translate them to the calculated tri_x, tri_y
41
+ pts = [
42
+ (px + tri_x, py + tri_y)
43
+ for px, py in get_triangle_points(0, 0, is_up, 0, 0, cw, ch)
44
+ ]
45
+ pygame.draw.polygon(surface, shape_color, pts)
46
+ pygame.draw.polygon(surface, border_color, pts, 1) # Draw border
@@ -0,0 +1,23 @@
1
+ """Utility functions for the drawing module."""
2
+
3
+
4
+ def get_triangle_points(
5
+ r: int, c: int, is_up: bool, ox: float, oy: float, cw: float, ch: float
6
+ ) -> list[tuple[float, float]]:
7
+ """
8
+ Calculates vertex points for drawing a triangle, relative to origin (ox, oy).
9
+ """
10
+ # Top-left corner of the bounding rectangle for the cell (r, c)
11
+ # Note the horizontal offset depends only on column (c * 0.75 * cw)
12
+ # Note the vertical offset depends only on row (r * ch)
13
+ x = ox + c * (cw * 0.75)
14
+ y = oy + r * ch
15
+
16
+ if is_up:
17
+ # Points for an upward-pointing triangle (base at bottom)
18
+ # Vertices: Bottom-left, Bottom-right, Top-middle
19
+ return [(x, y + ch), (x + cw, y + ch), (x + cw / 2, y)]
20
+ else:
21
+ # Points for a downward-pointing triangle (base at top)
22
+ # Vertices: Top-left, Top-right, Bottom-middle
23
+ return [(x, y), (x + cw, y), (x + cw / 2, y + ch)]
@@ -0,0 +1,9 @@
1
+ """
2
+ General utilities for the Triangle Engine.
3
+ """
4
+
5
+ # --- ADDED: Export geometry ---
6
+ from . import geometry
7
+ from .types import ActionType
8
+
9
+ __all__ = ["ActionType", "geometry"]
@@ -0,0 +1,62 @@
1
+ # File: src/trianglengin/utils/geometry.py
2
+ from collections.abc import Sequence # Import Tuple
3
+
4
+
5
+ def is_point_in_polygon(
6
+ point: tuple[float, float], polygon: Sequence[tuple[float, float]]
7
+ ) -> bool:
8
+ """
9
+ Checks if a point is inside a polygon using the Winding Number algorithm.
10
+ Handles points on the boundary correctly.
11
+
12
+ Args:
13
+ point: Tuple (x, y) representing the point coordinates.
14
+ polygon: Sequence of tuples [(x1, y1), (x2, y2), ...] representing polygon vertices in order.
15
+
16
+ Returns:
17
+ True if the point is inside or on the boundary of the polygon, False otherwise.
18
+ """
19
+ x, y = point
20
+ n = len(polygon)
21
+ if n < 3:
22
+ return False
23
+
24
+ wn = 0
25
+ epsilon = 1e-9
26
+
27
+ for i in range(n):
28
+ p1 = polygon[i]
29
+ p2 = polygon[(i + 1) % n]
30
+
31
+ if abs(p1[0] - x) < epsilon and abs(p1[1] - y) < epsilon:
32
+ return True
33
+ if (
34
+ abs(p1[1] - p2[1]) < epsilon
35
+ and abs(p1[1] - y) < epsilon
36
+ and min(p1[0], p2[0]) - epsilon <= x <= max(p1[0], p2[0]) + epsilon
37
+ ):
38
+ return True
39
+ if (
40
+ abs(p1[0] - p2[0]) < epsilon
41
+ and abs(p1[0] - x) < epsilon
42
+ and min(p1[1], p2[1]) - epsilon <= y <= max(p1[1], p2[1]) + epsilon
43
+ ):
44
+ return True
45
+
46
+ y_in_upward_range = p1[1] <= y + epsilon < p2[1] + epsilon
47
+ y_in_downward_range = p2[1] <= y + epsilon < p1[1] + epsilon
48
+
49
+ if y_in_upward_range or y_in_downward_range:
50
+ orientation = (p2[0] - p1[0]) * (y - p1[1]) - (x - p1[0]) * (p2[1] - p1[1])
51
+ if y_in_upward_range and orientation > epsilon:
52
+ wn += 1
53
+ elif y_in_downward_range and orientation < -epsilon:
54
+ wn -= 1
55
+ elif (
56
+ abs(orientation) < epsilon
57
+ and min(p1[0], p2[0]) - epsilon <= x <= max(p1[0], p2[0]) + epsilon
58
+ and min(p1[1], p2[1]) - epsilon <= y <= max(p1[1], p2[1]) + epsilon
59
+ ):
60
+ return True
61
+
62
+ return wn != 0
@@ -0,0 +1,10 @@
1
+ # File: trianglengin/trianglengin/utils/types.py
2
+ # Placeholder for Phase 1
3
+ """
4
+ Shared type definitions for the Triangle Engine.
5
+ """
6
+
7
+ from typing import TypeAlias
8
+
9
+ # Action representation (integer index)
10
+ ActionType: TypeAlias = int
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: trianglengin
3
+ Version: 2.0.1
4
+ Summary: High-performance C++/Python engine for a triangle puzzle game.
5
+ Author-email: "Luis Guilherme P. M." <lgpelin92@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/lguibr/trianglengin
8
+ Project-URL: Bug Tracker, https://github.com/lguibr/trianglengin/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: C++
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Topic :: Games/Entertainment :: Puzzle Games
16
+ Classifier: Development Status :: 4 - Beta
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: numpy>=1.20.0
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Requires-Dist: typing_extensions>=4.0.0
23
+ Requires-Dist: pygame>=2.1.0
24
+ Requires-Dist: typer[all]>=0.9.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
28
+ Requires-Dist: pytest-mock>=3.0.0; extra == "dev"
29
+ Requires-Dist: ruff; extra == "dev"
30
+ Requires-Dist: mypy; extra == "dev"
31
+ Requires-Dist: build; extra == "dev"
32
+ Requires-Dist: twine; extra == "dev"
33
+ Requires-Dist: codecov; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+
37
+ [![CI Status](https://github.com/lguibr/trianglengin/actions/workflows/ci_cd.yml/badge.svg)](https://github.com/lguibr/trianglengin/actions/workflows/ci_cd.yml)
38
+ [![codecov](https://codecov.io/gh/lguibr/trianglengin/graph/badge.svg?token=YOUR_CODECOV_TOKEN_HERE&flag=trianglengin)](https://codecov.io/gh/lguibr/trianglengin)
39
+ [![PyPI version](https://badge.fury.io/py/trianglengin.svg)](https://badge.fury.io/py/trianglengin)
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
41
+ [![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
42
+
43
+ # Triangle Engine (`trianglengin`) v2
44
+ <img src="bitmap.png" alt="trianglengin logo" width="300"/>
45
+
46
+ **Version 2 introduces a high-performance C++ core for the game logic.**
47
+
48
+ This library provides the core components for a triangle puzzle game, suitable for reinforcement learning agents or other applications requiring a fast game engine. Interactive play/debug modes are included.
49
+
50
+ It encapsulates:
51
+
52
+ 1. **Core Game Logic (C++):** High-performance implementation of environment rules, state representation, actions, placement validation, and line clearing. ([`src/trianglengin/cpp/README.md`](src/trianglengin/cpp/README.md))
53
+ 2. **Python Interface:** A Python `GameState` wrapper providing a user-friendly API to interact with the C++ core. ([`src/trianglengin/game_interface.py`](src/trianglengin/game_interface.py))
54
+ 3. **Configuration (Python/Pydantic):** Models for environment settings (`EnvConfig`). ([`src/trianglengin/config/README.md`](src/trianglengin/config/README.md))
55
+ 4. **Utilities (Python):** General helpers, geometry functions, shared types. ([`src/trianglengin/utils/README.md`](src/trianglengin/utils/README.md))
56
+ 5. **UI Components (Python/Pygame/Typer):** Basic visualization, interaction handling, and CLI for interactive modes. ([`src/trianglengin/ui/README.md`](src/trianglengin/ui/README.md))
57
+
58
+ ---
59
+
60
+ ## 🎮 The Ultimate Triangle Puzzle Guide 🧩
61
+
62
+ *(Game rules remain the same)*
63
+
64
+ *(... Game rules section remains unchanged ...)*
65
+
66
+ ---
67
+
68
+ ## Purpose
69
+
70
+ The primary goal is to provide a self-contained, installable library with a high-performance C++ core and a Python interface for the triangle puzzle game. This allows different RL agent implementations or other applications to build upon a consistent and fast game backend. The interactive UI is included but only initialized when running the specific UI commands.
71
+
72
+ ## Installation
73
+
74
+ **Prerequisites:**
75
+ * A C++ compiler supporting C++17 (e.g., GCC, Clang, MSVC).
76
+ * CMake (version 3.14 or higher).
77
+ * Python (>= 3.10) and Pip.
78
+
79
+ ```bash
80
+ # For standard use (once published or built):
81
+ pip install trianglengin
82
+
83
+ # Building from source (requires compiler and CMake):
84
+ git clone https://github.com/lguibr/trianglengin.git
85
+ cd trianglengin
86
+ pip install .
87
+ ```
88
+ *(Note: `pygame` and `typer` will be installed as core dependencies).*
89
+
90
+ ## Running Interactive Modes
91
+
92
+ After installing, you can run the interactive modes directly:
93
+
94
+ - **Play Mode:**
95
+ ```bash
96
+ trianglengin play [--seed 42] [--log-level INFO]
97
+ ```
98
+ - **Debug Mode:**
99
+ ```bash
100
+ trianglengin debug [--seed 42] [--log-level DEBUG]
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Local Development & Testing
106
+
107
+ These instructions are for developers contributing to `trianglengin`.
108
+
109
+ 1. **Clone the Repository:**
110
+ ```bash
111
+ git clone https://github.com/lguibr/trianglengin.git
112
+ cd trianglengin
113
+ ```
114
+
115
+ 2. **Prerequisites:** Ensure you have a C++17 compiler and CMake installed.
116
+
117
+ 3. **Create and Activate Virtual Environment:** (venv or conda)
118
+ ```bash
119
+ # Example using venv
120
+ python -m venv venv
121
+ source venv/bin/activate # or .\venv\Scripts\activate on Windows
122
+ ```
123
+ **IMPORTANT:** Ensure your virtual environment is activated.
124
+
125
+ 4. **Install Build Dependencies:** (Needed even for core dev)
126
+ ```bash
127
+ pip install pybind11>=2.10 cmake wheel
128
+ ```
129
+
130
+ 5. **Clean Previous Builds (Optional but Recommended):**
131
+ ```bash
132
+ rm -rf build/ src/trianglengin.egg-info/ dist/ src/trianglengin/trianglengin_cpp.*.so src/trianglengin/trianglengin_cpp*.cpp
133
+ ```
134
+
135
+ 6. **Install in Editable Mode with Dev Dependencies:**
136
+ * **Make sure your virtual environment is active!**
137
+ * Run from the project root directory:
138
+ ```bash
139
+ # Installs core, UI, and dev tools
140
+ pip install -e '.[dev]'
141
+ ```
142
+ This command compiles the C++ extension and installs the Python package so changes are reflected immediately. It also installs development tools.
143
+
144
+ 7. **Running Checks:**
145
+ * **Make sure your virtual environment is active!**
146
+ * **Tests & Coverage:**
147
+ ```bash
148
+ pytest tests/ --cov=src/trianglengin --cov-report=xml
149
+ ```
150
+ (Coverage measures core Python code, excluding UI).
151
+ To just run tests: `pytest`
152
+ * **Linting:** `ruff check .`
153
+ * **Formatting:** `ruff format .`
154
+ * **Type Checking:** `mypy src/trianglengin/ tests/`
155
+
156
+ 8. **Troubleshooting Build/Import Errors:**
157
+ * Ensure compiler and CMake are installed and in PATH.
158
+ * Make sure the virtual environment is active *before* running `pip install -e`.
159
+ * Clean previous builds (Step 5).
160
+ * Check `pyproject.toml` and `setup.py` for correct configuration.
161
+ * Verify Pybind11 version compatibility.
162
+
163
+ ---
164
+
165
+ ## Project Structure (v2 - UI Included)
166
+
167
+ ```
168
+ trianglengin/
169
+ ├── .github/workflows/ # GitHub Actions CI/CD
170
+ │ └── ci_cd.yml
171
+ ├── src/ # Source root
172
+ │ └── trianglengin/ # Python package source
173
+ │ ├── __init__.py # Exposes core public API (GameState, EnvConfig, Shape)
174
+ │ ├── game_interface.py # Python GameState wrapper class
175
+ │ ├── py.typed # PEP 561 marker
176
+ │ ├── cpp/ # C++ Core Implementation ([src/trianglengin/cpp/README.md])
177
+ │ │ ├── CMakeLists.txt
178
+ │ │ ├── bindings.cpp
179
+ │ │ ├── config.h
180
+ │ │ ├── structs.h
181
+ │ │ ├── grid_data.h / .cpp
182
+ │ │ ├── grid_logic.h / .cpp
183
+ │ │ ├── shape_logic.h / .cpp
184
+ │ │ └── game_state.h / .cpp
185
+ │ ├── core/ # Core Python components (now minimal/empty)
186
+ │ │ └── __init__.py
187
+ │ ├── utils/ # General Python utilities ([src/trianglengin/utils/README.md])
188
+ │ │ └── ... (geometry.py, types.py)
189
+ │ ├── config/ # Core configuration models ([src/trianglengin/config/README.md])
190
+ │ │ ├── __init__.py
191
+ │ │ └── env_config.py # EnvConfig (Pydantic)
192
+ │ └── ui/ # UI Components ([src/trianglengin/ui/README.md])
193
+ │ ├── __init__.py # Exposes UI API (Application, cli_app, DisplayConfig)
194
+ │ ├── app.py # Interactive mode application runner
195
+ │ ├── cli.py # CLI definition (play/debug)
196
+ │ ├── config.py # DisplayConfig (Pydantic)
197
+ │ ├── interaction/ # User input handling ([src/trianglengin/ui/interaction/README.md])
198
+ │ │ ├── __init__.py
199
+ │ │ ├── event_processor.py
200
+ │ │ ├── input_handler.py
201
+ │ │ ├── play_mode_handler.py
202
+ │ │ └── debug_mode_handler.py
203
+ │ └── visualization/ # Pygame rendering ([src/trianglengin/ui/visualization/README.md])
204
+ │ ├── __init__.py
205
+ │ ├── core/ # Core vis components ([src/trianglengin/ui/visualization/core/README.md])
206
+ │ │ ├── __init__.py
207
+ │ │ ├── colors.py
208
+ │ │ ├── coord_mapper.py
209
+ │ │ ├── fonts.py
210
+ │ │ ├── layout.py # NEW
211
+ │ │ └── visualizer.py
212
+ │ └── drawing/ # Drawing functions ([src/trianglengin/ui/visualization/drawing/README.md])
213
+ │ ├── __init__.py
214
+ │ ├── grid.py
215
+ │ ├── highlight.py
216
+ │ ├── hud.py
217
+ │ ├── previews.py
218
+ │ ├── shapes.py
219
+ │ └── utils.py
220
+ ├── tests/ # Unit/Integration tests (Python) ([tests/README.md])
221
+ │ ├── __init__.py
222
+ │ ├── conftest.py
223
+ │ └── core/environment/
224
+ │ └── test_game_state.py # Tests the Python GameState wrapper
225
+ ├── .gitignore
226
+ ├── pyproject.toml # Build config, dependencies
227
+ ├── setup.py # C++ Extension build script
228
+ ├── README.md # This file
229
+ ├── LICENSE
230
+ └── MANIFEST.in
231
+ ```
232
+
233
+ ## Core Components (v2)
234
+
235
+ - **`trianglengin.cpp` (C++ Core)**: Implements the high-performance game logic (state, grid, shapes, rules). Not directly imported in Python.
236
+ - **`trianglengin.game_interface.GameState` (Python Wrapper)**: The primary Python class for interacting with the game engine. It holds a reference to the C++ game state object and provides methods like `step`, `reset`, `is_over`, `valid_actions`, `get_shapes`, `get_grid_data_np`.
237
+ - **`trianglengin.config.EnvConfig`**: Python Pydantic model for core environment configuration. Passed to C++ core during initialization.
238
+ - **`trianglengin.utils`**: General Python utility functions and types. ([`src/trianglengin/utils/README.md`](src/trianglengin/utils/README.md))
239
+
240
+ ## UI Components (`trianglengin.ui`)
241
+
242
+ - **`trianglengin.ui.config.DisplayConfig`**: Pydantic model for UI display settings.
243
+ - **`trianglengin.ui.visualization`**: Python/Pygame rendering components. Uses data obtained from the `GameState` wrapper. ([`src/trianglengin/ui/visualization/README.md`](src/trianglengin/ui/visualization/README.md))
244
+ - **`trianglengin.ui.interaction`**: Python/Pygame input handling for interactive modes. Interacts with the `GameState` wrapper. ([`src/trianglengin/ui/interaction/README.md`](src/trianglengin/ui/interaction/README.md))
245
+ - **`trianglengin.ui.app.Application`**: Integrates UI components for interactive modes.
246
+ - **`trianglengin.ui.cli`**: Command-line interface (`trianglengin play`/`debug`).
247
+
248
+ ## Contributing
249
+
250
+ Contributions are welcome! Please open an issue or submit a pull request on the [GitHub repository](https://github.com/lguibr/trianglengin). Ensure that changes maintain code quality (pass tests, linting, type checking) and keep READMEs updated. Building requires a C++17 compiler and CMake.
@@ -0,0 +1,61 @@
1
+ trianglengin.libs/libgcc_s-f3fb5a36.so.1,sha256=SrjjCCuY7RHj-T9JLrY9XFMgCCpYD9Qmezr4uoJGVEQ,168321
2
+ trianglengin.libs/libstdc++-d2a021ba.so.6.0.32,sha256=1zr_iwGwEBe95gyKdgiw7C4Y1RR9ijV40j66rk4elzg,3537349
3
+ trianglengin/trianglengin_cpp.cpython-312-i386-linux-musl.so,sha256=LUz7N6r-MR-QtukYJM_bjvdLG3uEgYPgJHxj8jJa39w,305741
4
+ trianglengin/trianglengin_cpp.cpython-37m-i386-linux-gnu.so,sha256=LUz7N6r-MR-QtukYJM_bjvdLG3uEgYPgJHxj8jJa39w,305741
5
+ trianglengin/game_interface.py,sha256=0aCePxmhLJOdlPGHU_JOuu-lsBZVfOs0vK5eke-bxv0,8730
6
+ trianglengin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ trianglengin/__init__.py,sha256=4INnGvOYIjzOyCew7lhX6Irc8_H7QVxWc5jopt9TCZ4,919
8
+ trianglengin/config/README.md,sha256=PKm6HMVMZkA1KB22s2lP-jh5jwB7XIDiFgoShz_xj0s,1506
9
+ trianglengin/config/env_config.py,sha256=IMjbOrAZxgVzLjod9tmFhv964E30paG4zI1eUS8Nvy8,2359
10
+ trianglengin/config/display_config.py,sha256=FHY9iKuuk7L5h-xWtDTthHUlvyme4AJeA5kOk-zZJFg,1815
11
+ trianglengin/config/__init__.py,sha256=wKb-lRhguEAJCxcPHG1qyW-AmUherzMbUfi3SZ6IEao,160
12
+ trianglengin/cpp/game_state.cpp,sha256=ec_tLqySZTcEx7I01tLchLi5T90ep8l_oyRSZbUsu-U,10260
13
+ trianglengin/cpp/structs.h,sha256=v_aL5O5gwXH_qRPGUIkNplr167rgf7gCVlWIJKOLV1U,993
14
+ trianglengin/cpp/grid_data.cpp,sha256=KH9BbLuhi-_fmKgnT2oPuzHRDo87xFDHiBfpXCwgYsw,6904
15
+ trianglengin/cpp/grid_logic.h,sha256=O6roOE_U7nbKgAkaMkC8LrEjGDvnL3QiVzOnXunXy9s,781
16
+ trianglengin/cpp/grid_logic.cpp,sha256=8ag6wwdmZuHg8_I-skTI9LOdB84a82IrZAoREq0dkQo,3333
17
+ trianglengin/cpp/bindings.cpp,sha256=B3hfxIF_pBAyVG1QpbBvJT1awy8uJWRJSBNxaNY5_pY,8689
18
+ trianglengin/cpp/shape_logic.h,sha256=QvjLAPG5y42FetpXXuCcYrWlcwvKGo7csU8Ch-RmdFs,626
19
+ trianglengin/cpp/config.h,sha256=9Xd-9wTYmFwEuf-ZjrQa5zlXF58MrLhRKU1ygyG2Nmc,715
20
+ trianglengin/cpp/grid_data.h,sha256=KGOtUQQb7HJnU_LyWIWW5g6S2gJ2u3ewY32qXtoPfcs,2453
21
+ trianglengin/cpp/game_state.h,sha256=MBoAiG617XPtZNGqGPE2PC1pX1dBHuWBGHLJr3iBg6c,2547
22
+ trianglengin/cpp/shape_logic.cpp,sha256=gF1mgolaNvxpNphwDScNUvOkoq4FuxFASB3n50WI3Bo,4133
23
+ trianglengin/cpp/CMakeLists.txt,sha256=FCUnDHVQhe7PFj9FTQcQiWFgr61b8iJg0m0UH3hGYE4,1328
24
+ trianglengin/utils/geometry.py,sha256=0SY1woDY5u_e2_TrNpG5y6w7a8n417Ndtiq0Z4QuuW8,2066
25
+ trianglengin/utils/__init__.py,sha256=907xXfoKmxOc86noqwOtnomajkrkbInWvAhR-h5coow,176
26
+ trianglengin/utils/types.py,sha256=u5VALmDiCCWpYdtxS4rMgW5uxNtIEfpjH3tRg12yjYM,231
27
+ trianglengin/ui/README.md,sha256=M-5UU-NKG6qYu1riCUkftRZIwE2Jvf-5qOZ1QGWjPSg,1718
28
+ trianglengin/ui/app.py,sha256=9A7iBrGVJLG-OKDuYTPiayVn8bwO2Yx1-s1h4qJKX9c,4042
29
+ trianglengin/ui/config.py,sha256=E68Qvbr3dwyhC_Rh_PK-QHWlHOrXFQhAsARZZMoQHNs,1579
30
+ trianglengin/ui/__init__.py,sha256=AVIFYOWoZTt69NnkX0Ht3dEAPMpxQ6WZ8G9tDvxNXvA,558
31
+ trianglengin/ui/cli.py,sha256=8LXgyW45_qFuh6rCwwSqMkH8fxmnZ2uTEbEOEpsgdr0,3326
32
+ trianglengin/ui/interaction/debug_mode_handler.py,sha256=FbG8UZDVZsbnrSw3j7T7oIfUCLtAlNxfVl1qKjeApSY,2527
33
+ trianglengin/ui/interaction/play_mode_handler.py,sha256=olBy9tR09NrBWnivEyPdATar2zuboom5kXxPzu2kiBY,5544
34
+ trianglengin/ui/interaction/README.md,sha256=SN90_u45CBqbJjympMFvhq09Jem2eGH-RvELf5vp3WI,3515
35
+ trianglengin/ui/interaction/event_processor.py,sha256=j573lStOcX2x86urx4HIRoAfBXl7CbtK4HkzQByGbJ0,1650
36
+ trianglengin/ui/interaction/__init__.py,sha256=NZiN6T2Dzb1ZJOBVu19YTkLW4_gVI12irlLEliF3-1w,573
37
+ trianglengin/ui/interaction/input_handler.py,sha256=-IOfmds5lkguOgXkhBqMWC7TwQ0-F069AtQil1Fz3gw,3249
38
+ trianglengin/ui/visualization/README.md,sha256=4oFu_I2I8WUFDotsCRhEHZvjq4Fc2sVlB_tOvFy99CU,2111
39
+ trianglengin/ui/visualization/__init__.py,sha256=dCN6_TOLiw2uuNts_VuzAa-WhqxqR7yxF2NoU8pP1IU,1718
40
+ trianglengin/ui/visualization/drawing/README.md,sha256=RfIhgwRSwF-uybo5uT0Md8Lm6pgM7aafFK6oDEB3qhY,2886
41
+ trianglengin/ui/visualization/drawing/highlight.py,sha256=x-lyIbvT-lfwyqbRgdk0eTiWLTSRoSnvoR1dQ_6Z9ME,857
42
+ trianglengin/ui/visualization/drawing/shapes.py,sha256=4L9iFo4XHAdsAEqdyLFfyoSBgMtGWNTiD11HgzkyuQc,1709
43
+ trianglengin/ui/visualization/drawing/hud.py,sha256=3dhhJI9pm8nkgjTOG7ONd6X1jylpyw9s3zg2Tfhab3w,1227
44
+ trianglengin/ui/visualization/drawing/grid.py,sha256=21q5wB_SYSZilTDntdj5o4q6ZrxqpL-_VGO0cfR7ZL4,7160
45
+ trianglengin/ui/visualization/drawing/utils.py,sha256=3--x8Qsd9bLVGtveJbZDIQgZNK5naGMO7nMf7XWchMI,921
46
+ trianglengin/ui/visualization/drawing/__init__.py,sha256=KVdPQUjB-yZI1PzK2AoHKUOVaFhbjSJnvFJtcIPS4DA,1120
47
+ trianglengin/ui/visualization/drawing/previews.py,sha256=Kf4DDGLxxeK_Wgmcd2jg0b9_IMxrI35wKEVgkQn6W_k,7003
48
+ trianglengin/ui/visualization/core/colors.py,sha256=L5vg1dFRSO3kEyfaMEJxX1YwkmH44qBsMiHdRtMs5ZM,3038
49
+ trianglengin/ui/visualization/core/layout.py,sha256=uHUd0bIWnPA8gYPaenLfJKj0ry_4K_scrl-KWZmy4a4,2602
50
+ trianglengin/ui/visualization/core/README.md,sha256=fiyYIOBmS9-6-CktXCVrOD-KBp-Eo6xSZik_BWp2f2w,3203
51
+ trianglengin/ui/visualization/core/coord_mapper.py,sha256=FGD01ghlyfUbEvcvSKMi9FS_FmG8zCflROR775r3IxI,3260
52
+ trianglengin/ui/visualization/core/__init__.py,sha256=26XCOn_jywwQ0YszGP6rEgFJIqdP3meQNBPRu1SYLOc,557
53
+ trianglengin/ui/visualization/core/fonts.py,sha256=d9kQqdilPK0pTvXhv2oVJAbVT4pmWfjKxXfEir7Avdg,2016
54
+ trianglengin/ui/visualization/core/visualizer.py,sha256=gWY4_vZGEzTokl6Z7xMeGvdrpJMl4YdZnfoT4bgBYW4,8860
55
+ trianglengin/core/__init__.py,sha256=zMo_rn4wzVGzrevzZM4u4rkLwicZQeDqmXbnk9fI008,460
56
+ trianglengin-2.0.1.dist-info/WHEEL,sha256=IhKWbtsq-l3R9sOvgRrkKQKD7QmvveE5Bp0bBRPFL1g,110
57
+ trianglengin-2.0.1.dist-info/RECORD,,
58
+ trianglengin-2.0.1.dist-info/top_level.txt,sha256=YsSWmp_2zM23wRc5TRERHpVCgQuVYieYHDTpnwVQC7Y,13
59
+ trianglengin-2.0.1.dist-info/entry_points.txt,sha256=kQEqO_U-MEpMEC0xwOPSucBzQIq2Ny7XwCtFSruZhvY,57
60
+ trianglengin-2.0.1.dist-info/METADATA,sha256=g8tEo09YNK3qFb45tFv8A_176S_oUHS-jHRl5UIq3Ws,12036
61
+ trianglengin-2.0.1.dist-info/licenses/LICENSE,sha256=So3rgoJp-HgoxkclxZLIBC3pmmTwshN4tUO8KiQ6akc,1077
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (79.0.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-musllinux_1_2_i686
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ trianglengin = trianglengin.ui.cli:app