Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294
Per-entity FOV memory moves from std::vector<UIGridPointState> (two-bool visible/discovered pairs) to a 3-state DiscreteMap (0=UNKNOWN, 1=DISCOVERED, 2=VISIBLE), exposed as entity.perspective_map. The invariant visible-subset-of-discovered becomes structural (single value per cell), and the map is a live, serializable, first-class object rather than an implicit internal array. Changes: - New DiscreteMap C++ class with shared ownership; PyDiscreteMapObject now holds shared_ptr<DiscreteMap>. UIEntity holds the same shared_ptr. - New mcrfpy.Perspective IntEnum (UNKNOWN/DISCOVERED/VISIBLE), modelled on PyInputState. - entity.perspective_map: lazy-allocated on first access with a grid; setter validates size against grid and raises ValueError on mismatch; None clears (next access lazy-reallocates fresh). - updateVisibility() now demotes 2->1 then promotes visible cells to 2. - entity.at(x, y) returns grid.at(x, y) when VISIBLE, else None. - Fog-of-war rendering in UIGridView and UIGrid reads the 3-state map. - Removed: UIEntity::gridstate, ensureGridstate(), entity.gridstate getter, UIGridPointState struct + PyUIGridPointStateType. - Obsolete tests deleted (test_gridpointstate_point, issue_265_gridpointstate_dangle); 4 new tests cover lazy allocation, identity, serialization round-trip, size validation, and the visible-subset-of-discovered invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
417fc43325
commit
f797120d53
24 changed files with 7998 additions and 1906 deletions
|
|
@ -368,6 +368,9 @@ PyObject* PyDiscreteMap::pynew(PyTypeObject* type, PyObject* args, PyObject* kwd
|
|||
{
|
||||
PyDiscreteMapObject* self = (PyDiscreteMapObject*)type->tp_alloc(type, 0);
|
||||
if (self) {
|
||||
// Placement-new the shared_ptr member; tp_alloc zeroed memory but
|
||||
// shared_ptr requires proper construction before assignment.
|
||||
new (&self->data) std::shared_ptr<DiscreteMap>();
|
||||
self->values = nullptr;
|
||||
self->w = 0;
|
||||
self->h = 0;
|
||||
|
|
@ -419,27 +422,24 @@ int PyDiscreteMap::init(PyDiscreteMapObject* self, PyObject* args, PyObject* kwd
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Clean up any existing data
|
||||
if (self->values) {
|
||||
delete[] self->values;
|
||||
self->values = nullptr;
|
||||
}
|
||||
// Reset any existing storage (re-init supported)
|
||||
self->data.reset();
|
||||
Py_XDECREF(self->enum_type);
|
||||
self->enum_type = nullptr;
|
||||
|
||||
// Allocate new array
|
||||
size_t total_size = static_cast<size_t>(width) * static_cast<size_t>(height);
|
||||
self->values = new (std::nothrow) uint8_t[total_size];
|
||||
if (!self->values) {
|
||||
// Construct shared-ownership C++ storage (issue #294)
|
||||
try {
|
||||
self->data = std::make_shared<DiscreteMap>(
|
||||
width, height, static_cast<uint8_t>(fill_value));
|
||||
} catch (const std::bad_alloc&) {
|
||||
PyErr_SetString(PyExc_MemoryError, "Failed to allocate DiscreteMap");
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->w = width;
|
||||
self->h = height;
|
||||
|
||||
// Fill with initial value
|
||||
memset(self->values, static_cast<uint8_t>(fill_value), total_size);
|
||||
// Cache non-owning views for hot-path access
|
||||
self->values = self->data->data();
|
||||
self->w = self->data->width();
|
||||
self->h = self->data->height();
|
||||
|
||||
// Store enum type if provided
|
||||
if (enum_obj && enum_obj != Py_None) {
|
||||
|
|
@ -452,10 +452,10 @@ int PyDiscreteMap::init(PyDiscreteMapObject* self, PyObject* args, PyObject* kwd
|
|||
|
||||
void PyDiscreteMap::dealloc(PyDiscreteMapObject* self)
|
||||
{
|
||||
if (self->values) {
|
||||
delete[] self->values;
|
||||
self->values = nullptr;
|
||||
}
|
||||
// Release shared ownership; DiscreteMap destructor frees the buffer
|
||||
// if this is the last owner.
|
||||
self->data.~shared_ptr<DiscreteMap>();
|
||||
self->values = nullptr;
|
||||
Py_XDECREF(self->enum_type);
|
||||
self->enum_type = nullptr;
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
|
|
@ -1387,14 +1387,19 @@ PyObject* PyDiscreteMap::from_bytes(PyTypeObject* type, PyObject* args, PyObject
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
obj->w = w;
|
||||
obj->h = h;
|
||||
obj->values = new (std::nothrow) uint8_t[expected];
|
||||
if (!obj->values) {
|
||||
new (&obj->data) std::shared_ptr<DiscreteMap>();
|
||||
try {
|
||||
obj->data = std::make_shared<DiscreteMap>(w, h, 0);
|
||||
} catch (const std::bad_alloc&) {
|
||||
PyBuffer_Release(&buffer);
|
||||
obj->data.~shared_ptr<DiscreteMap>();
|
||||
Py_DECREF(obj);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
obj->w = w;
|
||||
obj->h = h;
|
||||
obj->values = obj->data->data();
|
||||
std::memcpy(obj->values, buffer.buf, expected);
|
||||
PyBuffer_Release(&buffer);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue