trianglengin 2.0.1__cp312-cp312-musllinux_1_2_x86_64.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.
- trianglengin/__init__.py +35 -0
- trianglengin/config/README.md +38 -0
- trianglengin/config/__init__.py +8 -0
- trianglengin/config/display_config.py +47 -0
- trianglengin/config/env_config.py +62 -0
- trianglengin/core/__init__.py +10 -0
- trianglengin/cpp/CMakeLists.txt +42 -0
- trianglengin/cpp/bindings.cpp +211 -0
- trianglengin/cpp/config.h +28 -0
- trianglengin/cpp/game_state.cpp +327 -0
- trianglengin/cpp/game_state.h +73 -0
- trianglengin/cpp/grid_data.cpp +239 -0
- trianglengin/cpp/grid_data.h +78 -0
- trianglengin/cpp/grid_logic.cpp +125 -0
- trianglengin/cpp/grid_logic.h +30 -0
- trianglengin/cpp/shape_logic.cpp +100 -0
- trianglengin/cpp/shape_logic.h +28 -0
- trianglengin/cpp/structs.h +40 -0
- trianglengin/game_interface.py +222 -0
- trianglengin/py.typed +0 -0
- trianglengin/trianglengin_cpp.cpython-312-x86_64-linux-musl.so +0 -0
- trianglengin/trianglengin_cpp.cpython-37m-x86_64-linux-gnu.so +0 -0
- trianglengin/ui/README.md +35 -0
- trianglengin/ui/__init__.py +21 -0
- trianglengin/ui/app.py +107 -0
- trianglengin/ui/cli.py +123 -0
- trianglengin/ui/config.py +44 -0
- trianglengin/ui/interaction/README.md +44 -0
- trianglengin/ui/interaction/__init__.py +19 -0
- trianglengin/ui/interaction/debug_mode_handler.py +72 -0
- trianglengin/ui/interaction/event_processor.py +49 -0
- trianglengin/ui/interaction/input_handler.py +89 -0
- trianglengin/ui/interaction/play_mode_handler.py +156 -0
- trianglengin/ui/visualization/README.md +42 -0
- trianglengin/ui/visualization/__init__.py +58 -0
- trianglengin/ui/visualization/core/README.md +51 -0
- trianglengin/ui/visualization/core/__init__.py +16 -0
- trianglengin/ui/visualization/core/colors.py +115 -0
- trianglengin/ui/visualization/core/coord_mapper.py +85 -0
- trianglengin/ui/visualization/core/fonts.py +65 -0
- trianglengin/ui/visualization/core/layout.py +77 -0
- trianglengin/ui/visualization/core/visualizer.py +248 -0
- trianglengin/ui/visualization/drawing/README.md +49 -0
- trianglengin/ui/visualization/drawing/__init__.py +43 -0
- trianglengin/ui/visualization/drawing/grid.py +213 -0
- trianglengin/ui/visualization/drawing/highlight.py +31 -0
- trianglengin/ui/visualization/drawing/hud.py +43 -0
- trianglengin/ui/visualization/drawing/previews.py +181 -0
- trianglengin/ui/visualization/drawing/shapes.py +46 -0
- trianglengin/ui/visualization/drawing/utils.py +23 -0
- trianglengin/utils/__init__.py +9 -0
- trianglengin/utils/geometry.py +62 -0
- trianglengin/utils/types.py +10 -0
- trianglengin-2.0.1.dist-info/METADATA +250 -0
- trianglengin-2.0.1.dist-info/RECORD +61 -0
- trianglengin-2.0.1.dist-info/WHEEL +5 -0
- trianglengin-2.0.1.dist-info/entry_points.txt +2 -0
- trianglengin-2.0.1.dist-info/licenses/LICENSE +22 -0
- trianglengin-2.0.1.dist-info/top_level.txt +1 -0
- trianglengin.libs/libgcc_s-a3a07607.so.1 +0 -0
- trianglengin.libs/libstdc++-496613c0.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,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,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
|
+
[](https://github.com/lguibr/trianglengin/actions/workflows/ci_cd.yml)
|
38
|
+
[](https://codecov.io/gh/lguibr/trianglengin)
|
39
|
+
[](https://badge.fury.io/py/trianglengin)
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
41
|
+
[](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/libstdc++-496613c0.so.6.0.32,sha256=AwZhL5WFT99I2Q6vIxXOhW7qddfuOZ1xecxiP9QTVY4,3494361
|
2
|
+
trianglengin.libs/libgcc_s-a3a07607.so.1,sha256=5ptIUeAzZweNZrFehN0_Bb5B0FcJgux7NbAFoU8vwwo,148041
|
3
|
+
trianglengin/game_interface.py,sha256=0aCePxmhLJOdlPGHU_JOuu-lsBZVfOs0vK5eke-bxv0,8730
|
4
|
+
trianglengin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
trianglengin/trianglengin_cpp.cpython-312-x86_64-linux-musl.so,sha256=UWH4yF8fOnL9Iu6550q8d_OmrGXUbsxtVSDud3p1JRE,297897
|
6
|
+
trianglengin/__init__.py,sha256=4INnGvOYIjzOyCew7lhX6Irc8_H7QVxWc5jopt9TCZ4,919
|
7
|
+
trianglengin/trianglengin_cpp.cpython-37m-x86_64-linux-gnu.so,sha256=UWH4yF8fOnL9Iu6550q8d_OmrGXUbsxtVSDud3p1JRE,297897
|
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=UyLNHXRBDnd27Yk45xc47r0-UQKuY5fM2-iebdLKea8,112
|
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
|