fix off-by-one in bounded range

This commit is contained in:
Rowan 2025-06-11 00:32:39 -04:00
parent d0310c693b
commit 143dc9d288
5 changed files with 50 additions and 23 deletions

View file

@ -1,13 +1,17 @@
class_name IntRange extends RefCounted class_name IntRange extends RefCounted
var min: int var minmax: PackedInt32Array
var max: int
var min: int:
get: return minmax[0]
var max: int:
get: return minmax[1]
static var Zero = IntRange.new(0, 0) static var Zero = IntRange.new(0, 0)
func _init(start: int, end: int, inclusive: bool = false) -> void: func _init(start: int, end: int, inclusive: bool = false) -> void:
min = start minmax = PackedInt32Array([start, end if not inclusive else end + 1])
max = end if not inclusive else end + 1
func length() -> int: func length() -> int:
return absi(max - min) return absi(max - min)

View file

@ -23,6 +23,7 @@ func next() -> Option:
return current_value return current_value
elif limit > 0: elif limit > 0:
limit -= 1 limit -= 1
index = bounds.wrap(index) var current_value = bounds.wrap(index)
return Option.some(index) index = current_value + 1
return Option.some(current_value)
else: return Option.none else: return Option.none

View file

@ -2,7 +2,7 @@ class_name HItemList extends HBoxContainer
@export var item_scene: PackedScene @export var item_scene: PackedScene
@export var max_items: int = 4 @export var max_items: int = 4
@export var buffered_items: int = 2 @export var buffered_items: int = 1
@onready var buffer_size = max_items + (buffered_items * 2) @onready var buffer_size = max_items + (buffered_items * 2)
var items: Array = [] var items: Array = []
@ -11,12 +11,22 @@ var bind_item: Callable = _bind_item
var ring_buffer: RingBuffer var ring_buffer: RingBuffer
func _ready() -> void: func _ready() -> void:
ring_buffer = RingBuffer.new(items.size(), buffer_size) ring_buffer = RingBuffer.new(items.size(), buffer_size, 1, -buffered_items)
ring_buffer.updated.connect(_on_updated) ring_buffer.updated.connect(_on_updated)
for _i in range(buffer_size): for _i in range(buffer_size):
add_child(create_item.call()) add_child(create_item.call())
_hide_buffered_items()
func _hide_buffered_items():
for i in range(buffered_items):
get_child(i).hide()
var length = buffer_size - 1
for i in range(length, length - buffered_items, -1):
get_child(i).hide()
func _create_item() -> Node: func _create_item() -> Node:
return item_scene.instantiate() return item_scene.instantiate()
@ -30,9 +40,12 @@ func _on_updated(_current_range: IntRange, _previous_range: IntRange):
func _update_display(): func _update_display():
var indices: Array = ring_buffer.range().collect() var indices: Array = ring_buffer.range().collect()
prints(indices, ring_buffer.start_index, ring_buffer.end_index, ring_buffer.capacity)
for i in range(min(indices.size(), max_items)): for i in range(min(indices.size(), max_items)):
var node = get_child(i + 1) var node = get_child(i)
prints(ring_buffer.index(i))
var item = items[indices[i]] var item = items[indices[i]]
if item != null:
bind_item.call(node, item) bind_item.call(node, item)
func add_item(item: Variant): func add_item(item: Variant):

View file

@ -4,6 +4,4 @@ class_name InventoryUI extends VBoxContainer
@export var item_list: HItemList @export var item_list: HItemList
func _ready() -> void: func _ready() -> void:
print(inventory)
print(inventory.items)
item_list.add_items(inventory.items) item_list.add_items(inventory.items)

View file

@ -12,7 +12,7 @@ var source_size: int:
get: return _source_size get: return _source_size
set(value): set_source_size(value) set(value): set_source_size(value)
func set_source_size(value: int): func set_source_size(value: int) -> void:
assert(value >= 0, "source size must be a non-negative integer") assert(value >= 0, "source size must be a non-negative integer")
_source_size = value _source_size = value
@ -21,44 +21,55 @@ var capacity: int:
get: return _capacity get: return _capacity
set(value): set_capacity(value) set(value): set_capacity(value)
func set_capacity(value: int): func set_capacity(value: int) -> void:
_capacity = value _capacity = value
var _offset: int
var offset: int:
get: return _offset
set(value): set_offset(value)
func set_offset(value: int) -> void:
_offset = value
var _index: int var _index: int
var selected_index: int: var selected_index: int:
get: return _index get: return _index
set(value): set_selected(value) set(value): set_selected(value)
func set_selected(value: int): func set_selected(value: int) -> void:
_index = self.wrap(value) _index = self.index(value)
var start_index: int: var start_index: int:
get: return _index get: return self.index(_offset + _index)
var end_index: int: var end_index: int:
get: return self.wrap(_index + _capacity - 1) get: return self.index(_offset + _index + _capacity - 1)
@warning_ignore("shadowed_variable") @warning_ignore("shadowed_variable")
func _init(source_size: int, capacity: int, start_index: int = 0) -> void: func _init(source_size: int, capacity: int, start_index: int = 0, offset: int = 0) -> void:
self.source_size = source_size self.source_size = source_size
self.capacity = capacity self.capacity = capacity
selected_index = start_index selected_index = start_index
self.offset = offset
func wrap(value: int) -> int: func index(value: int) -> int:
if _source_size == 0: if _source_size == 0:
return 0 return 0
elif value < 0:
return _source_size + value % _source_size
else: else:
return value % _source_size return value % _source_size
func range() -> RangeIterator: func range() -> BoundedRangeIterator:
return RangeIterator.new(IntRange.new(start_index, end_index)) return BoundedRangeIterator.new(IntRange.new(start_index, end_index), IntRange.new(0, capacity))
func move(direction: Direction) -> Result: func move(direction: Direction) -> Result:
if _source_size <= _capacity: if _source_size <= _capacity:
return Result.err("source size is smaller than buffer capacity") return Result.err("source size is smaller than buffer capacity")
var previous = range() var previous = range()
_index = self.wrap(_index + direction) _index = self.index(_index + direction)
updated.emit(range(), previous) updated.emit(range(), previous)
return Result.Unit return Result.Unit