Reputation: 480
I'd like to create a 2D environment in which the top & bottom and left & right are connected (similar to a Torus or Doughnut). However, rather than doing a check on an object's x/y coordinate every frame, I'd like to simulate it using integer overflow.
While normal iteration (as shown in the sample code below) can be done, it may be slightly more effective (though dangerous) to simply enable overflow on some variables, especially when dealing with hundreds or thousands of objects in each frame/iteration.
I can find some examples which also simulate integer overflow in Python like this. However, I'm looking for something that can overflow just by enabling overflow in certain variables and skipping the checks in general.
# With normal checking of every instance
import random
width = 100
height = 100
class item():
global width, height
def __init__(self):
self.x = random.randint(0, width)
self.y = random.randint(0, height)
items = [item for _ in range(10)] # create 10 instances
while True:
for obj in items:
obj.x += 10
obj.y += 20
while obj.x > width:
obj.x -= width
while obj.y > height:
obj.y -= height
while obj.x < width:
obj.x += width
while obj.y < height:
obj.y += height
I'd like to simulate integer overflow for only some certain classes/objects. Is there a way to have some variables automatically overflow and loop back to their min/max values?
Upvotes: 2
Views: 248
Reputation: 23743
With a descriptor you can You can define the behavior you want for your object attributes. Reading between the lines it sounds like you want your attributes to exhibit a value = value % maximum
behaviour.
from weakref import WeakKeyDictionary
class MaxValue:
'''A descriptor whose value will be: value modulo maximum.'''
def __init__(self, maximum, default=0):
self.maximum = maximum
self.default = default
self.data = WeakKeyDictionary()
def __get__(self, instance, owner):
return self.data.get(instance, self.default)
def __set__(self, instance, value):
self.data[instance] = value % self.maximum
Descriptors should be class attributes. For your example class:
import random
width = 100
height = 100
class Item:
## global width, height
x = MaxValue(width)
y = MaxValue(height)
def __init__(self):
self.x = random.randint(0, width)
self.y = random.randint(0, height)
def __str__(self):
return f'({self.x:>3},{self.y:>3})'
Example:
items = [Item() for _ in range(5)]
print(','.join(f'{item}' for item in items))
for n in range(15):
for item in items:
item.x += 10
item.y += 20
print(','.join(f'{item}' for item in items))
>>>
( 74, 6),( 49, 19),( 56, 10),( 72, 16),( 83, 16)
( 84, 26),( 59, 39),( 66, 30),( 82, 36),( 93, 36)
( 94, 46),( 69, 59),( 76, 50),( 92, 56),( 3, 56)
( 4, 66),( 79, 79),( 86, 70),( 2, 76),( 13, 76)
( 14, 86),( 89, 99),( 96, 90),( 12, 96),( 23, 96)
( 24, 6),( 99, 19),( 6, 10),( 22, 16),( 33, 16)
( 34, 26),( 9, 39),( 16, 30),( 32, 36),( 43, 36)
( 44, 46),( 19, 59),( 26, 50),( 42, 56),( 53, 56)
( 54, 66),( 29, 79),( 36, 70),( 52, 76),( 63, 76)
( 64, 86),( 39, 99),( 46, 90),( 62, 96),( 73, 96)
( 74, 6),( 49, 19),( 56, 10),( 72, 16),( 83, 16)
( 84, 26),( 59, 39),( 66, 30),( 82, 36),( 93, 36)
( 94, 46),( 69, 59),( 76, 50),( 92, 56),( 3, 56)
( 4, 66),( 79, 79),( 86, 70),( 2, 76),( 13, 76)
( 14, 86),( 89, 99),( 96, 90),( 12, 96),( 23, 96)
( 24, 6),( 99, 19),( 6, 10),( 22, 16),( 33, 16)
>>>
Not my idea - picked this up at Python Descriptors Demystified
Each instance of MaxValue
needs to keep track (or know) the value for each instance of Item
- Item.x
needs to know the x
value for Item
instances a
, b
, c
,... . A dictionary is convenient for that and a WeakKeyDictionary is used so that if the dictionary is the only reference to an Item
instance, then that instance can be garbage collected.
This solution alleviates the need to write a getter/setter for each attribute that shares a behaviour.
Upvotes: 1
Reputation: 5073
You can use properties to implement getters/setters with customized behaviour. For example like this:
import random
WIDTH = 100
HEIGHT = 100
class item():
def __init__(self):
self._x = random.randint(0, WIDTH - 1)
self._y = random.randint(0, HEIGHT - 1)
def __str__(self):
return '(%r, %r)' % (self._x, self._y)
@property
def x(self):
return self._x
@x.setter
def x(self, new_value):
self._x = new_value % WIDTH
@property
def y(self):
return self._y
@y.setter
def y(self, new_value):
self._y = new_value % HEIGHT
items = [item() for _ in range(10)]
while True:
for pos in items:
pos.x += 10
pos.y += 20
print(pos) # to show the results
Upvotes: 1