trianglengin 2.0.4__pp310-pypy310_pp73-win_amd64.whl → 2.0.6__pp310-pypy310_pp73-win_amd64.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/Release/trianglengin_cpp.pypy310-pp73-win_amd64.pyd +0 -0
- trianglengin/cpp/game_state.cpp +89 -83
- trianglengin/cpp/game_state.h +10 -1
- trianglengin/cpp/grid_data.cpp +18 -5
- trianglengin/cpp/grid_data.h +4 -5
- trianglengin/cpp/structs.h +30 -1
- trianglengin/trianglengin_cpp.pypy310-pp73-win_amd64.pyd +0 -0
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/METADATA +1 -1
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/RECORD +13 -13
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/WHEEL +0 -0
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/entry_points.txt +0 -0
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/licenses/LICENSE +0 -0
- {trianglengin-2.0.4.dist-info → trianglengin-2.0.6.dist-info}/top_level.txt +0 -0
Binary file
|
trianglengin/cpp/game_state.cpp
CHANGED
@@ -12,7 +12,7 @@ namespace trianglengin::cpp
|
|
12
12
|
|
13
13
|
GameStateCpp::GameStateCpp(const EnvConfigCpp &config, unsigned int initial_seed)
|
14
14
|
: config_(config),
|
15
|
-
grid_data_(config_),
|
15
|
+
grid_data_(config_), // Initialize GridData with config
|
16
16
|
shapes_(config_.num_shape_slots),
|
17
17
|
score_(0.0),
|
18
18
|
current_step_(0),
|
@@ -22,6 +22,39 @@ namespace trianglengin::cpp
|
|
22
22
|
reset();
|
23
23
|
}
|
24
24
|
|
25
|
+
// --- Explicit Copy Constructor ---
|
26
|
+
GameStateCpp::GameStateCpp(const GameStateCpp &other)
|
27
|
+
: config_(other.config_), // Copy config
|
28
|
+
grid_data_(other.grid_data_), // Use GridData's copy constructor
|
29
|
+
shapes_(other.shapes_), // Copy vector of optional shapes
|
30
|
+
score_(other.score_), // Copy score
|
31
|
+
current_step_(other.current_step_), // Copy step
|
32
|
+
game_over_(other.game_over_), // Copy game over flag
|
33
|
+
game_over_reason_(other.game_over_reason_), // Copy reason
|
34
|
+
valid_actions_cache_(other.valid_actions_cache_), // Copy the optional cache
|
35
|
+
rng_(other.rng_) // Copy the RNG state
|
36
|
+
{
|
37
|
+
// No additional logic needed if members handle their own copying well
|
38
|
+
}
|
39
|
+
|
40
|
+
// --- Explicit Copy Assignment Operator ---
|
41
|
+
GameStateCpp &GameStateCpp::operator=(const GameStateCpp &other)
|
42
|
+
{
|
43
|
+
if (this != &other)
|
44
|
+
{
|
45
|
+
config_ = other.config_;
|
46
|
+
grid_data_ = other.grid_data_; // Use GridData's copy assignment
|
47
|
+
shapes_ = other.shapes_;
|
48
|
+
score_ = other.score_;
|
49
|
+
current_step_ = other.current_step_;
|
50
|
+
game_over_ = other.game_over_;
|
51
|
+
game_over_reason_ = other.game_over_reason_;
|
52
|
+
valid_actions_cache_ = other.valid_actions_cache_; // Copy the optional cache
|
53
|
+
rng_ = other.rng_; // Copy the RNG state
|
54
|
+
}
|
55
|
+
return *this;
|
56
|
+
}
|
57
|
+
|
25
58
|
void GameStateCpp::reset()
|
26
59
|
{
|
27
60
|
grid_data_.reset();
|
@@ -37,12 +70,9 @@ namespace trianglengin::cpp
|
|
37
70
|
|
38
71
|
void GameStateCpp::check_initial_state_game_over()
|
39
72
|
{
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
force_game_over("No valid actions available at start.");
|
44
|
-
// REMOVED C++ Log: std::cerr << "[GameStateCpp::check_initial_state_game_over] Forced game over: No valid actions at start." << std::endl;
|
45
|
-
}
|
73
|
+
// Force calculation which updates the cache and potentially the game_over flag
|
74
|
+
get_valid_actions(true);
|
75
|
+
// No need to check cache emptiness here, get_valid_actions handles setting the flag
|
46
76
|
}
|
47
77
|
|
48
78
|
std::tuple<double, bool> GameStateCpp::step(Action action)
|
@@ -52,14 +82,16 @@ namespace trianglengin::cpp
|
|
52
82
|
return {0.0, true};
|
53
83
|
}
|
54
84
|
|
55
|
-
|
85
|
+
// Ensure cache is populated before checking the action
|
86
|
+
const auto &valid_actions = get_valid_actions(); // This populates cache if needed
|
87
|
+
|
88
|
+
// Check against the (now guaranteed) populated cache
|
56
89
|
if (valid_actions.find(action) == valid_actions.end())
|
57
90
|
{
|
58
91
|
// Invalid action detected
|
59
92
|
force_game_over("Invalid action provided: " + std::to_string(action));
|
60
|
-
// Apply penalty
|
61
|
-
|
62
|
-
return {config_.penalty_game_over, true}; // Return penalty and done=true
|
93
|
+
score_ += config_.penalty_game_over; // Apply penalty
|
94
|
+
return {config_.penalty_game_over, true};
|
63
95
|
}
|
64
96
|
|
65
97
|
int shape_idx, r, c;
|
@@ -69,27 +101,25 @@ namespace trianglengin::cpp
|
|
69
101
|
}
|
70
102
|
catch (const std::out_of_range &e)
|
71
103
|
{
|
72
|
-
// Error during decoding (should be caught by valid_actions check, but defensive)
|
73
104
|
force_game_over("Failed to decode action: " + std::to_string(action));
|
74
|
-
score_ += config_.penalty_game_over;
|
105
|
+
score_ += config_.penalty_game_over;
|
75
106
|
return {config_.penalty_game_over, true};
|
76
107
|
}
|
77
108
|
|
78
109
|
if (shape_idx < 0 || shape_idx >= static_cast<int>(shapes_.size()) || !shapes_[shape_idx].has_value())
|
79
110
|
{
|
80
|
-
// Action references invalid slot (should be caught by valid_actions check)
|
81
111
|
force_game_over("Action references invalid/empty shape slot: " + std::to_string(shape_idx));
|
82
|
-
score_ += config_.penalty_game_over;
|
112
|
+
score_ += config_.penalty_game_over;
|
83
113
|
return {config_.penalty_game_over, true};
|
84
114
|
}
|
85
115
|
|
86
116
|
const ShapeCpp &shape_to_place = shapes_[shape_idx].value();
|
87
117
|
|
118
|
+
// Re-check placement just before modification (defensive)
|
88
119
|
if (!grid_logic::can_place(grid_data_, shape_to_place, r, c))
|
89
120
|
{
|
90
|
-
// Should not happen if valid_actions is correct
|
91
121
|
force_game_over("Placement check failed for valid action (logic error?). Action: " + std::to_string(action));
|
92
|
-
score_ += config_.penalty_game_over;
|
122
|
+
score_ += config_.penalty_game_over;
|
93
123
|
return {config_.penalty_game_over, true};
|
94
124
|
}
|
95
125
|
|
@@ -105,10 +135,11 @@ namespace trianglengin::cpp
|
|
105
135
|
std::tie(dr, dc, is_up_ignored) = tri_data;
|
106
136
|
int target_r = r + dr;
|
107
137
|
int target_c = c + dc;
|
138
|
+
// Bounds check should be implicitly handled by can_place, but double-check
|
108
139
|
if (!grid_data_.is_valid(target_r, target_c) || grid_data_.is_death(target_r, target_c))
|
109
140
|
{
|
110
141
|
force_game_over("Attempted placement out of bounds/death zone during execution. Action: " + std::to_string(action));
|
111
|
-
score_ += config_.penalty_game_over;
|
142
|
+
score_ += config_.penalty_game_over;
|
112
143
|
return {config_.penalty_game_over, true};
|
113
144
|
}
|
114
145
|
occupied_grid[target_r][target_c] = true;
|
@@ -116,7 +147,7 @@ namespace trianglengin::cpp
|
|
116
147
|
newly_occupied_coords.insert({target_r, target_c});
|
117
148
|
placed_count++;
|
118
149
|
}
|
119
|
-
shapes_[shape_idx] = std::nullopt;
|
150
|
+
shapes_[shape_idx] = std::nullopt; // Clear the used shape slot
|
120
151
|
|
121
152
|
// --- Line Clearing ---
|
122
153
|
int lines_cleared_count;
|
@@ -143,9 +174,8 @@ namespace trianglengin::cpp
|
|
143
174
|
|
144
175
|
// --- Update State & Check Game Over ---
|
145
176
|
current_step_++;
|
146
|
-
|
147
|
-
get_valid_actions(true);
|
148
|
-
// game_over_ flag is now definitive
|
177
|
+
invalidate_action_cache(); // Invalidate cache AFTER state changes
|
178
|
+
get_valid_actions(true); // Force recalculation AND update game_over_ flag if needed
|
149
179
|
|
150
180
|
// --- Calculate Reward & Update Score ---
|
151
181
|
double reward = 0.0;
|
@@ -154,7 +184,8 @@ namespace trianglengin::cpp
|
|
154
184
|
|
155
185
|
if (game_over_)
|
156
186
|
{
|
157
|
-
|
187
|
+
// Penalty was applied earlier if game over was due to invalid action this step.
|
188
|
+
// If game over is due to lack of actions *after* this step, no penalty applies here.
|
158
189
|
}
|
159
190
|
else
|
160
191
|
{
|
@@ -162,34 +193,24 @@ namespace trianglengin::cpp
|
|
162
193
|
}
|
163
194
|
score_ += reward; // Update score based on calculated reward
|
164
195
|
|
165
|
-
return {reward, game_over_};
|
196
|
+
return {reward, game_over_}; // Return the potentially updated game_over_ flag
|
166
197
|
}
|
167
198
|
|
199
|
+
// --- Simplified is_over ---
|
168
200
|
bool GameStateCpp::is_over() const
|
169
201
|
{
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
if (valid_actions_cache_.has_value())
|
174
|
-
return valid_actions_cache_->empty();
|
175
|
-
// Need to remove const to calculate and potentially set game_over_
|
176
|
-
const_cast<GameStateCpp *>(this)->calculate_valid_actions_internal();
|
177
|
-
// Check game_over_ again as calculate might have set it
|
178
|
-
if (game_over_)
|
179
|
-
return true;
|
180
|
-
// Now the cache should exist
|
181
|
-
return valid_actions_cache_->empty();
|
202
|
+
// The game_over_ flag is the single source of truth.
|
203
|
+
// It's updated by step() [via get_valid_actions()] and reset().
|
204
|
+
return game_over_;
|
182
205
|
}
|
183
206
|
|
184
207
|
void GameStateCpp::force_game_over(const std::string &reason)
|
185
208
|
{
|
186
|
-
// This function only sets the flags and reason.
|
187
|
-
// Score update should happen in the context where the game over is triggered (e.g., step).
|
188
209
|
if (!game_over_)
|
189
210
|
{
|
190
211
|
game_over_ = true;
|
191
212
|
game_over_reason_ = reason;
|
192
|
-
valid_actions_cache_ = std::set<Action>();
|
213
|
+
valid_actions_cache_ = std::set<Action>(); // Clear valid actions on game over
|
193
214
|
}
|
194
215
|
}
|
195
216
|
|
@@ -200,25 +221,34 @@ namespace trianglengin::cpp
|
|
200
221
|
|
201
222
|
const std::set<Action> &GameStateCpp::get_valid_actions(bool force_recalculate)
|
202
223
|
{
|
224
|
+
// If game is over, always return the cached empty set
|
203
225
|
if (game_over_)
|
204
226
|
{
|
227
|
+
// Ensure cache is empty if game_over is true
|
205
228
|
if (!valid_actions_cache_.has_value() || !valid_actions_cache_->empty())
|
206
229
|
{
|
207
230
|
valid_actions_cache_ = std::set<Action>();
|
208
231
|
}
|
209
232
|
return *valid_actions_cache_;
|
210
233
|
}
|
234
|
+
|
235
|
+
// If not forcing and cache exists, return it
|
211
236
|
if (!force_recalculate && valid_actions_cache_.has_value())
|
212
237
|
{
|
213
238
|
return *valid_actions_cache_;
|
214
239
|
}
|
240
|
+
|
241
|
+
// Otherwise, calculate (which updates the mutable cache)
|
215
242
|
calculate_valid_actions_internal();
|
216
|
-
|
243
|
+
|
244
|
+
// Check if the calculation resulted in no valid actions, triggering game over
|
217
245
|
if (!game_over_ && valid_actions_cache_->empty())
|
218
246
|
{
|
247
|
+
// Set the game over flag HERE, as this is the definitive check after calculation
|
219
248
|
force_game_over("No valid actions available.");
|
220
|
-
// REMOVED C++ Log: std::cerr << "[GameStateCpp::get_valid_actions] Forced game over: No valid actions found after calculation." << std::endl;
|
221
249
|
}
|
250
|
+
|
251
|
+
// Return the calculated (and potentially now empty) cache
|
222
252
|
return *valid_actions_cache_;
|
223
253
|
}
|
224
254
|
|
@@ -227,19 +257,12 @@ namespace trianglengin::cpp
|
|
227
257
|
valid_actions_cache_ = std::nullopt;
|
228
258
|
}
|
229
259
|
|
230
|
-
//
|
231
|
-
void GameStateCpp::calculate_valid_actions_internal() const
|
260
|
+
// calculate_valid_actions_internal remains const, modifies mutable cache
|
261
|
+
void GameStateCpp::calculate_valid_actions_internal() const
|
232
262
|
{
|
233
|
-
|
234
|
-
|
235
|
-
valid_actions_cache_ = std::set<Action>();
|
236
|
-
// REMOVED C++ Log: std::cerr << "[GameStateCpp::calculate_valid_actions_internal] Game already over. Returning empty set." << std::endl;
|
237
|
-
return;
|
238
|
-
}
|
263
|
+
// This function should NOT set game_over_ directly.
|
264
|
+
// It just calculates the set. The caller (get_valid_actions) checks if empty.
|
239
265
|
std::set<Action> valid_actions;
|
240
|
-
int can_place_true_count = 0; // Counter for debugging
|
241
|
-
int attempts_count = 0; // Counter for total placement checks
|
242
|
-
|
243
266
|
for (int shape_idx = 0; shape_idx < static_cast<int>(shapes_.size()); ++shape_idx)
|
244
267
|
{
|
245
268
|
if (!shapes_[shape_idx].has_value())
|
@@ -247,46 +270,26 @@ namespace trianglengin::cpp
|
|
247
270
|
const ShapeCpp &shape = shapes_[shape_idx].value();
|
248
271
|
for (int r = 0; r < config_.rows; ++r)
|
249
272
|
{
|
250
|
-
|
251
|
-
for (int c =
|
273
|
+
// Optimization: Check only within playable range? No, C++ can_place handles death zones.
|
274
|
+
for (int c = 0; c < config_.cols; ++c)
|
252
275
|
{
|
253
|
-
|
254
|
-
bool can_place_result = grid_logic::can_place(grid_data_, shape, r, c); // Store result
|
255
|
-
if (can_place_result)
|
276
|
+
if (grid_logic::can_place(grid_data_, shape, r, c))
|
256
277
|
{
|
257
278
|
valid_actions.insert(encode_action(shape_idx, r, c));
|
258
|
-
can_place_true_count++; // Increment counter
|
259
279
|
}
|
260
280
|
}
|
261
281
|
}
|
262
282
|
}
|
263
|
-
//
|
264
|
-
// std::cerr << "[GameStateCpp::calculate_valid_actions_internal] Step: " << current_step_
|
265
|
-
// << ", Attempts: " << attempts_count
|
266
|
-
// << ", CanPlaceTrue: " << can_place_true_count
|
267
|
-
// << ", ValidActionsFound: " << valid_actions.size()
|
268
|
-
// << std::endl;
|
269
|
-
|
270
|
-
// Use mutable cache
|
271
|
-
valid_actions_cache_ = std::move(valid_actions);
|
283
|
+
valid_actions_cache_ = std::move(valid_actions); // Update the mutable cache
|
272
284
|
}
|
273
285
|
|
274
286
|
int GameStateCpp::get_current_step() const { return current_step_; }
|
275
287
|
std::optional<std::string> GameStateCpp::get_game_over_reason() const { return game_over_reason_; }
|
276
288
|
|
289
|
+
// Python-facing copy method uses the C++ copy constructor
|
277
290
|
GameStateCpp GameStateCpp::copy() const
|
278
291
|
{
|
279
|
-
GameStateCpp
|
280
|
-
// Copy the cache state explicitly
|
281
|
-
if (this->valid_actions_cache_.has_value())
|
282
|
-
{
|
283
|
-
newState.valid_actions_cache_ = this->valid_actions_cache_;
|
284
|
-
}
|
285
|
-
else
|
286
|
-
{
|
287
|
-
newState.valid_actions_cache_ = std::nullopt;
|
288
|
-
}
|
289
|
-
return newState;
|
292
|
+
return GameStateCpp(*this);
|
290
293
|
}
|
291
294
|
|
292
295
|
void GameStateCpp::debug_toggle_cell(int r, int c)
|
@@ -304,33 +307,36 @@ namespace trianglengin::cpp
|
|
304
307
|
grid_logic::check_and_clear_lines(grid_data_, {{r, c}});
|
305
308
|
}
|
306
309
|
invalidate_action_cache(); // Always invalidate after manual change
|
310
|
+
// Force recalculation of valid actions and game over state after toggle
|
311
|
+
get_valid_actions(true);
|
307
312
|
}
|
308
313
|
}
|
309
314
|
|
310
315
|
void GameStateCpp::debug_set_shapes(const std::vector<std::optional<ShapeCpp>> &new_shapes)
|
311
316
|
{
|
312
|
-
// Overwrite the current shapes with the provided ones.
|
313
|
-
// Ensure the size matches the number of slots.
|
314
317
|
size_t num_to_copy = std::min(new_shapes.size(), shapes_.size());
|
315
318
|
for (size_t i = 0; i < num_to_copy; ++i)
|
316
319
|
{
|
317
320
|
shapes_[i] = new_shapes[i];
|
318
321
|
}
|
319
|
-
// Fill remaining slots with nullopt if new_shapes is shorter
|
320
322
|
for (size_t i = num_to_copy; i < shapes_.size(); ++i)
|
321
323
|
{
|
322
324
|
shapes_[i] = std::nullopt;
|
323
325
|
}
|
324
|
-
// Invalidate cache as valid actions will change
|
325
326
|
invalidate_action_cache();
|
327
|
+
// Force recalculation of valid actions and game over state after setting shapes
|
328
|
+
get_valid_actions(true);
|
326
329
|
}
|
327
330
|
|
328
331
|
Action GameStateCpp::encode_action(int shape_idx, int r, int c) const
|
329
332
|
{
|
330
333
|
int grid_size = config_.rows * config_.cols;
|
334
|
+
// Basic bounds check (more robust check in can_place)
|
331
335
|
if (shape_idx < 0 || shape_idx >= config_.num_shape_slots || r < 0 || r >= config_.rows || c < 0 || c >= config_.cols)
|
332
336
|
{
|
333
|
-
|
337
|
+
// This case should ideally not be reached if called after can_place
|
338
|
+
// Return an invalid action index or throw? Let's throw for internal logic errors.
|
339
|
+
throw std::out_of_range("encode_action arguments out of range during valid action calculation.");
|
334
340
|
}
|
335
341
|
return shape_idx * grid_size + r * config_.cols + c;
|
336
342
|
}
|
trianglengin/cpp/game_state.h
CHANGED
@@ -27,6 +27,13 @@ namespace trianglengin::cpp
|
|
27
27
|
public:
|
28
28
|
explicit GameStateCpp(const EnvConfigCpp &config, unsigned int initial_seed);
|
29
29
|
|
30
|
+
// --- Rule of 5 ---
|
31
|
+
~GameStateCpp() = default; // Default destructor
|
32
|
+
GameStateCpp(const GameStateCpp &other); // Copy constructor
|
33
|
+
GameStateCpp &operator=(const GameStateCpp &other); // Copy assignment operator
|
34
|
+
GameStateCpp(GameStateCpp &&other) noexcept = default; // Default move constructor
|
35
|
+
GameStateCpp &operator=(GameStateCpp &&other) noexcept = default; // Default move assignment operator
|
36
|
+
|
30
37
|
void reset();
|
31
38
|
std::tuple<double, bool> step(Action action);
|
32
39
|
bool is_over() const;
|
@@ -34,7 +41,7 @@ namespace trianglengin::cpp
|
|
34
41
|
const std::set<Action> &get_valid_actions(bool force_recalculate = false);
|
35
42
|
int get_current_step() const;
|
36
43
|
std::optional<std::string> get_game_over_reason() const;
|
37
|
-
GameStateCpp copy() const;
|
44
|
+
GameStateCpp copy() const; // Keep Python-facing copy method
|
38
45
|
void debug_toggle_cell(int r, int c);
|
39
46
|
void invalidate_action_cache(); // Moved to public
|
40
47
|
// Debug method to force shapes into slots
|
@@ -46,6 +53,8 @@ namespace trianglengin::cpp
|
|
46
53
|
const std::vector<std::optional<ShapeCpp>> &get_shapes() const { return shapes_; }
|
47
54
|
std::vector<std::optional<ShapeCpp>> &get_shapes_mut() { return shapes_; }
|
48
55
|
const EnvConfigCpp &get_config() const { return config_; }
|
56
|
+
// Expose RNG state for copying if needed (or handle seeding in copy)
|
57
|
+
std::mt19937 get_rng_state() const { return rng_; }
|
49
58
|
|
50
59
|
private:
|
51
60
|
EnvConfigCpp config_;
|
trianglengin/cpp/grid_data.cpp
CHANGED
@@ -44,7 +44,7 @@ namespace trianglengin::cpp
|
|
44
44
|
precompute_lines();
|
45
45
|
}
|
46
46
|
|
47
|
-
// Copy constructor:
|
47
|
+
// Copy constructor: Explicitly copy all members
|
48
48
|
GridData::GridData(const GridData &other)
|
49
49
|
: config_(other.config_), // Copy config value
|
50
50
|
rows_(other.rows_),
|
@@ -55,9 +55,11 @@ namespace trianglengin::cpp
|
|
55
55
|
lines_(other.lines_),
|
56
56
|
coord_to_lines_map_(other.coord_to_lines_map_)
|
57
57
|
{
|
58
|
+
// All members are copyable, default member-wise copy is sufficient here,
|
59
|
+
// but being explicit ensures correctness if members change later.
|
58
60
|
}
|
59
61
|
|
60
|
-
// Copy assignment operator:
|
62
|
+
// Copy assignment operator: Explicitly copy all members
|
61
63
|
GridData &GridData::operator=(const GridData &other)
|
62
64
|
{
|
63
65
|
if (this != &other)
|
@@ -103,7 +105,8 @@ namespace trianglengin::cpp
|
|
103
105
|
{
|
104
106
|
throw std::out_of_range("Coordinates (" + std::to_string(r) + "," + std::to_string(c) + ") out of bounds.");
|
105
107
|
}
|
106
|
-
|
108
|
+
// An occupied cell cannot be a death cell by game logic after placement/clearing
|
109
|
+
return occupied_grid_[r][c];
|
107
110
|
}
|
108
111
|
|
109
112
|
std::optional<int> GridData::get_color_id(int r, int c) const
|
@@ -190,6 +193,7 @@ namespace trianglengin::cpp
|
|
190
193
|
for (const auto &direction : directions)
|
191
194
|
{
|
192
195
|
Coord line_start_coord = start_coord;
|
196
|
+
// Find the true start of the line segment in this direction
|
193
197
|
while (true)
|
194
198
|
{
|
195
199
|
auto prev_coord_opt = get_neighbor(std::get<0>(line_start_coord), std::get<1>(line_start_coord), direction, true);
|
@@ -200,8 +204,11 @@ namespace trianglengin::cpp
|
|
200
204
|
else
|
201
205
|
break;
|
202
206
|
}
|
207
|
+
// Check if we already processed this line starting from this coordinate and direction
|
203
208
|
if (processed_starts.count({line_start_coord, direction}))
|
204
209
|
continue;
|
210
|
+
|
211
|
+
// Trace the line forward from the true start
|
205
212
|
Line current_line;
|
206
213
|
std::optional<Coord> trace_coord_opt = line_start_coord;
|
207
214
|
while (trace_coord_opt && is_live(std::get<0>(*trace_coord_opt), std::get<1>(*trace_coord_opt)))
|
@@ -209,7 +216,9 @@ namespace trianglengin::cpp
|
|
209
216
|
current_line.push_back(*trace_coord_opt);
|
210
217
|
trace_coord_opt = get_neighbor(std::get<0>(*trace_coord_opt), std::get<1>(*trace_coord_opt), direction, false);
|
211
218
|
}
|
212
|
-
|
219
|
+
|
220
|
+
// Store the line if it's long enough and mark it as processed
|
221
|
+
if (current_line.size() >= 2) // Only store lines of length 2 or more
|
213
222
|
{
|
214
223
|
maximal_lines_set.insert(current_line);
|
215
224
|
processed_starts.insert({line_start_coord, direction});
|
@@ -218,16 +227,20 @@ namespace trianglengin::cpp
|
|
218
227
|
}
|
219
228
|
}
|
220
229
|
|
230
|
+
// Convert set to vector and sort for deterministic order
|
221
231
|
lines_ = std::vector<Line>(maximal_lines_set.begin(), maximal_lines_set.end());
|
222
232
|
std::sort(lines_.begin(), lines_.end(), [](const Line &a, const Line &b)
|
223
233
|
{
|
224
|
-
if (a.empty() || b.empty()) return b.empty();
|
234
|
+
if (a.empty() || b.empty()) return b.empty(); // Handle empty lines if they somehow occur
|
235
|
+
// Sort primarily by starting row, then starting column, then size
|
225
236
|
if (std::get<0>(a[0]) != std::get<0>(b[0])) return std::get<0>(a[0]) < std::get<0>(b[0]);
|
226
237
|
if (std::get<1>(a[0]) != std::get<1>(b[0])) return std::get<1>(a[0]) < std::get<1>(b[0]);
|
227
238
|
return a.size() < b.size(); });
|
228
239
|
|
240
|
+
// Build the coordinate-to-lines map
|
229
241
|
for (const auto &line_vec : lines_)
|
230
242
|
{
|
243
|
+
// Use a set of Coords (LineFs) as the value in the map for efficient lookup
|
231
244
|
LineFs line_fs(line_vec.begin(), line_vec.end());
|
232
245
|
for (const auto &coord : line_vec)
|
233
246
|
{
|
trianglengin/cpp/grid_data.h
CHANGED
@@ -48,10 +48,9 @@ namespace trianglengin::cpp
|
|
48
48
|
int rows() const { return rows_; }
|
49
49
|
int cols() const { return cols_; }
|
50
50
|
|
51
|
-
// Copy
|
52
|
-
GridData(const GridData &other);
|
53
|
-
// Copy assignment operator
|
54
|
-
GridData &operator=(const GridData &other);
|
51
|
+
// --- Explicit Copy Control ---
|
52
|
+
GridData(const GridData &other); // Copy constructor
|
53
|
+
GridData &operator=(const GridData &other); // Copy assignment operator
|
55
54
|
|
56
55
|
// Default move constructor/assignment should work now
|
57
56
|
GridData(GridData &&other) noexcept = default;
|
@@ -75,4 +74,4 @@ namespace trianglengin::cpp
|
|
75
74
|
|
76
75
|
} // namespace trianglengin::cpp
|
77
76
|
|
78
|
-
#endif // TRIANGLENGIN_CPP_GRID_DATA_H
|
77
|
+
#endif // TRIANGLENGIN_CPP_GRID_DATA_H
|
trianglengin/cpp/structs.h
CHANGED
@@ -8,7 +8,9 @@
|
|
8
8
|
#include <tuple>
|
9
9
|
#include <string>
|
10
10
|
#include <cstdint>
|
11
|
-
#include <utility>
|
11
|
+
#include <utility> // For std::move
|
12
|
+
#include <optional> // For optional members
|
13
|
+
#include <set> // For previous valid actions
|
12
14
|
|
13
15
|
namespace trianglengin::cpp
|
14
16
|
{
|
@@ -35,6 +37,33 @@ namespace trianglengin::cpp
|
|
35
37
|
return triangles == other.triangles && color == other.color && color_id == other.color_id;
|
36
38
|
}
|
37
39
|
};
|
40
|
+
|
41
|
+
// --- NEW: Structure to hold undo information ---
|
42
|
+
struct StepUndoInfo
|
43
|
+
{
|
44
|
+
// Cells changed by placement or clearing
|
45
|
+
// Stores (row, col, previous_occupied_state, previous_color_id)
|
46
|
+
std::vector<std::tuple<int, int, bool, int8_t>> changed_cells;
|
47
|
+
|
48
|
+
// Info about the shape that was consumed by the step
|
49
|
+
int consumed_shape_slot = -1;
|
50
|
+
std::optional<ShapeCpp> consumed_shape = std::nullopt;
|
51
|
+
|
52
|
+
// Previous game state variables
|
53
|
+
double previous_score = 0.0;
|
54
|
+
int previous_step = 0;
|
55
|
+
bool previous_game_over = false;
|
56
|
+
std::optional<std::string> previous_game_over_reason = std::nullopt;
|
57
|
+
|
58
|
+
// Cache invalidation marker (or potentially the previous cache itself)
|
59
|
+
// Storing the previous cache might be large, maybe just invalidate?
|
60
|
+
// Let's start by just marking that the cache *was* valid before the step.
|
61
|
+
bool was_action_cache_valid = false;
|
62
|
+
// OPTIONAL (more complex): Store the actual previous cache
|
63
|
+
// std::optional<std::set<Action>> previous_valid_actions_cache = std::nullopt;
|
64
|
+
};
|
65
|
+
// --- END NEW ---
|
66
|
+
|
38
67
|
} // namespace trianglengin::cpp
|
39
68
|
|
40
69
|
#endif // TRIANGLENGIN_CPP_STRUCTS_H
|
Binary file
|
@@ -1,8 +1,8 @@
|
|
1
1
|
trianglengin/__init__.py,sha256=VBYKMivxX19prPkxmSCSJJrjFXDVWkQJ9b5kQ6RzvkI,954
|
2
2
|
trianglengin/game_interface.py,sha256=7zvt61QGmAuagRfNMpRORhX11sy9NqWM9xWstsEawKY,9540
|
3
3
|
trianglengin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
trianglengin/trianglengin_cpp.pypy310-pp73-win_amd64.pyd,sha256=
|
5
|
-
trianglengin/Release/trianglengin_cpp.pypy310-pp73-win_amd64.pyd,sha256=
|
4
|
+
trianglengin/trianglengin_cpp.pypy310-pp73-win_amd64.pyd,sha256=4qhbJfu42HZKB9lwDyu-hZe0xWotvevVrchQnWyCuAU,244224
|
5
|
+
trianglengin/Release/trianglengin_cpp.pypy310-pp73-win_amd64.pyd,sha256=4qhbJfu42HZKB9lwDyu-hZe0xWotvevVrchQnWyCuAU,244224
|
6
6
|
trianglengin/config/README.md,sha256=zPN8ezwsN5fmH4xCsVSgOZwfyng_TwrCj3MlwEq63Y4,1544
|
7
7
|
trianglengin/config/__init__.py,sha256=sdr4D-pSCYmfXY2jOLEjPra2VewDiaWNwx1bWKp0nf8,168
|
8
8
|
trianglengin/config/display_config.py,sha256=ivZEGTCuUfmiVU6GJpVpEASFh30AAHSH42PIIi6bPZg,1862
|
@@ -11,15 +11,15 @@ trianglengin/core/__init__.py,sha256=LWZkoJctRGUhd5DZUJlPCS7usdQLZyKYkoyWUWO3L4o
|
|
11
11
|
trianglengin/cpp/CMakeLists.txt,sha256=vmhTQLRD7LRPbfFlisVVJ9-cCkeM3UMrikIde7TRLWc,1370
|
12
12
|
trianglengin/cpp/bindings.cpp,sha256=okdR2NCdxgP_FTgMjwXNbzcaFdERRrvy7m1eB_5FY8g,8899
|
13
13
|
trianglengin/cpp/config.h,sha256=rZZ6os70igGgA_fp-81fMv9TQMFJb91m6T-KeTOpKAU,742
|
14
|
-
trianglengin/cpp/game_state.cpp,sha256
|
15
|
-
trianglengin/cpp/game_state.h,sha256=
|
16
|
-
trianglengin/cpp/grid_data.cpp,sha256=
|
17
|
-
trianglengin/cpp/grid_data.h,sha256=
|
14
|
+
trianglengin/cpp/game_state.cpp,sha256=TTwJm-9BBvof2tntAVdQ5fjMSjqAh2F4wFsMkFlgn48,13068
|
15
|
+
trianglengin/cpp/game_state.h,sha256=no5D0SP-vijTuL-sObJ4tx06nW0oaX42mzMZn1matL8,3302
|
16
|
+
trianglengin/cpp/grid_data.cpp,sha256=6iucpVisR1dYayKjSPbjCaYEKaqWaNqGVswyLm6f-zM,8024
|
17
|
+
trianglengin/cpp/grid_data.h,sha256=HVH5gqXAuHwDJrC2d7uy_maeS2ulckeGy68TkpL9gNg,2568
|
18
18
|
trianglengin/cpp/grid_logic.cpp,sha256=O8CfzMw_3r1fU0Iu3KnapPBi5kC7tSs47yWeaVsIxOw,3457
|
19
19
|
trianglengin/cpp/grid_logic.h,sha256=kWUheuixufHfywbzxY_ZEjk_3ayOnCTXk41CvAbVdpc,810
|
20
20
|
trianglengin/cpp/shape_logic.cpp,sha256=O7OrzngMmU4oLUlHhbbtwzg_he3pfdDK1EA1N8fXF_g,4232
|
21
21
|
trianglengin/cpp/shape_logic.h,sha256=GZPK748iKFunOgwus7ZVvUoC_Bu36PxK6JatxoN0hGc,653
|
22
|
-
trianglengin/cpp/structs.h,sha256=
|
22
|
+
trianglengin/cpp/structs.h,sha256=7bcabAxsiWE2lkinYn6rKQaEgzQ69yWHYiBKrTX4Ry0,2223
|
23
23
|
trianglengin/ui/README.md,sha256=NB-CFYy4sNmS6ld3YFmIax4p8UQmhnd28eDucaV8TqE,1753
|
24
24
|
trianglengin/ui/__init__.py,sha256=ANMOGFAYXw2pbATBwVvSZjfCVQvBN8-RvDg_oWHBXS8,579
|
25
25
|
trianglengin/ui/app.py,sha256=XkT5wBJPBMLZIeKDvL57GGQXC9pqdRuT6mrzgy9hIVw,4149
|
@@ -51,9 +51,9 @@ trianglengin/ui/visualization/drawing/utils.py,sha256=gtGtt8bgaIvctFA5TEmKqmXNov
|
|
51
51
|
trianglengin/utils/__init__.py,sha256=pnFOkerF1TAz9oCzkNYBw6N56uENsyOiMZzA0cQ2SrA,185
|
52
52
|
trianglengin/utils/geometry.py,sha256=nrhr5C3MGO-_xXz96w1B0OJNaATLbc_AeZwoB_O6gsI,2128
|
53
53
|
trianglengin/utils/types.py,sha256=N_CjVJISvBP5QG1QYvepgORD0oNpMJlJRCJ9IdgjmWc,241
|
54
|
-
trianglengin-2.0.
|
55
|
-
trianglengin-2.0.
|
56
|
-
trianglengin-2.0.
|
57
|
-
trianglengin-2.0.
|
58
|
-
trianglengin-2.0.
|
59
|
-
trianglengin-2.0.
|
54
|
+
trianglengin-2.0.6.dist-info/licenses/LICENSE,sha256=DNjSCXzwafRxA5zjyWccoRXZTDMpTNElFpNVKz4NEpY,1098
|
55
|
+
trianglengin-2.0.6.dist-info/METADATA,sha256=p_tcQFMpt0tSbMQ5R9lgaGXnfMvnvjMFSdjyJ1G_TNU,12316
|
56
|
+
trianglengin-2.0.6.dist-info/WHEEL,sha256=swajkNOprSPE94y57o5LFBDVlRC4NETdBeo0gLNdwn8,108
|
57
|
+
trianglengin-2.0.6.dist-info/entry_points.txt,sha256=kQEqO_U-MEpMEC0xwOPSucBzQIq2Ny7XwCtFSruZhvY,57
|
58
|
+
trianglengin-2.0.6.dist-info/top_level.txt,sha256=YsSWmp_2zM23wRc5TRERHpVCgQuVYieYHDTpnwVQC7Y,13
|
59
|
+
trianglengin-2.0.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|