Pygame Random Terrain Spawning - python

I'm trying to create a game in which 100 rocks are scattered through a large series of coordinates randomly. The problem is that sometimes the rocks may overlap each other. The solution I came up with was to check if the rocks overlap each other, and if they do, create new randomly generate coordinates for the rock again and again until it collides with nothing. I'm not entirely sure how to do this; I've tried detecting collision between the rock and the rock list, but all that ends up happening is that it thinks that it collides with itself and always returns as True. The __init__ function generates the coordinates with random.randint(-2500,2500). When the rocks are created in the for loop, each rock is added to the list. In the update function it checks for rectangular collision between the rocks. How can I fix this? Thanks, an answer would be much appreciated. Ask me if you need more information.

Well, I guess there are a few ways you can approach this problem:
1) The first would be the one you already used, you would check for collisions each time you generate a rock and if a collision exist you would regenerate a position for the given rock
2) The second one would be to slice your "field" into rectangles of the size of the rock, just like a tilemap, therefore creating a list of possible positions, something like this:
possible_positions = [(i, j) for i in range(x_size_field//x_size_rock) for j in range(y_size_field//y_size_rock)]
for i in range(n_of_rocks):
rock_pos = random.choice(possible_positions)
possible_positions.remove(rock_pos)
But this approach would implicate in a given set of possible positions that make a "uniform" rock distribution
3) So if its really necessary to make put the rocks on absolute random positions you could create a list of possible positions like the following:
possible_positions = [[(i, j) for j in range(y_size_field-y_size_rock)] for i in range(x_size_field-x_size_rock)]
for i in range(n_of_rocks):
# X and Y represente positions on the list not on the field
x = random.randint(0, len(possible_positions))
y = random.randint(0, len(possible_positions[x]))
# The rock positions
rock_pos = possible_positions[x][y]
# Now we remove the positions on the region of the new rock
for i in range(x,x+x_size_rock):
possible_positions[i] = possible_positions[i][0:y] + possible_positions[i][y+y_size_rock:-1]
if [] in possible_positions:
possible_positions.remove([])
Of course this code may generate errors (its a rather simple code) and it needs some optimizations, but i think you may get the general ideia from this.
Sorry about my english

Related

How can I stop enemies from overlapping pygame

I'm trying to find a way to have enemies track the player in my 2d game (pygame) but not clump up
Currently, when I shoot at them, the bullet collides into and damages all of the enemies that are clumped. I would like it to be a hoard but spread out just enough to where I can't hit every single enemy at once
It looks like this
Here's a gif of them clumping
I'm not sure how I would get the individual values of the enemies' positions so I can move them when they collide or how I should move them
This is what I currently have for the enemies to track the player:
for aliveEnemies in enemy:
if playerObj.rect.x - aliveEnemies.rect.x != 0:
if playerObj.rect.x > aliveEnemies.rect.x:
aliveEnemies.rect.x += 1
if playerObj.rect.x < aliveEnemies.rect.x:
aliveEnemies.rect.x -= 1
if playerObj.rect.y - aliveEnemies.rect.y != 0:
if playerObj.rect.y > aliveEnemies.rect.y:
aliveEnemies.rect.y += 1
if playerObj.rect.y < aliveEnemies.rect.y:
aliveEnemies.rect.y -= 1"
Any help or points in the right direction would be greatly appreciated
You can do collision detection between the enemies, to determine which ones are too close. You'll also need to change their behavior, to decide what to do when they actually get too close.
If you know you'll never get too many enemies, you can try comparing every enemy with every other enemy. This will take O(N^2) work, but that is probably OK if N is limited.
If you are comparing every enemy to every other anyway, you have a wider variety of options than just "collision detection": like the Boids algorithm (which does collision avoidance instead).
Pygame rect objects have a function called "colliderect" which tests whether two rect objects are overlapping: https://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
You can use this to test each enemy whether they're overlapping any other enemy before moving them.

python go board game, struggling to figure out how to capture multiple stones

I've been trying to make a board game of GO, in python with pygame and a numpy array for storing the board locations and stone colors (0, 1, 2). My issue is that I've been using a for loop to examine each index at a time, and examining the adjacent indexes by adding or taking away an int:
columns = 5
rows = 5
def create_board():
board = np.zeros((row,col))
return board
board = create_board()
def libertyCheck():
for c in range(columns):
for r in range(rows):
if board[c][r+1] == 0:
x = 1
(lots more ifs, then add x's to see if all spaces are occupied)
This method seems to be OK for capturing a single stone (as long as its not on the edges of the board, because it also starts causing out of bounds issues), but becomes very convoluted once several stones of the same color are next to each other and may or may not need to be captured.
There has to be a better way of searching these indexes without causing out of bounds? one that will allow me to keep track of all of the stones and their adjacent spaces?

Vectorize Conway's Game of Life in pure numpy?

I'm wondering if there is a way to implement Conway's game of life without resorting to for loops, if statements and other control structures typical of programming.
It should be pretty easy to vectorize for loops, but how would you convert the checks on the neighborhood to a matrix operation?
The base logic is something like this:
def neighbors(cell, distance=1):
"""Return the neighbors of cell."""
x, y = cell
r = xrange(0 - distance, 1 + distance)
return ((x + i, y + j) # new cell offset from center
for i in r for j in r # iterate over range in 2d
if not i == j == 0) # exclude the center cell
I hope this is not considered as off-topic by the mods, I'm genuinely curios and I am just starting out with CAs.
Cheers
The answer to your question is "yes, it is possible" (particularly the board updates from board n to board n+1).
I describe the process in detail here. The main technique to generate the neighborhood around a central cell involves using "strides" (the way that numpy and other array computation systems know how to walk across rows and columns of elements when they are really stored in memory in flat 1D thing) in a custom fashion to generate neighborhoods around cells. I describe that process here.
One last comment: since Game of Life iterates from state n to state n+1, while you could literally remove all imperative looping, it doesn't really make sense to take out that top-level control loop. So, has a loop: for round in range(num_rounds): board.update() where board.update doesn't use loops (except to do some side calculations ... again, you could remove those but it would make the program longer and less elegant).
To give you a concrete example (and be more compatible with StackOverflow's answer requirements), here's some select cutting and pasting from my posts to generate the central neighborhoods from a simple 4x4 board [apologies, this is python 2 code, you'll have to modify the prints a bit]:
board = np.arange(16).reshape((4,4))
print board
print board.shape
We want to pick out the four "complete" neighborhoods centered around 5, 6, 7, and 8. Let’s look at the neighborhood for 5. What is the shape of the result? 3×3. What are the strides? Well, to walk across a row is still just walking one element at a time and to get to the next row is still 4 elements at a time. These are the same as the strides in the original. The difference is we don’t take "everything", we just take a selection. Let’s see if that actually works:
from numpy.lib.stride_tricks import as_strided
neighbors = as_strided(board, shape=(3,3), strides=board.strides)
print neighbors
Ok, nice. Now, if we want all four neighborhoods, what is the output shape? We have several 3×3 results. How many? In this case, we have 2×2 of them (for each of the "center" cells). This gives a shape of (2,2,3,3) – the neighborhoods are the inner dimensions and the organization of the neighborhoods is the outer dimensions.
So, our strides (in terms of elements) end up being (4,0) within one neighborhood and (4,0) for progressing neighborhood to neighborhood. The total stride (element wise) is: (4,0,4,0). But, the component strides (our outer two dimensions) are the same as the strides of the board. This means that our neighborhood strides are board.strides + board.strides.
print board.strides + board.strides
neighborhoods = as_strided(board,
shape=(2,2,3,3),
strides=board.strides+board.strides)
print neighborhoods[0,0]
print neighborhoods[-1, -1]

Bejeweled Style Game Matches

I am working on designing a bejeweled style game with a twist. Rather than trying to match all the blocks of the same shape, you are trying to match a pair of letters. The board will always have roughly 10 letters on the board and you are trying to get them together. There are three different color blocks. Each color has 1 blank block and then 5 blocks with different letters. The goal is to get say a red "T" block next to a red "A" block. This is where I am running into problems.
Right now, the code will clear from the board three or more of the same color and letter. So, if I can get three red "T" blocks next to each other they will clear. The code basically scans each line looking for a group of three (or more). I have tried to modify the code so it will clear even just the same color (regardless of letter combo) but I can't even get that working. I think it has to do with how I load the images. I have posted most of the code (without any of my crazy modifications to try and get this working since it just broke it) that deals with loading the images and finding matches. Can anybody help me modify the code so it can at least find one correct match. When a red "T" block is next to a red "A" block and a blank red block, it should be classified as a match and be removed from the board. It doesn't need to be anything that complex as long as it works. Thanks for any help!
Find matches code:
def find_matches(self):
def lines():
for j in range(self.h):
yield range(j * self.w, (j + 1) * self.w)
for i in range(self.w):
yield range(i, self.size, self.w)
def key(i):
return self.board[i].image
def matches():
for line in lines():
for _, group in itertools.groupby(line, key):
match = list(group)
if len(match) >= MINIMUM_MATCH:
yield match
return list(matches())
Image loading:
shapes = 'red blue yellow'
rareshapes = 'reda redc redg redt redu bluea bluec blueg bluet blueu yellowa yellowc yellowg yellowt yellowu'
self.shapes = [pygame.image.load('images/{}.png'.format(shape))
for shape in shapes.split()]
self.rareshapes = [pygame.image.load('rareimages/{}.png'.format(shape))
for shape in rareshapes.split()]
EDIT:
Since this post, a lot has changed and been updated with the game. It has sound and background music, the groundwork for a new scoring system, a new timer system, and I can specify which rare shape I want to appear in a specific spot on the grid when the game first starts. However, I am still having issues with the core gameplay. I hate to drag up and older post, but I am at a loss as to how to get this to work. I have messed around with the code dealing with matches with no luck.
Basically, the rareshapes are assigned numbers. So, a red A is 0, a red T is 3, all the way up to a yellow T which is 14. For the gameplay to work the way we intend, the user will need to get a red A (0), a red T(3), and 2 blank red squares next to each other for it to be a valid match and clear from the board. This is the ONLY correct way you can clear squares from the board. Getting 4 blank squares of the same color next to each other DOES NOT remove them.
However, the code needs to detect if they get an invalid pair as well. For example, if they get a red A(0), a red C(1), and two blank red squares, it will disappear but count as a strike against them.
The goal of the game is to remove all the letter blocks before time runs out without getting three strikes. Since we can specify how many and where letter blocks appear now, it just comes down to the matching. Does anybody have any idea how to make this work?
I was reading the code you posted, and I said to myself, that looks pretty good — one-dimensional board representation, well-named variables, use of itertools.groupby — that's pretty much how I would have written it myself. And then I remembered that I had written it myself. Still, I'm glad to see someone's making use of my code!
(Having said that, I notice that you removed the docstring. Why did you do that? Put it back right away!)
Anyway, this match finder uses itertools.groupby to find runs of identical items in a sequence. Or rather, since a key function is given, runs of items where the key function returns identical results.
You can see that the key function as written just returns the image at the board location. This means that board locations match so long as they contain identical images. But you want to relax that: you want board locations to match so long as they contain images of the same colour.
So you need to revise the key function so that it returns the colour of the image at the board location. For example, when you loaded the images you could create a dictionary mapping images to their colour:
self.image_colour = {}
self.shapes = []
self.rareshapes = []
colours = 'red blue yellow'
letters = 'acgtu'
for c in colours.split():
im = pygame.image.load('images/{}.png'.format(c))
self.shapes.append(im)
self.image_colour[im] = c
for l in letters:
im = pygame.image.load('rareimages/{}{}.png'.format(c, l))
self.rareshapes.append(im)
self.image_colour[im] = c
and then in the key function you could look up the image to find its colour:
def key(i):
return self.image_colour.get(self.board[i].image)

select the bigger ball

I've got an array of data filled with "#" and "." for example it display something like this :
...........#############..............................
.........################..................#######....
........##################................#########...
.......####################..............###########..
........##################................#########...
.........################..................#######....
...........#############..............................
I want to create an algorithm that find the bigger ball and erase the smaller one.
I was thinking of using the longest sequence of "#" to know what is the diameter.
So i've got something like this :
x = 0
longest_line = 0
for i in range(0, nbLine) :
for j in range(0, nbRaw) :
if data[i, j] = red :
x = x+1
if data[i, j+1] != red:
And i don't know what to do next..
I would use some sort of a segmentation algorithm, and then simply count the number of pixels in each object. Then simply erase the smaller one, which should be easy as you have a tag of the object.
The segmentation algorithms typically work like this.
Perform a raster scan, starting upper left, working towards bottom right.
As you see a #, you know you have an object. Check it's neighbors.
If the neighbors have a previously assigned value, assign that value to it
If there there are multiple values, put that into some sort of a table, which after you are done processing, you will simplify.
So, for a very simple example:
...##...
.######.
...##...
Your processing will look like:
00011000
02111110
00011000
With a conversion such that:
2=>1
Apply the look up table, and all objects will be tagged with a 1 value. Then simply count the number of pixels, and you are done.
I'll leave the implementation to you;-)
Get your data into a nicer array structure
Perform connected component labelling
Count the number of elements with each label (ignoring the background label)
Choose the label with the largest number of elements
Do you olways have only 2 shapes like this? Because in this case, you could also use the python regular expression library. This code seems to do the trick for your example (I copied your little drawing in a file and named it "balls.txt"):
import re
f = open("balls.txt", "r")
for line in f :
balls = re.search("(\.+)(#+)(\.+)(#*)(\.+)", line)
dots1 = balls.groups()[0]
ball1 = balls.groups()[1]
dots2 = balls.groups()[2]
ball2 = balls.groups()[3]
dots3 = balls.groups()[4]
if len(ball1) < len(ball2):
ball1 = ball1.replace('#', '.')
else:
ball2 = ball2.replace('#', '.')
print "%s%s%s%s%s" % (dots1, ball1, dots2, ball2, dots3)
And this is what I get:
...........#############..............................
.........################.............................
........##################............................
.......####################...........................
........##################............................
.........################.............................
...........#############..............................
I hope this can give you some ideas for the resolution of your problem
Assuming the balls don't touch:
Assuming exactly two balls
Create two ball objects, (called balls 0 and 1) each owns a set of points. Points are x,y pairs.
scan each row from top to bottom, left to right within each line.
When you see the first #, assign it to ball 0 (add it’s x,y cords to the set owned by the ball 0 object).
When you see any subsequent #, add it to ball 0 if it is adjacent to any point already in ball 0; otherwise add it to ball 1. (If the new # is at x, y we just test (x+1,y) is in set, (x-1, y) is in set, (x, y+1) is in set, (x, y-1) is in set, and the diagonal neighbors)
When the scan is complete, the list sizes reveal the larger ball. You then have a list of the points to be erased in the other ball’s points set.

Categories

Resources