This question already has answers here:
What would be the output of range(x, y) if x > y?
(3 answers)
Closed last year.
I'm trying to load images in PyGame based on a value, like when the value is 6 it sets the image to be image number 6.
def bar():
global ink
global screen
global barImg
ink = 0
for ink in range(0,100):
barImg = pygame.image.load(f'inkbar\load{ink}.png')
screen.blit(barImg,(100,100))
pygame.display.update()
The value of ink gets changed in another function and I know that part works. Each image is called load0.png, load1.png and so on until 100, but the image never appears on the screen. I have tested putting the image on the screen by commenting out the for loop and just setting barImg to a specific image and it did put the image on the screen.
px, py = pygame.mouse.get_pos()
if pygame.mouse.get_pressed() == (1,0,0):
pygame.draw.rect(screen, (255,255,255), (px,py,10,10))
ink+=0.5
math.ceil(ink)
print(ink)
this is part of a function that allows the user to draw. This part detects mouse click and increases the value of ink. I tried calling bar() underneath the ink increase, but that decreased the rate of drawing.
I have removed the function bar()
ink+=1
math.ceil(ink)
print(ink)
for ink in range(1,100):
barImg = pygame.image.load(f'inkbar\load{ink}.png')
screen.blit(barImg,(100,100))
This is what I have used as a replacement, but now ink does not increase by one, it goes from 1 to 100 immediately, and causes large amounts of lag.
Maybe the images having "load" in the name is messing with something?
I have some code for running through frames of an animation which I know works
The code:
frame_index += animation_speed
if frame_index >= len(animation):
self.frame_index = 0
image = pygame.image.load(f'Ink ({int(frame_index)}).png')
screen.blit(image, (0,0))
pygame.display.update()
Essentially you want to have two variables, a frame index and an animation speed. The index is the number of the first image you are loading. In this case Ink 0.png or whatever it's called. So your frame index will be 0. This will increment by your animation speed variable. The higher this is, the faster your animation will be. The lower, the slower. After it loops it will go back to 0 if thats what you want. If you don't want that then you can simply remove the if statement.
Also check that you aren't filling the screen AFTER doing this as whatever you're wanting to see will just be covered instantly. Let me know if this works.
I have been trying to figure out a way to check each adjacent cell for my minesweeper game and am coming up short. I am a beginner in python and would also like to start using OOP. however before I can even get there, I need to rectify this. all the tutorials I have seen don't use basic python, but different versions to the IDLE i use, so I am struggling. can anyone help me? I need to be able to go around each adjacent cell and check if there is a bomb there. The value to check if there is a bomb there is 1 and will also turn red. Thank you all so much!
also if you could dumb it down a little for me, that would be lovely.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
First up, you definitely want to make an OOP project out of this. Minesweeper's probably near the complexity limit of what you can reasonably do without object-oriented programming, but if you want to take the basic Minesweeper concept and make it more interesting / complex, you're going to need better structure.
And even if you're not considering making it more complex, thinking about what sorts of complexities you could add in is helpful in planning your classes. (Perhaps you didn't realize there's a "planning your classes" step? I'll get to that.) My steps here should work for pretty much any beginning OOP project, since it seems Minesweeper's just a convenient example.
I realize this answer will be a giant diversion from OP's how-do-I-check-the-nearest-neighbors question, but OP was also asking about OOP and answering their question in an OOP context means getting a class model in place. Trying to retrofit OOP onto non-OOP code is possible, but is usually harder than doing OOP from scratch.
If you're new to OOP in Python, check these two tutorials. Both of them run through the basics, neither requires an IDE, and neither introduces complexity / features beyond what you need to start. The rest of my answer will assume you're familiar with how to write classes. I highly recommend typing out both tutorials on your own terminal and trying a couple variants on them before you go further.
Now that you know roughly what a class is, make a list of all the operations you're going to want in your Minesweeper program, and what sorts of things come up in it. The point here isn't to write code, and natural language will serve as pseudocode here. Things you'll probably want to include: "Add a mine to a cell on the grid," "check a cell on the grid," "display the number of spots near a given cell on the grid." You might also include some "future plans" like "resize the grid in the middle of a game", "limit number of moves," "get hint," "allow mines to move," "have people that move through minefield," multiple types of checks / mines / cells, hex grid as an alternative to a square grid, strangely-shaped and 3D grids, etc. Don't worry if you don't know how to code those things yet. If you've got an interesting idea, include it on the list.
Go through that list and make some notes about the main words / concepts that keep coming up. These will be your classes. For Minesweeper, I'd list "Cell," "Grid," "Game," "Check," and "Mine," but your mileage may vary. These are not necessarily the final class list. They're just a step toward organizing your thoughts so you can turn the high-level "Minesweeper game" into something concrete & extensible. You can add / remove classes later once you get a better feel for which ones you'll actually use.
Note the relationships between objects of the classes, including which ones need to be described for the others to make sense / be instantiated. This step is analogous to how you might plan a relational database: A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern, a Cell may or may not contain a single Mine, a Check reveals whether a Mine is in a given Cell and if not, reveals the number of Mines in Cells adjacent to the Checked Cell, etc.
Open up your IDE, and start blocking out classes in a file. The point here isn't so much to write code as to get all your classes and notes into the code for easy reference. If this file starts to get big, think about how the classes group together and split that one file into several, with import statements connecting them. By "blocking out classes," I mean "writing the simplest classes which will compile, but don't try to make them run." At this stage, you can / should have classes that look like this:
class Grid:
"""A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern"""
def __init__(self, game, **setup_params):
"""Initialize the grid given a game and possibly other parameters for shape and size"""
raise NotImplementedError('This code has not yet been written.')
def resize_grid(self, new_size):
raise NotImplementedError('This code has not yet been written.')
def check_cell_at(self, position):
raise NotImplementedError('This code has not yet been written.')
Things to check: This is completely legal Python and compiles fine. All of your notes from steps 2-4 should end up in docstrings. All of the target functionality you described in your notes corresponds to particular methods on classes that have something to do with those functions. Every class you've described is present, and has a docstring describing its purpose & structure. Every class's __init__() method takes the arguments that are necessary to instantiate the class. Every method takes some parameters that will likely be helpful. You can always edit the parameter lists later, but again, the point is to organize your code before you get too far into writing it, so that relationships and functionality are easy to track. If you're using Git or another version-tracking tool, make your first commit as soon as you're done with this step.
Now that you've got all the structure blocked out, figure out what the "main entry point" is going to be, instantiate that class (probably Game in my approach), and check that your code compiles, runs, and exits with a NotImplementedError coming with the line and message you expect. (If so, that's success!) If the project feels big, or if you're going to be sharing code with other developers, you may also want to add unit tests at this stage. The expectation is that every test will fail, but writing the tests helps to document the planned functionality.
Fill in the methods one by one, add unit tests to go with new or updated methods, and try running the code a bit at a time. You don't have to write the methods in any particular order, but it will be easier to test once all the __init__() methods are complete, and those are generally straightforward.
Now, how do you check the neighbors of a cell?
You should have methods on a Cell for "get list of my neighbors," and "get whether this cell contains a mine." You probably also have a method on Grid to "get Cell at position." You might have multiple types of Checks, but the base Minesweeper game only has the one, so Cell has a method like
def check(self):
"""Returns "BOMB" if this cell has a Bomb. Otherwise, returns the number of neighbors with bombs as an integer."""
if self.has_bomb:
return "BOMB"
neighboring_mines = 0
for cell in self.grid.get_neighbors_of(self.position):
if cell.has_bomb:
neighboring_mines += 1
return neighboring_mines
There are only 8([1,1],[1,0],[1,-1],[0,1],[0,-1],[-1,1],[-1,0],[-1,-1] relative to the clicked grid coordinate) adjacent squares, so it should be easy to do what you are asking only with "if" statements
anyway, just check the surrounding squares and if true add to a variable.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
#print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
print(grid)
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
NBombs = 0
for i in range(-1,2):
for k in range(-1,2):
if (i!=0 or k!=0) and (row+i>=0) and (column+k>=0) and (row+i<len(grid)) and (column+k<len(grid[0])):#prevents from both being 0, or for the index to be negative or out of range
print(i,k)
if grid[row+i][column+k] == 1:
NBombs+=1
print("Number of bombs:",NBombs)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
So basically like another answer in this thread, you can check the surrounding/adjacent tiles by adding -1, 0, or 1 to both your current x and y.
You could write a separate function that would return a list of the coordinates of the surrounding tiles with bombs and take the tile coordinates and grid as arguments like the following:
def check_surrounding_tiles(current_x, current_y, grid):
bomb_tiles = []
#Check starting from position (current_x - 1, current_y - 1)
for i in range(-1, 2):
for j in range(-1, 2):
#Ignore the current x and y, and do bounds checking
if (i != 0 or j != 0) and (current_x + i) > -1 and (current_y + j) > -1 and (current_x + i) < len(grid) and (current_y + j) < len(grid[0]):
if grid[current_x + i][current_y + j] == 1
bomb_tiles.append[(current_x + i, current_y + j)]
return bomb_tiles
This, like mentioned above, will return a list of coordinate pairs with bombs in them.
If you need to get the number of bombs, simply use the following:
adjacent_bomb_count = len(check_surrounding_tiles(<x position>,<y position>, grid))
Below is a snippet from a game I am currently making for A-Level coursework. I am making an intro cutscene in which I wish to make text be revealed one letter at a time (scroll, pokemon style). However my current solution requires the use of a for loop per line of text. This is fine for visual effect however it prevents the user from being able to interact with the window. I would like to add a skip button but cannot do so sue to this problem. I tried using more if statements but the code became messy, buggy, and a lot less efficient. Is there an easier, more efficient fix?
screen.blit(introImage4,(0,16))
if flags["IntroStage3"] == True:
for i in range(len(introText[0])):
introTextImage1 = myFont.render(introText[0][i], True, white)
screen.blit(introTextImage1,(50 + (myFont.size(introText[0][:i])[0]), 50))
pygame.display.update()
clock.tick(textFPS)
for i in range(len(introText[1])):
introTextImage2 = myFont.render(introText[1][i], True, white)
screen.blit(introTextImage2,(50 + (myFont.size(introText[1][:i])[0]), 100))
pygame.display.update()
clock.tick(textFPS)
for i in range(len(introText[2])):
introTextImage3 = myFont.render(introText[2][i], True, white)
screen.blit(introTextImage3,(50 + (myFont.size(introText[2][:i])[0]), 150))
pygame.display.update()
clock.tick(textFPS)
flags["IntroStage4"] = True
flags["IntroStage3"] = False
if flags["IntroStage4"] == True:
introTextImage1 = myFont.render(introText[0], True, white)
introTextImage2 = myFont.render(introText[1], True, white)
introTextImage3 = myFont.render(introText[2], True, white)
screen.blit(introTextImage1,(50, 50))
screen.blit(introTextImage2,(50, 100))
screen.blit(introTextImage3,(50, 150))
flags["IntroStage5"] = True
the issue here is that the event handler can't run a new check until your for loop is done.
the solution is to write an animation function for your text. you can do this by adding a variable which contains the text shown on screen, you can then change the value of this variable to another part of the complete text you want to have scroll based on some time dependent value.
this time dependent value can be the time that has passed since the event that triggered the scrolling text.
just to make it a bit more clear here's an example:
say i want the complete text to be "Alice has a big basket of fruit" but i can only fit one word in my scrolling text box and i want to show it for two seconds:
text = "Alice has a big basket of fruit"
def animate_text(text,t): #here t is current unix time minus the unix time whenthat the event that triggered the text scrolling
text_batches=text.split(' ')
return text_batches[t//2] if t//2 <= len(text_batches) else return False
so now we've split the text into batches instead of nesting a loop in your main loop you can blit the batch corresponding to the time that has passed since the animation started
while 1!=0:
# game loopy stuff here
blit(animate_text(text,time.time()-time_when_animation_started))
now that's all a little messy and pseudocodey and it doesn't perfectly handle your particular situation but you should get the idea here
To create an abstract, scrolling city skyline for a prototype, I created a class that generates random rectangles. These rects are added to a list and items are pulled from that list to be drawn on the screen. The rects begin off screen to the right and scroll across to the left until they leave the view plane and are trashed. The movement of the buildings is oddly jerky and they also shift to the right a few pixels at a specific point on the screen.
This video of the prototype is fairly accurate with very little video capture lag. Pay attention to the gaps between the buildings, as they get within the right most 3rd of the display area, the gap will suddenly shrink as if the building to the left of the gap is suddenly shifting right a few pixels. The smaller the gap, the more noticeable it is. The other anomalies in the video are from the recorder and are not present in the app. Here's a very short video that clearly shows this phenomenon: https://www.youtube.com/watch?v=0cdhrezjcY8
At about 1 second in you'll notice a very narrow gap between buildings in the rear layer. At :04 seconds in the gap is even with the blue player object, the left rectangle shifts and the gap vanishes. There's a second, larger gap to the right of that one that does the same thing but since the gap is larger it doesn't completely vanish. I've looked over the code numerous times but can't see what could be causing this anomaly. I'm hoping someone can tell me if it's something I did or a limitation I'm encountering.
I originally wrote this program linearly, without classes or functions. I rewrote it using a class that generates layer objects and handles all the generating and scrolling. In both cases the problem exists. It's driving me crazy trying to figure out why the buildings do not move smoothly. I've even written a version of this using png images instead of randomly generated rectangles. In that version the pngs scroll smoothly and seamlessly: https://www.youtube.com/watch?v=Uiw_giAvbOo (The video is a bit jerky, but the actual program plays smooth) So the issue is limited to these random rectangles.
Here's the code for the program: https://www.refheap.com/73079
Here's the class code by itself:
class Scroller():
def __init__(self, speed, color, heightMax):
# Speed of the layer scroll, the color of the layer and the maximum height for buildings
# set up the building parameters
self.buildingHeightMax = heightMax
self.buildingHeightMin = 100
self.buildingWidthMax = 125
self.buildingWidthMin = 75
self.buildings = []
self.layerspeed = speed
self.buildTime = True
self.buildCountdown = 10
self.color = color
def update(self):
# Check if it's time to build. If not, decrement counter
if self.buildTime == False:
self.buildCountdown -= 1
# If time is 0, time to build, reset counter to a new random time
if self.buildCountdown <= 0:
self.buildTime = True
self.buildCountdown = random.randint(3, self.layerspeed)
# create building if it's time
if self.buildTime:
# generate random width and height of building
buildingHeight = random.randint(self.buildingHeightMin, self.buildingHeightMax)
buildingWidth = random.randint(self.buildingWidthMin, self.buildingWidthMax)
buildingTop = WINDOWHEIGHT - buildingHeight
# This generates the building object from the above parameters
building = pygame.Rect(WINDOWWIDTH, buildingTop, buildingWidth, WINDOWHEIGHT)
self.buildTime = False
self.buildCountdown = random.randint(3, self.layerspeed * 5)
# add building to buildings list
self.buildings.append(building)
# move all buildings on layer at set speed
for building in self.buildings:
# if the building is off the screen, trash it. If not, move it to the
# right at the objects speed.
if building.right < 0:
self.buildings.remove(building)
else:
building.left -= self.layerspeed
# draw the Front buildings
for i in range(len(self.buildings)):
pygame.draw.rect(windowSurface, self.color, self.buildings[i])
Your problem most likely lies in:
# move all buildings on layer at set speed
for building in self.buildings:
# if the building is off the screen, trash it. If not, move it to the
# right at the objects speed.
if building.right < 0:
self.buildings.remove(building)
else:
building.left -= self.layerspeed
You're using remove on the same list you're iterating from, and this will make it skip the next building. So it's not the building to the right that's moving faster, it's the one to the left that has skipped moving.
You can see it yourself with this simple example:
a = [2, 3, 4, 1.5, 6, 8, 3.2]
for element in a:
if element == 4:
a.remove(element)
else:
print element
Try it and you'll see that not only 4 won't be printed, but also 1.5 will be skipped.
Possibly a good way to do it is to first iterate through all the buildings to see which ones need to be removed, then remove then all, and finally move all the ones that are left.
You might want to check this link for some good suggestions.
You're also updating the countdown twice, first on line 47 and then on line 58. Is there any reason for this?
So I'm recreating a part of pokemon yellow (trying to make it as close to the original as possible) And for 2 days now I'm searching a smart and efficient way to render and display a string one character at a time in the textbox, just like the pokemon games!(By the way I'm using pygame and python). Does anyone know any way of achieving this? I've tried many things but when rendering one character at a time, there is always inadequate space between them.
Sorry for the long question!
Cheers,
Alex
(edit) Thanks for your interest guys!!!
I'm not sure if I know the correct way to display my code here, If I should just
copy paste it in here or upload in dropbox or somewhere else..
(edit2) Just to clarify, i use the font at size 28, so the way I'm trying to render the characters right now, is to make a list where every element has the format (character_to_render,x_pos_to_render,y_pos_to_render). The next character would be (character2_to_render,x_pos_to_render + 28,y_pos_to_render). But approaching the problem this way, leaves inadequate space between some characters and some others are just fine.
(Edit 3) : Thanks for all your answers guys ! After closely observing the emulator, I noticed that the inadequate spacing between rendered characters is apparent there as well! So I'll just ignore this issue Andover on with my project !! Cheers and have a nice day!
Ok so here is the best solution that I have come up with so far.
You want to be able to display a string, but you only want to do it one character at a time. With strings you can do something like string[0:len(string)] which will return the whole string. So what I am thinking and please correct me if I'm wrong, but say you lower the FPS for a couple seconds, or If you do not want to do this because you still want to accept user input to skip the text.
So you have your while loop, and you check for if text is being displayed. If it is, you want to add a new letter to the string that you are displaying to the screen. I would recommend using a class for the text displayed on the screen.
surf = pygame.Surface(80, 80)
Text = TextBoxString("Hello World")
font = pygame.font.SysFont("Arial", 18)
while true:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
elif event.type == MOUSEBUTTONUP:
Text.showAll()
surf.fill((0,0,0))
text = font.render(Text.currentString, (0,0,0))
surf.blit(text, (0,0))
Text.addOn()
class TextBoxString:
def __init__(self, string):
#string that you will be dealing with
self.totalString = string
self.currentString = string[0]
#how many characters you want shown to the screen
self.length = 0
#this means that every four times through yourÂ
#while loop a new char is displayed
self.speed = 4
def addOn(self) #adds one to the loop num and then checks if the loop num equals the speed
self.loopNum += 1
if self.loopNum == self.speed:
self.length += 1
self.loopNum=0
self.currentString = totalString[0: self.length]
def showAll(self):
self.length = len(self.totalString)
self.currentString = [0: self.length]