From 446150e4ac3ab753cbfdf558e795ddb5e8464c7b Mon Sep 17 00:00:00 2001 From: rowan Date: Tue, 10 Jun 2025 21:54:23 -0400 Subject: [PATCH] upload the stuff !! --- godot/addons/collections/bst.gd | 139 ++++++++++++++++++++++++++ godot/addons/collections/int_range.gd | 54 ++++++++++ godot/addons/collections/set.gd | 89 +++++++++++++++++ godot/addons/iterator/fused.gd | 3 +- godot/addons/iterator/singleton.gd | 14 +++ godot/addons/monads/option.gd | 4 +- godot/addons/monads/result.gd | 20 ++-- godot/addons/range/integer.gd | 20 ---- godot/src/h_item_list.gd | 39 ++------ 9 files changed, 315 insertions(+), 67 deletions(-) create mode 100644 godot/addons/collections/bst.gd create mode 100644 godot/addons/collections/int_range.gd create mode 100644 godot/addons/collections/set.gd create mode 100644 godot/addons/iterator/singleton.gd delete mode 100644 godot/addons/range/integer.gd diff --git a/godot/addons/collections/bst.gd b/godot/addons/collections/bst.gd new file mode 100644 index 0000000..618a623 --- /dev/null +++ b/godot/addons/collections/bst.gd @@ -0,0 +1,139 @@ +class_name BST + +class _Node extends RefCounted: + var value: int + var left: Option = Option.none + var right: Option = Option.none + + func _init(value: int) -> void: + self.value = value + + func size() -> int: + return BST.size(self) + + func has_children() -> bool: + return BST.has_children(self) + + func balance() -> _Node: + return BST.balance(self).unwrap() + + func insert(value: int) -> _Node: + return BST.insert(self, value) + + func search(value: int) -> Option: + return BST.search(self, value) + + func remove(value: int) -> Option: + return BST.remove(self, value) + + func iter() -> Iterator: + return BST.iter(self) + +static func from(value: int) -> _Node: + return _Node.new(value) + +static func size(node: _Node) -> int: + var left = node.left.map(size).or_default(0) + var right = node.right.map(size).or_default(0) + return left + right + 1 + +static func _store_in_order(_root: Option, nodes: Array[_Node] = []): + if _root.is_none(): return + + var root = _root.unwrap() + _store_in_order(root.left, nodes) + nodes.append(root) + _store_in_order(root.right, nodes) + +static func _build_balanced_tree(nodes: Array[_Node], start: int = 0, end: int = len(nodes) - 1) -> Option: + if start > end: + return Option.none + + var mid: int = (start + end) / 2 + var root = nodes[mid] + + root.left = _build_balanced_tree(nodes, start, mid - 1) + root.right = _build_balanced_tree(nodes, mid + 1, end) + + return Option.some(root) + +static func balance(root: _Node) -> Option: + var nodes: Array[_Node] = [] + _store_in_order(Option.some(root), nodes) + return _build_balanced_tree(nodes, 0, len(nodes) - 1) + +static func has_children(node: _Node) -> bool: + return node.left.is_none() and node.right.is_none() + +static func _insert(_root: Option, node: _Node) -> _Node: + if _root.is_none(): return node + + var root = _root.unwrap() + if root.value == node.value: return root + elif root.value < node.value: + root.right = Option.some(_insert(root.right, node)) + else: + root.left = Option.some(_insert(root.left, node)) + return root + +static func insert(root: _Node, value: int) -> _Node: + return _insert(Option.some(root), _Node.new(value)) + +static func _search(_root: Option, value: int) -> Option: + if _root.is_none(): + return Option.none + var root = _root.unwrap() + if root.value == value: + return _root + if root.value < value: + return _search(root.right, value) + return _search(root.left, value) + +static func search(root: _Node, value: int) -> Option: + return _search(Option.some(root), value) + +static func _get_successor(node: _Node) -> _Node: + var current = node.right + while current.is_some() and current.left.is_some(): + current = node.left.unwrap() + return current + +static func _remove(_root: Option, value: int) -> Option: + if _root.is_none(): return _root + + var root = _root.unwrap() + if root.value < value: + return _remove(root.right, value) + elif root.value > value: + return _remove(root.left, value) + else: + if root.right.is_none(): + return root.left + elif root.left.is_none(): + return root.right + + var successor = _get_successor(root) + root.value = successor.value + root.right = remove(root.right, successor.value) + + return Option.some(root) + +static func remove(root: _Node, value: int) -> Option: + return _remove(Option.some(root), value) + +static func traverse(root: _Node, fn: Callable): + root.left.map(fn.call) + fn.call(root.value) + root.right.map(fn.call) + +static func iter(node: _Node) -> Iterator: + var iters = Option.collect_some([ + node.left.map(iter), + Option.some(SingletonIterator.new(node.value)), + node.right.map(iter) + ], true).unwrap() + + var iter_arr: Array[Iterator] + iter_arr.assign(iters) + + return FusedIterator.new(iter_arr) diff --git a/godot/addons/collections/int_range.gd b/godot/addons/collections/int_range.gd new file mode 100644 index 0000000..6ccfb6b --- /dev/null +++ b/godot/addons/collections/int_range.gd @@ -0,0 +1,54 @@ +class_name IntRange extends RefCounted + +var min: int +var max: int + +static var Zero = IntRange.new(0, 0) + +func _init(start: int, end: int, inclusive: bool = false) -> void: + min = start + max = end if not inclusive else end + 1 + +func length() -> int: + return absi(max - min) + +func is_zero() -> bool: + return min == 0 and max == 0 + +func is_empty() -> bool: + return is_zero() + +func iter(step: int = 1) -> RangeIterator: + return RangeIterator.new(self, min, step) + +func clamp(value: int) -> int: + return clampi(value, min, max) + +func wrap(value: int) -> int: + return wrapi(value, min, max) + +func contains(value: int) -> bool: + return value >= min and value < max + +func is_superset_of(range: IntRange) -> bool: + return contains(range.min) and contains(range.max) + +func is_subset_of(range: IntRange) -> bool: + return range.is_superset_of(self) + +func overlaps(range: IntRange) -> bool: + return contains(range.min) or range.contains(min) or contains(range.max) or range.contains(max) + +func difference(other: IntRange) -> Set: + if is_subset_of(other): + return Set.empty() + elif not overlaps(other): + return Set.from_iter(iter()) + + return Set.from_iter(iter()).difference(Set.from_iter(other.iter())) + +func union(other: IntRange) -> Set: + return Set.from_iter(iter()).union(Set.from_iter(other.iter())) + +func intersect(other: IntRange) -> Set: + return Set.from_iter(iter()).intersect(Set.from_iter(other.iter())) diff --git a/godot/addons/collections/set.gd b/godot/addons/collections/set.gd new file mode 100644 index 0000000..300aab9 --- /dev/null +++ b/godot/addons/collections/set.gd @@ -0,0 +1,89 @@ +class_name Set extends BST._Node + +var data: Variant + +func _init(data: Variant = null) -> void: + super(_get_value(data)) + self.data = data + +static func _get_value(value: Variant) -> int: + match typeof(value): + TYPE_NIL: return -1 + TYPE_INT: return value + _: return hash(value) + +static func empty() -> Set: + return Set.new(null) + +static func from_iter(iter: Variant) -> Set: + var nodes: Array[_Node] = [] + for value in iter: + nodes.append(Set.new(value)) + return BST._build_balanced_tree(nodes).unwrap() + +func is_empty() -> bool: + return value == -1 and not has_children() + +func contains(data: Variant) -> bool: + return BST.search(self, _get_value(data)).is_some() + +func insert(data: Variant) -> _Node: + if is_empty(): + self.value = _get_value(data) + self.data = data + return self + elif not contains(data): + return BST._insert(Option.some(self), Set.new(data)) + else: + return self + +func remove(data: Variant) -> Option: + return BST.remove(self, _get_value(data)) + +func get(data: Variant) -> Option: + return BST.search(self, _get_value(data)).map(func(x: Set) -> Variant: return x.data) + +func equals(other: Set) -> bool: + if not size() == other.size(): return false + + for value in iter(): + if not other.contains(value): return false + + return true + +func union(other: Set) -> Set: + var u = Set.from_iter(iter()) + for value in other.iter(): + u.insert(value) + return u.balance() + +func intersect(other: Set) -> Set: + var i = Set.empty() + for value in iter(): + if other.contains(value): + i.insert(value) + return i.balance() + +func difference(other: Set) -> Set: + if self.is_empty(): return Set.empty() + elif other.is_empty(): return Set.from_iter(iter()) + var diff = Set.empty() + for value in iter(): + if not other.contains(value): + diff.insert(value) + return diff.balance() + +static func _iter(set: Set) -> Iterator: + var iters = Option.collect_some([ + set.left.map(_iter), + Option.none if set.value == -1 else Option.some(SingletonIterator.new(set.data)), + set.right.map(_iter) + ], true).unwrap() + + var iter_arr: Array[Iterator] + iter_arr.assign(iters) + + return FusedIterator.new(iter_arr) + +func iter() -> Iterator: + return _iter(self) diff --git a/godot/addons/iterator/fused.gd b/godot/addons/iterator/fused.gd index a34adf1..5b077cb 100644 --- a/godot/addons/iterator/fused.gd +++ b/godot/addons/iterator/fused.gd @@ -16,7 +16,8 @@ func next() -> Option: if _index >= len(_iters): return Option.none match _iters[_index].next(): - var value when value.is_some(): return value + var value when value.is_some(): + return value _: _index += 1 return next() diff --git a/godot/addons/iterator/singleton.gd b/godot/addons/iterator/singleton.gd new file mode 100644 index 0000000..e209923 --- /dev/null +++ b/godot/addons/iterator/singleton.gd @@ -0,0 +1,14 @@ +class_name SingletonIterator extends Iterator + +var value: Variant +var emitted: bool = false + +func _init(value: Variant) -> void: + self.value = value + +func next() -> Option: + if not emitted: + emitted = true + return Option.some(value) + else: + return Option.none diff --git a/godot/addons/monads/option.gd b/godot/addons/monads/option.gd index 03acb5b..eb43dcb 100644 --- a/godot/addons/monads/option.gd +++ b/godot/addons/monads/option.gd @@ -7,12 +7,12 @@ static func some(value: Variant) -> Option: static var none: Option.None = None.new() -static func collect_some(options: Array[Option]) -> Option: +static func collect_some(options: Array[Option], ignore_none: bool = false) -> Option: var result = [] for option in options: if option.is_some(): result.push_back(option.unwrap()) - else: + elif not ignore_none: return Option.none return Option.some(result) diff --git a/godot/addons/monads/result.gd b/godot/addons/monads/result.gd index 94f815f..4c13413 100644 --- a/godot/addons/monads/result.gd +++ b/godot/addons/monads/result.gd @@ -8,20 +8,16 @@ static func ok(value: Variant) -> Result: static func err(error: Variant) -> Result: return Err.new(error) -static func collect_ok(results: Array[Result]) -> Result: - match results: - []: return Result.Unit - [var x]: return x - _: - var res = [] +static func collect_ok(results: Array[Result], ignore_err: bool = false) -> Result: + var res = [] - for result in results: - if result.is_ok(): - res.push_back(result.unwrap()) - else: - return result + for result in results: + if result.is_ok(): + res.push_back(result.unwrap()) + elif not ignore_err: + return result - return Result.ok(res) + return Result.ok(res) class Ok extends Result: var value: Variant diff --git a/godot/addons/range/integer.gd b/godot/addons/range/integer.gd deleted file mode 100644 index 8e22f1f..0000000 --- a/godot/addons/range/integer.gd +++ /dev/null @@ -1,20 +0,0 @@ -class_name IntRange extends RefCounted - -var min: int -var max: int - -func _init(start: int, end: int, inclusive: bool = false) -> void: - min = start - max = end if not inclusive else end + 1 - -func length() -> int: - return absi(max - min) - -func clamp(value: int) -> int: - return clampi(value, min, max) - -func wrap(value: int) -> int: - return wrapi(value, min, max) - -func contains(value: int) -> bool: - return value >= min and value < max diff --git a/godot/src/h_item_list.gd b/godot/src/h_item_list.gd index c7f0c2c..fc01e80 100644 --- a/godot/src/h_item_list.gd +++ b/godot/src/h_item_list.gd @@ -11,10 +11,14 @@ var bind_item: Callable = _bind_item var ring_buffer: RingBuffer func _ready() -> void: + var a = IntRange.new(0, 6) + var b = IntRange.new(5, 7) + for value in a.difference(b).iter(): + print(value) ring_buffer = RingBuffer.new(items.size(), buffer_size) ring_buffer.updated.connect(_on_updated) - for _i in range(max_items): + for _i in range(buffer_size): add_child(create_item.call()) func _create_item() -> Node: @@ -30,8 +34,8 @@ func _on_updated(_current_range: IntRange, _previous_range: IntRange): func _update_display(): var indices: Array = ring_buffer.range().collect() - for i in range(min(indices.size(), ring_buffer.capacity)): - var node = get_child(i) + for i in range(min(indices.size(), max_items)): + var node = get_child(i + 1) var item = items[indices[i]] bind_item.call(node, item) @@ -44,32 +48,3 @@ func add_items(new_items: Array): items.append_array(new_items) ring_buffer.source_size = items.size() _update_display() - - -# inventory ui -# two parts -# * list of items -# * selected item details -# -# list of items is a window into the inventory -# eg. a list of 5 items with a ui that can display 3 where the third item is selected: -# [1, (2, 3, 4), 5] -# -# add_item(item): - # append item to item array - # if new index occurs in display window: - # bind element - -# remove_item(item): - # index = find index - # remove_index(index) - -# remove_index(index): - # remove item at index - -# move(direction): - # if direction is left: - # move last element to first - # else: - # move first element to last - # bind new element