开发者

python: representing square grid that wraps on itself (cylinder)

I am modeling something that occurs on a square grid that wraps on itself (i.e., if you walk up past the highest point, you end up at the lowest point, like a cylinder; if you walk to the right, you just hit the boundary). I need to keep track of the location of various agents, the amount of resources at different points, and calculate the direction that agents will be moving in based on certain rules.

What's the best way to model this?

Should I make a class that represents points, which has methods to return neighboring points in each direction? If so, I would probably need to make it hashable so that I can use it as keys for the dictionary that contains the full grid (I assume such grid should be a dictionary?)

Or should I make a class that describes the whole grid an开发者_JAVA技巧d not expose individual points as independent objects?

Or should I just use regular (x, y) tuples and have methods elsewhere that allow to look up neighbors?

A lot of what I'll need to model is not yet clearly defined. Furthermore, I expect the geometry of the surface might possibly change one day (e.g., it could wrap on itself in both directions).

EDIT: One additional question: should I attach the information about the quantity of resources to each Point instance; or should I have a separate class that contains a map of resources indexed by Point?


If you want a hashable Point class without too much work, subclass tuple and add your own neighbor methods.

class Point(tuple):
    def r_neighbor(self):
        return Point((self[0] + 1, self[1]))
    def l_neighbor(self): 
        [...]

x = Point((10, 11))
print x
print x.r_neighbor()

The tuple constructor wants an iterable, hence the double-parens in Point((10, 11)); if you want to avoid that, you can always override __new__ (overriding __init__ is pointless because tuples are immutable):

def __new__(self, x, y):
    return super(Point, self).__new__(self, (x, y))

This might also be the place to apply modular arithmetic -- though that will really depend on what you are doing:

def __new__(self, x, y, gridsize=100):
    return super(Point, self).__new__(self, (x % gridsize, y % gridsize))

or to enable arbitrary dimension grids, and go back to using tuples in __new__:

def __new__(self, tup, gridsize=100):
    return super(Point, self).__new__(self, (x % gridsize for x in tup))

Regarding your question about resources: since Point is an immutable class, it's a poor place to store information about resources that might change. A defaultdict would be handy; you wouldn't have to initialize it.

from collections import defaultdict

grid = defaultdict(list)
p = Point((10, 13))
grid[(10, 13)] = [2, 3, 4]
print grid[p]                  # prints [2, 3, 4]
print grid[p.r_neighbor]       # no KeyError; prints []

If you want more flexibility, you could use a dict instead of a list in defaultdict; but defaultdict(defaultdict) won't work; you have to create a new defaultdict factory function.

def intdict():
    return defaultdict(int)

grid = defaultdict(intdict)

or more concisely

grid = defaultdict(lambda: defaultdict(int))

then

p = Point((10, 13))
grid[(10, 13)]["coins"] = 50
print grid[p]["coins"]              # prints 50
print grid[p.r_neighbor]["coins"]   # prints 0; again, no KeyError


I need to keep track of the location of various agents, the amount of resources at different points, and calculate the direction that agents will be moving in based on certain rules.

Sounds like a graph to to me, though I try to see a graph in every problem. All the operations you mentioned (move around, store resources, find out where to move to) are very common on graphs. You would also be able to easily change the topology, from a cylinder to a torus or in any other way.

The only issue is that it takes more space than other representations.

On the plus side you can use a graph library to create the graph and probably even some graph algorithms to calculate where agents go.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜