From ef82409ba8fef7a6aa6dd323c93c72dd300e4607 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Fri, 29 Nov 2024 10:47:57 -0500 Subject: [PATCH] make_unique, better Tile indexing errors, coalescing dupe tiles --- src/dmm.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++---- src/tile.rs | 52 +++++++++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/src/dmm.rs b/src/dmm.rs index 2ffe46e..46fe3ce 100644 --- a/src/dmm.rs +++ b/src/dmm.rs @@ -1,9 +1,12 @@ extern crate dmm_tools; -use std::collections::btree_map; +use std::io; +use std::borrow::BorrowMut; +use std::collections::{btree_map, HashMap, HashSet}; use std::collections::btree_map::Keys as BTreeMapKeysIter; use std::path::{Path, PathBuf}; +use dmm_tools::dmm::{Key, Prefab}; use itertools::iproduct; use pyo3::exceptions::{PyOSError, PyRuntimeError, PyValueError}; use pyo3::types::{PyAnyMethods, PyList, PyString, PyTuple}; @@ -113,6 +116,61 @@ impl CoordIterator { } } +impl Dmm { + pub fn generate_new_key(&mut self) -> Key { + let mut key: Key = Default::default(); + while self.map.dictionary.contains_key(&key) { + key = key.next(); + } + + self.map.dictionary.insert(key, vec![]); + self.map.adjust_key_length(); + key + } + + pub fn coalesce_duplicate_tiles(&mut self) { + let mut coords_using_keys: HashMap> = HashMap::default(); + let map = &mut self.map; + + // First collect all known keys + map.iter_levels().for_each(|(z, zlvl)| { + zlvl.iter_top_down().for_each(|(coord, key)| { + coords_using_keys.entry(key).or_default().push(coord.z(z)); + }); + }); + + // Then find our prefab collisions, moving the collided keys over to the first one we found + // Then update the coords we know of and move them to the first key + let mut unused_keys: HashSet = HashSet::default(); + let mut prefab_collisions: HashMap<&Vec, &Key> = HashMap::default(); + for (key, prefabs) in &map.dictionary { + if prefab_collisions.contains_key(prefabs) { + unused_keys.insert(*key); + if let Some(coords) = coords_using_keys.get(key) { + for coord in coords { + let dim = map.grid.dim(); + let raw = (coord.z as usize - 1, dim.1 - coord.y as usize, coord.x as usize - 1); + map.grid[raw] = *prefab_collisions[prefabs]; + } + } + } else { + prefab_collisions.insert(prefabs, key); + } + } + + for key in unused_keys { + map.dictionary.borrow_mut().remove_entry(&key); + } + + map.adjust_key_length(); + } + + fn to_file(&mut self, path: &Path) -> io::Result<()> { + self.coalesce_duplicate_tiles(); + self.map.to_file(path) + } +} + #[pymethods] impl Dmm { #[staticmethod] @@ -147,13 +205,13 @@ impl Dmm { }) } - fn save_to(&self, filename: &Bound) -> PyResult<()> { + fn save_to(&mut self, filename: &Bound) -> PyResult<()> { if let Ok(path) = filename.extract::() { - if let Ok(()) = self.map.to_file(&path) { + if let Ok(()) = self.to_file(&path) { return Ok(()); } } else if let Ok(pystr) = filename.downcast::() { - if let Ok(()) = self.map.to_file(Path::new(&pystr.to_string())) { + if let Ok(()) = self.to_file(Path::new(&pystr.to_string())) { return Ok(()); } } diff --git a/src/tile.rs b/src/tile.rs index 4e7b5ab..a4f5321 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -2,7 +2,7 @@ extern crate dmm_tools; use dmm_tools::dmm::Prefab; use pyo3::conversion::ToPyObject; -use pyo3::exceptions::{PyRuntimeError, PyValueError}; +use pyo3::exceptions::{PyIndexError, PyKeyError, PyRuntimeError, PyValueError}; use pyo3::types::{PyAnyMethods, PyDict, PyList, PyString}; use pyo3::{ pyclass, pymethods, Bound, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, @@ -195,21 +195,31 @@ impl Tile { Address::Coords(c) => map[c], }; let prefabs = &map.dictionary[&key]; + if index as usize >= prefabs.len() { + return Err(PyIndexError::new_err("list index out of range")); + } let binding = prefabs[index as usize].path.clone(); let s = binding.as_str(); path::Path::new(s) } - pub fn prefab_var(&self, index: i32, name: String, py: Python<'_>) -> PyObject { + pub fn prefab_var(&self, index: i32, name: String, py: Python<'_>) -> PyResult { let map = &self.dmm.downcast_bound::(py).unwrap().borrow().map; let key = match self.addr { Address::Key(k) => k, Address::Coords(c) => map[c], }; let prefabs = &map.dictionary[&key]; + if index as usize >= prefabs.len() { + return Err(PyIndexError::new_err("list index out of range")); + } + let prefab = &prefabs[index as usize]; + if !prefab.vars.contains_key(&name) { + return Err(PyKeyError::new_err(format!("no varedit {}", name))); + } - constant_to_python_value(prefabs[index as usize].vars.get(&name).unwrap()) + Ok(constant_to_python_value(prefab.vars.get(&name).unwrap())) } #[pyo3(signature = (index, name, default=None))] @@ -219,26 +229,30 @@ impl Tile { name: String, default: Option<&Bound>, py: Python<'_>, - ) -> PyObject { + ) -> PyResult { let map = &self.dmm.downcast_bound::(py).unwrap().borrow().map; let key = match self.addr { Address::Key(k) => k, Address::Coords(c) => map[c], }; let prefabs = &map.dictionary[&key]; + if index as usize >= prefabs.len() { + return Err(PyIndexError::new_err("list index out of range")); + } + let vars = &prefabs[index as usize].vars; if vars.contains_key(&name) { - return constant_to_python_value(vars.get(&name).unwrap()); + return Ok(constant_to_python_value(vars.get(&name).unwrap())); } if let Some(t) = default { - return t.into_py(py); + return Ok(t.into_py(py)); } - py.None() + Ok(py.None()) } - pub fn prefab_vars(&self, index: i32, py: Python<'_>) -> Vec { + pub fn prefab_vars(&self, index: i32, py: Python<'_>) -> PyResult> { let map = &self.dmm.downcast_bound::(py).unwrap().borrow().map; let mut vec = Vec::new(); let key = match self.addr { @@ -246,12 +260,15 @@ impl Tile { Address::Coords(c) => map[c], }; let prefabs = &map.dictionary[&key]; + if index as usize >= prefabs.len() { + return Err(PyIndexError::new_err("list index out of range")); + } prefabs[index as usize].vars.iter().for_each(|(name, _)| { vec.push(name.clone()); }); - vec + Ok(vec) } pub fn set_prefab_var( @@ -321,6 +338,23 @@ impl Tile { ))) } + fn make_unique(&mut self, py: Python<'_>) -> PyResult<()> { + let map = &mut self.dmm.downcast_bound::(py).unwrap().borrow_mut().map; + let dmm = self.dmm.downcast_bound::(py).unwrap(); + match self.addr { + Address::Key(_) => { + return Err(PyErr::new::("can only make Tiles from DMM#tiledef(x, y, z) unique")); + }, + Address::Coords(c) => { + let new_key = dmm.borrow_mut().generate_new_key(); + let dim = map.grid.dim(); + map.dictionary.insert(new_key, map.dictionary[&map[c]].clone()); + map.grid[(c.z as usize - 1, dim.1 - c.y as usize, c.x as usize - 1)] = new_key; + }, + } + Ok(()) + } + fn __repr__(&self, py: Python<'_>) -> PyResult { let map = &self.dmm.downcast_bound::(py).unwrap().borrow().map; Ok(format!(