1

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?

ChocolateOverflow
  • 460
  • 1
  • 4
  • 11
  • 4
    Python ints don't overflow. If they did, they wouldn't overflow at 100 or any other value of your choice, they would overflow at some power of 2 based on the bit size of the computer. You just want `obj.x = (obj.x + 10) % width`, for example. – jasonharper May 26 '19 at 14:31
  • If the maximum is 100, when `item.x` is `99` will `item.x += 10` result in `item.x --> 9` or `item.x --> 0`? – wwii May 26 '19 at 16:35
  • jasonharper's ```obj.x = (obj.x + 10) % width``` is very much like what I'm looking for, where 99 + 10 makes 9. I'd like to be able to enable overflow, even if it's only for 2^x integers. – ChocolateOverflow May 27 '19 at 02:35

2 Answers2

1

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
wovano
  • 4,543
  • 5
  • 22
  • 49
  • 1
    Another way to use `property` is described in the [Descriptor HOWTO](https://docs.python.org/3/howto/descriptor.html?highlight=descriptor#properties). – wwii May 26 '19 at 19:59
0

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.

wwii
  • 23,232
  • 7
  • 37
  • 77
  • Interesting answer! I didn't know that much about descriptors, but this seems a nice solution in general. Although I think that the use of weak references makes the solution a bit more complicated than necessary. It just doesn't feel right for me. Do you know of any other way to avoid that? – wovano May 26 '19 at 18:09
  • @wovano, I looked around and another option would be to store the value in the `Item` instances, the descriptor's `__init__` would need a *field* parameter (`self.field=field`); `__get__(self, instance, owner)` would look like `return instance.__dict__.get(self.field, self.default)`; and `__set__(self, instance, value)` would look like `instance.__dict__[self.field] = value % self.maximum`. – wwii May 27 '19 at 04:14
  • [Example on github](https://github.com/VDBWRAIR/pyRoche/blob/d116f8525871e806bb46790105b4d619d939d012/roche/newbler/fileparsers/descriptors.py#L1) that stores on the *instances*. – wwii May 27 '19 at 04:21
  • Thanks for the searching and additional comments. I will take a closer look at them later! Descriptors seem to have the potential to provide a better solution, but the solutions I've seen so far still feel a bit clumsy, IMHO. I'm still looking for the optimal solution :-) – wovano May 27 '19 at 06:34