trianglengin 2.0.6__tar.gz → 2.0.7__tar.gz

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 (73) hide show
  1. {trianglengin-2.0.6/src/trianglengin.egg-info → trianglengin-2.0.7}/PKG-INFO +128 -4
  2. trianglengin-2.0.7/README.md +339 -0
  3. {trianglengin-2.0.6 → trianglengin-2.0.7}/pyproject.toml +13 -34
  4. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/bindings.cpp +5 -8
  5. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/game_state.cpp +37 -55
  6. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/game_state.h +3 -1
  7. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/game_interface.py +8 -23
  8. {trianglengin-2.0.6 → trianglengin-2.0.7/src/trianglengin.egg-info}/PKG-INFO +128 -4
  9. trianglengin-2.0.6/README.md +0 -215
  10. {trianglengin-2.0.6 → trianglengin-2.0.7}/LICENSE +0 -0
  11. {trianglengin-2.0.6 → trianglengin-2.0.7}/MANIFEST.in +0 -0
  12. {trianglengin-2.0.6 → trianglengin-2.0.7}/setup.cfg +0 -0
  13. {trianglengin-2.0.6 → trianglengin-2.0.7}/setup.py +0 -0
  14. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/__init__.py +0 -0
  15. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/config/README.md +0 -0
  16. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/config/__init__.py +0 -0
  17. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/config/env_config.py +0 -0
  18. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/core/__init__.py +0 -0
  19. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/CMakeLists.txt +0 -0
  20. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/config.h +0 -0
  21. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/grid_data.cpp +0 -0
  22. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/grid_data.h +0 -0
  23. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/grid_logic.cpp +0 -0
  24. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/grid_logic.h +0 -0
  25. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/shape_logic.cpp +0 -0
  26. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/shape_logic.h +0 -0
  27. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/cpp/structs.h +0 -0
  28. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/py.typed +0 -0
  29. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/README.md +0 -0
  30. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/__init__.py +0 -0
  31. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/app.py +0 -0
  32. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/cli.py +0 -0
  33. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/config.py +0 -0
  34. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/README.md +0 -0
  35. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/__init__.py +0 -0
  36. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/debug_mode_handler.py +0 -0
  37. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/event_processor.py +0 -0
  38. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/input_handler.py +0 -0
  39. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/interaction/play_mode_handler.py +0 -0
  40. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/README.md +0 -0
  41. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/__init__.py +0 -0
  42. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/README.md +0 -0
  43. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/__init__.py +0 -0
  44. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/colors.py +0 -0
  45. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/coord_mapper.py +0 -0
  46. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/fonts.py +0 -0
  47. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/layout.py +0 -0
  48. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/core/visualizer.py +0 -0
  49. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/README.md +0 -0
  50. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/__init__.py +0 -0
  51. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/grid.py +0 -0
  52. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/highlight.py +0 -0
  53. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/hud.py +0 -0
  54. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/previews.py +0 -0
  55. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/shapes.py +0 -0
  56. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/ui/visualization/drawing/utils.py +0 -0
  57. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/utils/__init__.py +0 -0
  58. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/utils/geometry.py +0 -0
  59. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin/utils/types.py +0 -0
  60. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/SOURCES.txt +0 -0
  61. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/dependency_links.txt +0 -0
  62. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/entry_points.txt +0 -0
  63. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/not-zip-safe +0 -0
  64. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/requires.txt +0 -0
  65. {trianglengin-2.0.6 → trianglengin-2.0.7}/src/trianglengin.egg-info/top_level.txt +0 -0
  66. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/__init__.py +0 -0
  67. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/conftest.py +0 -0
  68. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/core/__init__.py +0 -0
  69. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/core/environment/README.md +0 -0
  70. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/core/environment/__init__.py +0 -0
  71. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/core/environment/test_game_state.py +0 -0
  72. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/utils/__init__.py +0 -0
  73. {trianglengin-2.0.6 → trianglengin-2.0.7}/tests/utils/test_geometry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trianglengin
3
- Version: 2.0.6
3
+ Version: 2.0.7
4
4
  Summary: High-performance C++/Python engine for a triangle puzzle game.
5
5
  Author-email: "Luis Guilherme P. M." <lgpelin92@gmail.com>
6
6
  License-Expression: MIT
@@ -57,17 +57,141 @@ It encapsulates:
57
57
 
58
58
  ---
59
59
 
60
+
61
+
60
62
  ## 🎮 The Ultimate Triangle Puzzle Guide 🧩
61
63
 
62
- *(Game rules remain the same)*
64
+ Get ready to become a Triangle Master! This guide explains everything you need to know to play the game, step-by-step, with lots of details!
65
+
66
+ ### 1. Introduction: Your Mission! 🎯
67
+
68
+ Your goal is to place colorful shapes onto a special triangular grid. By filling up lines of triangles, you make them disappear and score points! Keep placing shapes and clearing lines for as long as possible to get the highest score before the grid fills up and you run out of moves. Sounds simple? Let's dive into the details!
69
+
70
+ ### 2. The Playing Field: The Grid 🗺️
71
+
72
+ - **Triangle Cells:** The game board is a grid made of many small triangles. Some point UP (🔺) and some point DOWN (🔻). They alternate like a checkerboard pattern based on their row and column index (specifically, `(row + col) % 2 != 0` means UP).
73
+ - **Shape:** The grid itself is rectangular overall, but the playable area within it is typically shaped like a triangle or hexagon, wider in the middle and narrower at the top and bottom.
74
+ - **Playable Area:** You can only place shapes within the designated playable area.
75
+ - **Death Zones 💀:** Around the edges of the playable area (often at the start and end of rows), some triangles are marked as "Death Zones". You **cannot** place any part of a shape onto these triangles. They are off-limits! Think of them as the boundaries within the rectangular grid.
76
+
77
+ ### 3. Your Tools: The Shapes 🟦🟥🟩
78
+
79
+ - **Shape Formation:** Each shape is a collection of connected small triangles (🔺 and 🔻). They come in different colors and arrangements. Some might be a single triangle, others might be long lines, L-shapes, or more complex patterns.
80
+ - **Relative Positions:** The triangles within a shape have fixed positions _relative to each other_. When you move the shape, all its triangles move together as one block.
81
+ - **Preview Area:** You will always have **three** shapes available to choose from at any time. These are shown in a special "preview area" on the side of the screen.
82
+
83
+ ### 4. Making Your Move: Placing Shapes 🖱️➡️▦
84
+
85
+ This is the core action! Here's exactly how to place a shape:
86
+
87
+ - **Step 4a: Select a Shape:** Look at the three shapes in the preview area. Click on the one you want to place. It should highlight 💡 to show it's selected.
88
+ - **Step 4b: Aim on the Grid:** Move your mouse cursor over the main grid. You'll see a faint "ghost" image of your selected shape following your mouse. This preview helps you aim.
89
+ - **Step 4c: The Placement Rules (MUST Follow!)**
90
+ - 📏 **Rule 1: Fit Inside Playable Area:** ALL triangles of your chosen shape must land within the playable grid area. No part of the shape can land in a Death Zone 💀.
91
+ - 🧱 **Rule 2: No Overlap:** ALL triangles of your chosen shape must land on currently _empty_ spaces on the grid. You cannot place a shape on top of triangles that are already filled with color from previous shapes.
92
+ - 📐 **Rule 3: Orientation Match!** This is crucial!
93
+ - If a part of your shape is an UP triangle (🔺), it MUST land on an UP space (🔺) on the grid.
94
+ - If a part of your shape is a DOWN triangle (🔻), it MUST land on a DOWN space (🔻) on the grid.
95
+ - 🔺➡️🔺 (OK!)
96
+ - 🔻➡️🔻 (OK!)
97
+ - 🔺➡️🔻 (INVALID! ❌)
98
+ - 🔻➡️🔺 (INVALID! ❌)
99
+ - **Visual Feedback:** The game helps you!
100
+ - 👍 **Valid Spot:** If the position under your mouse follows ALL three rules, the ghost preview will usually look solid and possibly greenish. This means you _can_ place the shape here.
101
+ - 👎 **Invalid Spot:** If the position breaks _any_ of the rules (out of bounds, overlaps, wrong orientation), the ghost preview will usually look faded and possibly reddish. This means you _cannot_ place the shape here.
102
+ - **Step 4d: Confirm Placement:** Once you find a **valid** spot (👍), click the left mouse button again. _Click!_ The shape is now placed permanently on the grid! ✨
103
+
104
+ ### 5. Scoring Points: How You Win! 🏆
105
+
106
+ You score points in two main ways:
107
+
108
+ - **Placing Triangles:** You get a small number of points for _every single small triangle_ that makes up the shape you just placed. (e.g., placing a 3-triangle shape might give you 3 \* tiny_score points).
109
+ - **Clearing Lines:** This is where the BIG points come from! You get a much larger number of points for _every single small triangle_ that disappears when you clear a line (or multiple lines at once!). See the next section for details!
110
+
111
+ ### 6. Line Clearing Magic! ✨ (The Key to High Scores!)
112
+
113
+ This is the most exciting part! When you place a shape, the game immediately checks if you've completed any lines. This section explains how the game _finds_ and _clears_ these lines.
114
+
115
+ - **What Lines Can Be Cleared?** There are **three** types of lines the game looks for:
116
+
117
+ - **Horizontal Lines ↔️:** A straight, unbroken line of filled triangles going across a single row.
118
+ - **Diagonal Lines (Top-Left to Bottom-Right) ↘️:** An unbroken diagonal line of filled triangles stepping down and to the right.
119
+ - **Diagonal Lines (Bottom-Left to Top-Right) ↗️:** An unbroken diagonal line of filled triangles stepping up and to the right.
120
+
121
+ - **How Lines are Found: Pre-calculation of Maximal Lines**
63
122
 
64
- *(... Game rules section remains unchanged ...)*
123
+ - **The Idea:** Instead of checking every possible line combination all the time, the game pre-calculates all *maximal* continuous lines of playable triangles when it starts. A **maximal line** is the longest possible straight segment of *playable* triangles (not in a Death Zone) in one of the three directions (Horizontal, Diagonal ↘️, Diagonal ↗️).
124
+ - **Tracing:** For every playable triangle on the grid, the game traces outwards in each of the three directions to find the full extent of the continuous playable line passing through that triangle in that direction.
125
+ - **Storing Maximal Lines:** Only the complete maximal lines found are stored. For example, if tracing finds a playable sequence `A-B-C-D`, only the line `(A,B,C,D)` is stored, not the sub-segments like `(A,B,C)` or `(B,C,D)`. These maximal lines represent the *potential* lines that can be cleared.
126
+ - **Coordinate Map:** The game also builds a map linking each playable triangle coordinate `(r, c)` to the set of maximal lines it belongs to. This allows for quick lookup.
127
+
128
+ - **Defining the Paths (Neighbor Logic):** How does the game know which triangle is "next" when tracing? It depends on the current triangle's orientation (🔺 or 🔻) and the direction being traced:
129
+
130
+ - **Horizontal ↔️:**
131
+ - Left Neighbor: `(r, c-1)` (Always in the same row)
132
+ - Right Neighbor: `(r, c+1)` (Always in the same row)
133
+ - **Diagonal ↘️ (TL-BR):**
134
+ - If current is 🔺 (Up): Next is `(r+1, c)` (Down triangle directly below)
135
+ - If current is 🔻 (Down): Next is `(r, c+1)` (Up triangle to the right)
136
+ - **Diagonal ↗️ (BL-TR):**
137
+ - If current is 🔻 (Down): Next is `(r-1, c)` (Up triangle directly above)
138
+ - If current is 🔺 (Up): Next is `(r, c+1)` (Down triangle to the right)
139
+
140
+ - **Visualizing the Paths:**
141
+
142
+ - **Horizontal ↔️:**
143
+ ```
144
+ ... [🔻][🔺][🔻][🔺][🔻][🔺] ... (Moves left/right in the same row)
145
+ ```
146
+ - **Diagonal ↘️ (TL-BR):** (Connects via shared horizontal edges)
147
+ ```
148
+ ...[🔺]...
149
+ ...[🔻][🔺] ...
150
+ ... [🔻][🔺] ...
151
+ ... [🔻] ...
152
+ (Path alternates row/col increments depending on orientation)
153
+ ```
154
+ - **Diagonal ↗️ (BL-TR):** (Connects via shared horizontal edges)
155
+ ```
156
+ ... [🔺] ...
157
+ ... [🔺][🔻] ...
158
+ ... [🔺][🔻] ...
159
+ ... [🔻] ...
160
+ (Path alternates row/col increments depending on orientation)
161
+ ```
162
+
163
+ - **The "Full Line" Rule:** After you place a piece, the game looks at the coordinates `(r, c)` of the triangles you just placed. Using the pre-calculated map, it finds all the *maximal* lines that contain _any_ of those coordinates. For each of those maximal lines (that have at least 2 triangles), it checks: "Is _every single triangle coordinate_ in this maximal line now occupied?" If yes, that line is complete! (Note: Single isolated triangles don't count as clearable lines).
164
+
165
+ - **The _Poof_! 💨:**
166
+ - If placing your shape completes one or MORE maximal lines (of any type, length >= 2) simultaneously, all the triangles in ALL completed lines vanish instantly!
167
+ - The spaces become empty again.
168
+ - You score points for _every single triangle_ that vanished. Clearing multiple lines at once is the best way to rack up points! 🥳
169
+
170
+ ### 7. Getting New Shapes: The Refill 🪄
171
+
172
+ - **The Trigger:** The game only gives you new shapes when a specific condition is met.
173
+ - **The Condition:** New shapes appear **only when all three of your preview slots become empty at the exact same time.**
174
+ - **How it Happens:** This usually occurs right after you place your _last_ available shape (the third one).
175
+ - **The Refill:** As soon as the third slot becomes empty, _BAM!_ 🪄 Three brand new, randomly generated shapes instantly appear in the preview slots.
176
+ - **Important:** If you place a shape and only one or two slots are empty, you **do not** get new shapes yet. You must use up all three before the refill happens.
177
+
178
+ ### 8. The End of the Road: Game Over 😭
179
+
180
+ So, how does the game end?
181
+
182
+ - **The Condition:** The game is over when you **cannot legally place _any_ of the three shapes currently available in your preview slots anywhere on the grid.**
183
+ - **The Check:** After every move (placing a shape and any resulting line clears), and after any potential shape refill, the game checks: "Is there at least one valid spot on the grid for Shape 1? OR for Shape 2? OR for Shape 3?"
184
+ - **No More Moves:** If the answer is "NO" for all three shapes (meaning none of them can be placed anywhere according to the Placement Rules), then the game immediately ends.
185
+ - **Strategy:** This means you need to be careful! Don't fill up the grid in a way that leaves no room for the types of shapes you might get later. Always try to keep options open! 🤔
186
+
187
+ That's it! Now you know all the rules. Go forth and conquer the Triangle Puzzle! 🏆
65
188
 
66
189
  ---
67
190
 
68
191
  ## Purpose
69
192
 
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.
193
+ The primary goal is to provide a self-contained, installable library for the core logic and basic interactive UI of the triangle puzzle game. This allows different RL agent implementations or other applications to build upon a consistent and well-defined game backend, avoiding code duplication.
194
+
71
195
 
72
196
  ## Installation
73
197
 
@@ -0,0 +1,339 @@
1
+
2
+ [![CI Status](https://github.com/lguibr/trianglengin/actions/workflows/ci_cd.yml/badge.svg)](https://github.com/lguibr/trianglengin/actions/workflows/ci_cd.yml)
3
+ [![codecov](https://codecov.io/gh/lguibr/trianglengin/graph/badge.svg?token=YOUR_CODECOV_TOKEN_HERE&flag=trianglengin)](https://codecov.io/gh/lguibr/trianglengin)
4
+ [![PyPI version](https://badge.fury.io/py/trianglengin.svg)](https://badge.fury.io/py/trianglengin)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ # Triangle Engine (`trianglengin`) v2.0.3
9
+ <img src="bitmap.png" alt="trianglengin logo" width="300"/>
10
+
11
+ **Version 2 introduces a high-performance C++ core for the game logic.**
12
+
13
+ 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.
14
+
15
+ It encapsulates:
16
+
17
+ 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))
18
+ 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))
19
+ 3. **Configuration (Python/Pydantic):** Models for environment settings (`EnvConfig`). ([`src/trianglengin/config/README.md`](src/trianglengin/config/README.md))
20
+ 4. **Utilities (Python):** General helpers, geometry functions, shared types. ([`src/trianglengin/utils/README.md`](src/trianglengin/utils/README.md))
21
+ 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))
22
+
23
+ ---
24
+
25
+
26
+
27
+ ## 🎮 The Ultimate Triangle Puzzle Guide 🧩
28
+
29
+ Get ready to become a Triangle Master! This guide explains everything you need to know to play the game, step-by-step, with lots of details!
30
+
31
+ ### 1. Introduction: Your Mission! 🎯
32
+
33
+ Your goal is to place colorful shapes onto a special triangular grid. By filling up lines of triangles, you make them disappear and score points! Keep placing shapes and clearing lines for as long as possible to get the highest score before the grid fills up and you run out of moves. Sounds simple? Let's dive into the details!
34
+
35
+ ### 2. The Playing Field: The Grid 🗺️
36
+
37
+ - **Triangle Cells:** The game board is a grid made of many small triangles. Some point UP (🔺) and some point DOWN (🔻). They alternate like a checkerboard pattern based on their row and column index (specifically, `(row + col) % 2 != 0` means UP).
38
+ - **Shape:** The grid itself is rectangular overall, but the playable area within it is typically shaped like a triangle or hexagon, wider in the middle and narrower at the top and bottom.
39
+ - **Playable Area:** You can only place shapes within the designated playable area.
40
+ - **Death Zones 💀:** Around the edges of the playable area (often at the start and end of rows), some triangles are marked as "Death Zones". You **cannot** place any part of a shape onto these triangles. They are off-limits! Think of them as the boundaries within the rectangular grid.
41
+
42
+ ### 3. Your Tools: The Shapes 🟦🟥🟩
43
+
44
+ - **Shape Formation:** Each shape is a collection of connected small triangles (🔺 and 🔻). They come in different colors and arrangements. Some might be a single triangle, others might be long lines, L-shapes, or more complex patterns.
45
+ - **Relative Positions:** The triangles within a shape have fixed positions _relative to each other_. When you move the shape, all its triangles move together as one block.
46
+ - **Preview Area:** You will always have **three** shapes available to choose from at any time. These are shown in a special "preview area" on the side of the screen.
47
+
48
+ ### 4. Making Your Move: Placing Shapes 🖱️➡️▦
49
+
50
+ This is the core action! Here's exactly how to place a shape:
51
+
52
+ - **Step 4a: Select a Shape:** Look at the three shapes in the preview area. Click on the one you want to place. It should highlight 💡 to show it's selected.
53
+ - **Step 4b: Aim on the Grid:** Move your mouse cursor over the main grid. You'll see a faint "ghost" image of your selected shape following your mouse. This preview helps you aim.
54
+ - **Step 4c: The Placement Rules (MUST Follow!)**
55
+ - 📏 **Rule 1: Fit Inside Playable Area:** ALL triangles of your chosen shape must land within the playable grid area. No part of the shape can land in a Death Zone 💀.
56
+ - 🧱 **Rule 2: No Overlap:** ALL triangles of your chosen shape must land on currently _empty_ spaces on the grid. You cannot place a shape on top of triangles that are already filled with color from previous shapes.
57
+ - 📐 **Rule 3: Orientation Match!** This is crucial!
58
+ - If a part of your shape is an UP triangle (🔺), it MUST land on an UP space (🔺) on the grid.
59
+ - If a part of your shape is a DOWN triangle (🔻), it MUST land on a DOWN space (🔻) on the grid.
60
+ - 🔺➡️🔺 (OK!)
61
+ - 🔻➡️🔻 (OK!)
62
+ - 🔺➡️🔻 (INVALID! ❌)
63
+ - 🔻➡️🔺 (INVALID! ❌)
64
+ - **Visual Feedback:** The game helps you!
65
+ - 👍 **Valid Spot:** If the position under your mouse follows ALL three rules, the ghost preview will usually look solid and possibly greenish. This means you _can_ place the shape here.
66
+ - 👎 **Invalid Spot:** If the position breaks _any_ of the rules (out of bounds, overlaps, wrong orientation), the ghost preview will usually look faded and possibly reddish. This means you _cannot_ place the shape here.
67
+ - **Step 4d: Confirm Placement:** Once you find a **valid** spot (👍), click the left mouse button again. _Click!_ The shape is now placed permanently on the grid! ✨
68
+
69
+ ### 5. Scoring Points: How You Win! 🏆
70
+
71
+ You score points in two main ways:
72
+
73
+ - **Placing Triangles:** You get a small number of points for _every single small triangle_ that makes up the shape you just placed. (e.g., placing a 3-triangle shape might give you 3 \* tiny_score points).
74
+ - **Clearing Lines:** This is where the BIG points come from! You get a much larger number of points for _every single small triangle_ that disappears when you clear a line (or multiple lines at once!). See the next section for details!
75
+
76
+ ### 6. Line Clearing Magic! ✨ (The Key to High Scores!)
77
+
78
+ This is the most exciting part! When you place a shape, the game immediately checks if you've completed any lines. This section explains how the game _finds_ and _clears_ these lines.
79
+
80
+ - **What Lines Can Be Cleared?** There are **three** types of lines the game looks for:
81
+
82
+ - **Horizontal Lines ↔️:** A straight, unbroken line of filled triangles going across a single row.
83
+ - **Diagonal Lines (Top-Left to Bottom-Right) ↘️:** An unbroken diagonal line of filled triangles stepping down and to the right.
84
+ - **Diagonal Lines (Bottom-Left to Top-Right) ↗️:** An unbroken diagonal line of filled triangles stepping up and to the right.
85
+
86
+ - **How Lines are Found: Pre-calculation of Maximal Lines**
87
+
88
+ - **The Idea:** Instead of checking every possible line combination all the time, the game pre-calculates all *maximal* continuous lines of playable triangles when it starts. A **maximal line** is the longest possible straight segment of *playable* triangles (not in a Death Zone) in one of the three directions (Horizontal, Diagonal ↘️, Diagonal ↗️).
89
+ - **Tracing:** For every playable triangle on the grid, the game traces outwards in each of the three directions to find the full extent of the continuous playable line passing through that triangle in that direction.
90
+ - **Storing Maximal Lines:** Only the complete maximal lines found are stored. For example, if tracing finds a playable sequence `A-B-C-D`, only the line `(A,B,C,D)` is stored, not the sub-segments like `(A,B,C)` or `(B,C,D)`. These maximal lines represent the *potential* lines that can be cleared.
91
+ - **Coordinate Map:** The game also builds a map linking each playable triangle coordinate `(r, c)` to the set of maximal lines it belongs to. This allows for quick lookup.
92
+
93
+ - **Defining the Paths (Neighbor Logic):** How does the game know which triangle is "next" when tracing? It depends on the current triangle's orientation (🔺 or 🔻) and the direction being traced:
94
+
95
+ - **Horizontal ↔️:**
96
+ - Left Neighbor: `(r, c-1)` (Always in the same row)
97
+ - Right Neighbor: `(r, c+1)` (Always in the same row)
98
+ - **Diagonal ↘️ (TL-BR):**
99
+ - If current is 🔺 (Up): Next is `(r+1, c)` (Down triangle directly below)
100
+ - If current is 🔻 (Down): Next is `(r, c+1)` (Up triangle to the right)
101
+ - **Diagonal ↗️ (BL-TR):**
102
+ - If current is 🔻 (Down): Next is `(r-1, c)` (Up triangle directly above)
103
+ - If current is 🔺 (Up): Next is `(r, c+1)` (Down triangle to the right)
104
+
105
+ - **Visualizing the Paths:**
106
+
107
+ - **Horizontal ↔️:**
108
+ ```
109
+ ... [🔻][🔺][🔻][🔺][🔻][🔺] ... (Moves left/right in the same row)
110
+ ```
111
+ - **Diagonal ↘️ (TL-BR):** (Connects via shared horizontal edges)
112
+ ```
113
+ ...[🔺]...
114
+ ...[🔻][🔺] ...
115
+ ... [🔻][🔺] ...
116
+ ... [🔻] ...
117
+ (Path alternates row/col increments depending on orientation)
118
+ ```
119
+ - **Diagonal ↗️ (BL-TR):** (Connects via shared horizontal edges)
120
+ ```
121
+ ... [🔺] ...
122
+ ... [🔺][🔻] ...
123
+ ... [🔺][🔻] ...
124
+ ... [🔻] ...
125
+ (Path alternates row/col increments depending on orientation)
126
+ ```
127
+
128
+ - **The "Full Line" Rule:** After you place a piece, the game looks at the coordinates `(r, c)` of the triangles you just placed. Using the pre-calculated map, it finds all the *maximal* lines that contain _any_ of those coordinates. For each of those maximal lines (that have at least 2 triangles), it checks: "Is _every single triangle coordinate_ in this maximal line now occupied?" If yes, that line is complete! (Note: Single isolated triangles don't count as clearable lines).
129
+
130
+ - **The _Poof_! 💨:**
131
+ - If placing your shape completes one or MORE maximal lines (of any type, length >= 2) simultaneously, all the triangles in ALL completed lines vanish instantly!
132
+ - The spaces become empty again.
133
+ - You score points for _every single triangle_ that vanished. Clearing multiple lines at once is the best way to rack up points! 🥳
134
+
135
+ ### 7. Getting New Shapes: The Refill 🪄
136
+
137
+ - **The Trigger:** The game only gives you new shapes when a specific condition is met.
138
+ - **The Condition:** New shapes appear **only when all three of your preview slots become empty at the exact same time.**
139
+ - **How it Happens:** This usually occurs right after you place your _last_ available shape (the third one).
140
+ - **The Refill:** As soon as the third slot becomes empty, _BAM!_ 🪄 Three brand new, randomly generated shapes instantly appear in the preview slots.
141
+ - **Important:** If you place a shape and only one or two slots are empty, you **do not** get new shapes yet. You must use up all three before the refill happens.
142
+
143
+ ### 8. The End of the Road: Game Over 😭
144
+
145
+ So, how does the game end?
146
+
147
+ - **The Condition:** The game is over when you **cannot legally place _any_ of the three shapes currently available in your preview slots anywhere on the grid.**
148
+ - **The Check:** After every move (placing a shape and any resulting line clears), and after any potential shape refill, the game checks: "Is there at least one valid spot on the grid for Shape 1? OR for Shape 2? OR for Shape 3?"
149
+ - **No More Moves:** If the answer is "NO" for all three shapes (meaning none of them can be placed anywhere according to the Placement Rules), then the game immediately ends.
150
+ - **Strategy:** This means you need to be careful! Don't fill up the grid in a way that leaves no room for the types of shapes you might get later. Always try to keep options open! 🤔
151
+
152
+ That's it! Now you know all the rules. Go forth and conquer the Triangle Puzzle! 🏆
153
+
154
+ ---
155
+
156
+ ## Purpose
157
+
158
+ The primary goal is to provide a self-contained, installable library for the core logic and basic interactive UI of the triangle puzzle game. This allows different RL agent implementations or other applications to build upon a consistent and well-defined game backend, avoiding code duplication.
159
+
160
+
161
+ ## Installation
162
+
163
+ **Prerequisites:**
164
+ * A C++ compiler supporting C++17 (e.g., GCC, Clang, MSVC).
165
+ * CMake (version 3.14 or higher).
166
+ * Python (>= 3.10) and Pip.
167
+
168
+ ```bash
169
+ # For standard use (once published or built):
170
+ pip install trianglengin>=2.0.3
171
+
172
+ # Building from source (requires compiler and CMake):
173
+ git clone https://github.com/lguibr/trianglengin.git
174
+ cd trianglengin
175
+ pip install .
176
+ ```
177
+ *(Note: `pygame` and `typer` will be installed as core dependencies).*
178
+
179
+ ## Running Interactive Modes
180
+
181
+ After installing, you can run the interactive modes directly:
182
+
183
+ - **Play Mode:**
184
+ ```bash
185
+ trianglengin play [--seed 42] [--log-level INFO]
186
+ ```
187
+ - **Debug Mode:**
188
+ ```bash
189
+ trianglengin debug [--seed 42] [--log-level DEBUG]
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Local Development & Testing
195
+
196
+ These instructions are for developers contributing to `trianglengin`.
197
+
198
+ 1. **Clone the Repository:**
199
+ ```bash
200
+ git clone https://github.com/lguibr/trianglengin.git
201
+ cd trianglengin
202
+ ```
203
+
204
+ 2. **Prerequisites:** Ensure you have a C++17 compiler and CMake installed.
205
+
206
+ 3. **Create and Activate Virtual Environment:** (venv or conda)
207
+ ```bash
208
+ # Example using venv
209
+ python -m venv venv
210
+ source venv/bin/activate # or .\venv\Scripts\activate on Windows
211
+ ```
212
+ **IMPORTANT:** Ensure your virtual environment is activated.
213
+
214
+ 4. **Install Build Dependencies:** (Needed even for core dev)
215
+ ```bash
216
+ pip install pybind11>=2.10 cmake wheel
217
+ ```
218
+
219
+ 5. **Clean Previous Builds (Optional but Recommended):**
220
+ ```bash
221
+ rm -rf build/ src/trianglengin.egg-info/ dist/ src/trianglengin/trianglengin_cpp.*.so src/trianglengin/trianglengin_cpp*.cpp
222
+ ```
223
+
224
+ 6. **Install in Editable Mode with Dev Dependencies:**
225
+ * **Make sure your virtual environment is active!**
226
+ * Run from the project root directory:
227
+ ```bash
228
+ # Installs core, UI, and dev tools
229
+ pip install -e '.[dev]'
230
+ ```
231
+ This command compiles the C++ extension and installs the Python package so changes are reflected immediately. It also installs development tools.
232
+
233
+ 7. **Running Checks:**
234
+ * **Make sure your virtual environment is active!**
235
+ * **Tests & Coverage:**
236
+ ```bash
237
+ pytest tests/ --cov=src/trianglengin --cov-report=xml
238
+ ```
239
+ (Coverage measures core Python code, excluding UI).
240
+ To just run tests: `pytest`
241
+ * **Linting:** `ruff check .`
242
+ * **Formatting:** `ruff format .`
243
+ * **Type Checking:** `mypy src/trianglengin/ tests/`
244
+
245
+ 8. **Troubleshooting Build/Import Errors:**
246
+ * Ensure compiler and CMake are installed and in PATH.
247
+ * Make sure the virtual environment is active *before* running `pip install -e`.
248
+ * Clean previous builds (Step 5).
249
+ * Check `pyproject.toml` and `setup.py` for correct configuration.
250
+ * Verify Pybind11 version compatibility.
251
+
252
+ ---
253
+
254
+ ## Project Structure (v2 - UI Included)
255
+
256
+ ```
257
+ trianglengin/
258
+ ├── .github/workflows/ # GitHub Actions CI/CD
259
+ │ └── ci_cd.yml
260
+ ├── src/ # Source root
261
+ │ └── trianglengin/ # Python package source
262
+ │ ├── __init__.py # Exposes core public API (GameState, EnvConfig, Shape)
263
+ │ ├── game_interface.py # Python GameState wrapper class
264
+ │ ├── py.typed # PEP 561 marker
265
+ │ ├── cpp/ # C++ Core Implementation ([src/trianglengin/cpp/README.md])
266
+ │ │ ├── CMakeLists.txt
267
+ │ │ ├── bindings.cpp
268
+ │ │ ├── config.h
269
+ │ │ ├── structs.h
270
+ │ │ ├── grid_data.h / .cpp
271
+ │ │ ├── grid_logic.h / .cpp
272
+ │ │ ├── shape_logic.h / .cpp
273
+ │ │ └── game_state.h / .cpp
274
+ │ ├── core/ # Core Python components (now minimal/empty)
275
+ │ │ └── __init__.py
276
+ │ ├── utils/ # General Python utilities ([src/trianglengin/utils/README.md])
277
+ │ │ └── ... (geometry.py, types.py)
278
+ │ ├── config/ # Core configuration models ([src/trianglengin/config/README.md])
279
+ │ │ ├── __init__.py
280
+ │ │ └── env_config.py # EnvConfig (Pydantic)
281
+ │ └── ui/ # UI Components ([src/trianglengin/ui/README.md])
282
+ │ ├── __init__.py # Exposes UI API (Application, cli_app, DisplayConfig)
283
+ │ ├── app.py # Interactive mode application runner
284
+ │ ├── cli.py # CLI definition (play/debug)
285
+ │ ├── config.py # DisplayConfig (Pydantic)
286
+ │ ├── interaction/ # User input handling ([src/trianglengin/ui/interaction/README.md])
287
+ │ │ ├── __init__.py
288
+ │ │ ├── event_processor.py
289
+ │ │ ├── input_handler.py
290
+ │ │ ├── play_mode_handler.py
291
+ │ │ └── debug_mode_handler.py
292
+ │ └── visualization/ # Pygame rendering ([src/trianglengin/ui/visualization/README.md])
293
+ │ ├── __init__.py
294
+ │ ├── core/ # Core vis components ([src/trianglengin/ui/visualization/core/README.md])
295
+ │ │ ├── __init__.py
296
+ │ │ ├── colors.py
297
+ │ │ ├── coord_mapper.py
298
+ │ │ ├── fonts.py
299
+ │ │ ├── layout.py # NEW
300
+ │ │ └── visualizer.py
301
+ │ └── drawing/ # Drawing functions ([src/trianglengin/ui/visualization/drawing/README.md])
302
+ │ ├── __init__.py
303
+ │ ├── grid.py
304
+ │ ├── highlight.py
305
+ │ ├── hud.py
306
+ │ ├── previews.py
307
+ │ ├── shapes.py
308
+ │ └── utils.py
309
+ ├── tests/ # Unit/Integration tests (Python) ([tests/README.md])
310
+ │ ├── __init__.py
311
+ │ ├── conftest.py
312
+ │ └── core/environment/
313
+ │ └── test_game_state.py # Tests the Python GameState wrapper
314
+ ├── .gitignore
315
+ ├── pyproject.toml # Build config, dependencies
316
+ ├── setup.py # C++ Extension build script
317
+ ├── README.md # This file
318
+ ├── LICENSE
319
+ └── MANIFEST.in
320
+ ```
321
+
322
+ ## Core Components (v2)
323
+
324
+ - **`trianglengin.cpp` (C++ Core)**: Implements the high-performance game logic (state, grid, shapes, rules). Not directly imported in Python.
325
+ - **`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`, **`get_outcome`**.
326
+ - **`trianglengin.config.EnvConfig`**: Python Pydantic model for core environment configuration. Passed to C++ core during initialization.
327
+ - **`trianglengin.utils`**: General Python utility functions and types. ([`src/trianglengin/utils/README.md`](src/trianglengin/utils/README.md))
328
+
329
+ ## UI Components (`trianglengin.ui`)
330
+
331
+ - **`trianglengin.ui.config.DisplayConfig`**: Pydantic model for UI display settings.
332
+ - **`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))
333
+ - **`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))
334
+ - **`trianglengin.ui.app.Application`**: Integrates UI components for interactive modes.
335
+ - **`trianglengin.ui.cli`**: Command-line interface (`trianglengin play`/`debug`).
336
+
337
+ ## Contributing
338
+
339
+ 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.
@@ -1,7 +1,7 @@
1
- # File: pyproject.toml
1
+
2
2
  [build-system]
3
3
  requires = [
4
- "setuptools>=61.0", # Keep >=61 for find_packages support if needed, >=77 for new license format preferred
4
+ "setuptools>=61.0",
5
5
  "wheel",
6
6
  "pybind11>=2.10",
7
7
  "cmake>=3.14",
@@ -10,11 +10,10 @@ build-backend = "setuptools.build_meta"
10
10
 
11
11
  [project]
12
12
  name = "trianglengin"
13
- version = "2.0.6" # Incremented version
13
+ version = "2.0.7" # Incremented version
14
14
  authors = [{ name="Luis Guilherme P. M.", email="lgpelin92@gmail.com" }]
15
- description = "High-performance C++/Python engine for a triangle puzzle game." # Updated description
15
+ description = "High-performance C++/Python engine for a triangle puzzle game."
16
16
  readme = "README.md"
17
- # Use SPDX identifier string and specify license file(s)
18
17
  license = "MIT"
19
18
  license-files = ["LICENSE"]
20
19
  requires-python = ">=3.10"
@@ -22,15 +21,13 @@ classifiers = [
22
21
  "Programming Language :: Python :: 3",
23
22
  "Programming Language :: Python :: 3.10",
24
23
  "Programming Language :: Python :: 3.11",
25
- "Programming Language :: C++", # Added C++ classifier
26
- # REMOVED: "License :: OSI Approved :: MIT License", # Deprecated classifier
24
+ "Programming Language :: C++",
27
25
  "Operating System :: OS Independent",
28
26
  "Topic :: Scientific/Engineering :: Artificial Intelligence",
29
27
  "Topic :: Games/Entertainment :: Puzzle Games",
30
28
  "Development Status :: 4 - Beta",
31
29
  ]
32
30
  dependencies = [
33
- # Core dependencies + UI dependencies are now required
34
31
  "numpy>=1.20.0",
35
32
  "pydantic>=2.0.0",
36
33
  "typing_extensions>=4.0.0",
@@ -43,11 +40,9 @@ dependencies = [
43
40
  "Bug Tracker" = "https://github.com/lguibr/trianglengin/issues"
44
41
 
45
42
  [project.scripts]
46
- # Script now points to the CLI inside the 'ui' subpackage
47
43
  trianglengin = "trianglengin.ui.cli:app"
48
44
 
49
45
  [project.optional-dependencies]
50
- # UI section removed
51
46
  dev = [
52
47
  "pytest>=7.0.0",
53
48
  "pytest-cov>=3.0.0",
@@ -57,25 +52,22 @@ dev = [
57
52
  "build",
58
53
  "twine",
59
54
  "codecov",
60
- # No need to list pygame/typer here anymore
61
55
  ]
62
56
 
63
57
  [tool.setuptools]
64
58
  # package_dir is removed from here - handled by setup.py
65
- # include-package-data = true # Optional: Keep if you rely on MANIFEST.in for data files
66
59
 
67
60
  [tool.setuptools.package-data]
68
- # Include C++ headers if needed, py.typed, etc.
69
61
  "trianglengin" = ["py.typed", "cpp/*.h"]
70
62
 
71
63
  # --- Tool Configurations ---
72
64
 
73
65
  [tool.ruff]
74
66
  line-length = 88
75
- src = ["src", "tests"] # Specify source directories
67
+ src = ["src", "tests"]
76
68
 
77
69
  [tool.ruff.lint]
78
- select = ["E", "W", "F", "I", "UP", "B", "C4", "ARG", "SIM", "TCH", "PTH", "NPY", "PYI"] # Added PYI for stubs
70
+ select = ["E", "W", "F", "I", "UP", "B", "C4", "ARG", "SIM", "TCH", "PTH", "NPY", "PYI"]
79
71
  ignore = ["E501"]
80
72
 
81
73
  [tool.ruff.format]
@@ -85,48 +77,35 @@ quote-style = "double"
85
77
  python_version = "3.10"
86
78
  warn_return_any = true
87
79
  warn_unused_configs = true
88
- ignore_missing_imports = false # Be stricter
80
+ ignore_missing_imports = false
89
81
  disallow_untyped_defs = true
90
- # Specify source directory for mypy
91
82
  files = ["src/trianglengin", "tests"]
92
83
 
93
84
  [[tool.mypy.overrides]]
94
- # Ignore the compiled C++ extension module
95
85
  module = "trianglengin.trianglengin_cpp"
96
86
  ignore_missing_imports = true
97
87
 
98
- # No longer need overrides for pygame/typer as they are required
99
-
100
88
 
101
89
  [tool.pytest.ini_options]
102
90
  minversion = "7.0"
103
- # Update coverage path
104
91
  addopts = "-ra -q --cov=src/trianglengin --cov-report=term-missing"
105
92
  testpaths = [
106
93
  "tests",
107
94
  ]
108
95
 
109
96
  [tool.coverage.run]
110
- source = ["src/trianglengin"] # Specify source for coverage
97
+ source = ["src/trianglengin"]
111
98
  omit = [
112
99
  "src/trianglengin/__init__.py",
113
- "src/trianglengin/cpp/*", # Exclude C++ source from Python coverage
114
- "src/trianglengin/config/env_config.py", # Keep config covered (only env_config)
100
+ "src/trianglengin/cpp/*",
101
+ "src/trianglengin/config/env_config.py",
115
102
  "src/trianglengin/utils/types.py",
116
- # Omit the entire UI package
117
103
  "src/trianglengin/ui/*",
118
- # Omit the now empty core package
119
104
  "src/trianglengin/core/*",
120
105
  "tests/*",
121
106
  "setup.py",
122
107
  ]
123
108
 
124
109
  [tool.coverage.report]
125
- fail_under = 70 # Increased target coverage for core logic
126
- show_missing = true
127
-
128
- # Optional: Add cibuildwheel configuration if needed
129
- # [tool.cibuildwheel]
130
- # skip = "pp*" # Example: Skip PyPy builds
131
- # test-requires = "pytest" # Dependencies needed for tests run by cibuildwheel
132
- # test-command = "pytest {project}/tests" # Command to run tests after building wheel
110
+ fail_under = 70
111
+ show_missing = true