Generate random number outside of range in python

后端 未结 5 1237
盖世英雄少女心
盖世英雄少女心 2021-02-18 22:52

I\'m currently working on a pygame game and I need to place objects randomly on the screen, except they cannot be within a designated rectangle. Is there an easy way to do this

5条回答
  •  南方客
    南方客 (楼主)
    2021-02-18 23:30

    I've already posted a different answer that I still like, as it is simple and clear, and not necessarily slow... at any rate it's not exactly what the OP asked for.

    I thought about it and I devised an algorithm for solving the OP's problem within their constraints:

    1. partition the screen in 9 rectangles around and comprising the "hole".
    2. consider the 8 rectangles ("tiles") around the central hole"
    3. for each tile, compute the origin (x, y), the height and the area in pixels
    4. compute the cumulative sum of the areas of the tiles, as well as the total area of the tiles
    5. for each extraction, choose a random number between 0 and the total area of the tiles (inclusive and exclusive)
    6. using the cumulative sums determine in which tile the random pixel lies
    7. using divmod determine the column and the row (dx, dy) in the tile
    8. using the origins of the tile in the screen coordinates, compute the random pixel in screen coordinates.

    To implement the ideas above, in which there is an initialization phase in which we compute static data and a phase in which we repeatedly use those data, the natural data structure is a class, and here it is my implementation

    from random import randrange
    
    class make_a_hole_in_the_screen():
    
        def __init__(self, screen, hole_orig, hole_sizes):
            xs, ys = screen
            x, y = hole_orig
            wx, wy = hole_sizes
            tiles = [(_y,_x*_y) for _x in [x,wx,xs-x-wx] for _y in [y,wy,ys-y-wy]]
            self.tiles = tiles[:4] + tiles[5:]
            self.pixels = [tile[1] for tile in self.tiles]
            self.total = sum(self.pixels)
            self.boundaries = [sum(self.pixels[:i+1]) for i in range(8)]
            self.x = [0,    0,    0,
                      x,          x,
                      x+wx, x+wx, x+wx]
            self.y = [0,    y,    y+wy,
                      0,          y+wy,
                      0,    y,    y+wy]
    
        def choose(self):
            n = randrange(self.total)
            for i, tile in enumerate(self.tiles):
                if n < self.boundaries[i]: break
            n1 = n - ([0]+self.boundaries)[i]
            dx, dy = divmod(n1,self.tiles[i][0])
            return self.x[i]+dx, self.y[i]+dy
    

    To test the correctness of the implementation, here it is a rough check that I run on python 2.7,

    drilled_screen = make_a_hole_in_the_screen((200,100),(30,50),(20,30))
    for i in range(1000000):
        x, y = drilled_screen.choose()
        if 30<=x<50 and 50<=y<80: print "***", x, y
        if x<0 or x>=200 or y<0 or y>=100: print "+++", x, y
    

    A possible optimization consists in using a bisection algorithm to find the relevant tile in place of the simpler linear search that I've implemented.

提交回复
热议问题