"""
Generates a basic map for combat
"""
__Author__ = "RidiculousName"
__date__ = "3/21/18"
import pygame as pg
import copy
import random
def createMap(width, height, trees, rocks, roadWidth):
"""
creates a combat map
:param width: int; 20-60
width of map in squares
:param height: int; 20-60
height of map in squares
:param trees: int; 0 to (width*height)//5
# of trees in map
:param rocks: int; 0 to (width*height)//5
# of rocks in map
:param roadWidth: int; 0-10
width of road in map
(if value=0, will not have a road)
:return:
tuple matrix of map
"""
# If you indent your text a bit it's simpler to read:
"""
:param width: int; 20-60
width of map in squares
:param height: int; 20-60
height of map in squares
:param trees: int; 0 to (width*height)//5
# of trees in map
:param rocks: int; 0 to (width*height)//5
# of rocks in map
:param roadWidth: int; 0-10
width of road in map
(if value=0, will not have a road)
:return:
tuple matrix of map
"""
# If you also add empty lines, it's even better. I also made it full sentences.
"""
:param width: int; 20-60
Width of the map in squares.
:param height: int; 20-60
Height of the map in squares.
:param trees: int; 0 to 1/5th of the map size.
Number of trees in the map.
:param rocks: int; 0 to 1/5th of the map size.
Number of rocks in the map.
:param roadWidth: int; 0-10
Width of road in map, use 0 to skip making a road.
:return:
tuple matrix of map
"""
# variable declarations
mapMatrix = []
rowList = [0] * width
treeLocations = []
rockLocations = []
colIndex = random.randint(0, height)
rowIndex = random.randint(0, width)
#error checking
if trees > (width * height) // 3:
print("ERROR: TOO MANY TREES")
return 0
elif rocks > (width * height) // 3:
print("ERROR: TOO MANY ROCKS")
return 0
# So your documentation is lying, it's actually 1/3rd of the map. Introduce a
# constant for it, and use it both here and in the documentation, so it's
# always consistent.
TREE_FRACTION = 0.33
"""
...
:param trees: int; 0 upto TREE_FRACTION of the map.
Number of trees in the map.
...
"""
...
if trees > width * height * TREE_FRACTION:
...
# create a blank map full of grass
for i in range(height):
row = copy.copy(rowList)
mapMatrix.append(row)
# add trees
for i in range(trees):
while (rowIndex, colIndex) in treeLocations:
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
mapMatrix[rowIndex][colIndex] = 1
treeLocations.append((rowIndex, colIndex))
# After you planted a tree, the "while (rowIndex, colIndex) in treeLocations" will
# immediately hold for the next tree that you want to plant. The body of the
# while will then find a new coordinate, so nothing bad will happen. However,
# since you already know this will happen, why not avoid it completely?
for i in range(trees):
# Initialize the colIndex and rowIndex once before you test (rowIndex, colIndex).
#
# This makes the initialization higher up in the code also obsolete.
# Finally, by doing it in this way, it is easier to understand what
# code belongs together, and if you want to move some code to another
# place, it's more likely you'll get it right the first time.
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
while (rowIndex, colIndex) in treeLocations:
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
mapMatrix[rowIndex][colIndex] = 1
treeLocations.append((rowIndex, colIndex))
# A second problem is that treeLocations is a list, which is very slow to
# search if it has many entries. It is generally a good idea to avoid searching
# in a list. Searching for a dict key or in a set is much faster.
filled = set()
for i in range(trees):
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
while (rowIndex, colIndex) in filled: # Inspect the set
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
mapMatrix[rowIndex][colIndex] = 1
treeLocations.append((rowIndex, colIndex))
filled.add((rowIndex, colIndex)) # Add it to the set as well.
# Alternatively, why not use the mapMatrix for checking?
# I left treeLocations in now, but it could be removed entirely, as you don't
# seem to use it elsewhere.
for i in range(trees):
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
while mapMatrix[rowIndex][colIndex] == 1:
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
mapMatrix[rowIndex][colIndex] = 1
treeLocations.append((rowIndex, colIndex))
# add rocks
for i in range(rocks):
while (rowIndex, colIndex) in treeLocations \
or (rowIndex, colIndex) in rockLocations:
# Instead of having a separate rockLocations, you could use 'filled' again
# (just fill it further), or use mapMatrix again:
#
# while mapMatrix[rowIndex][colIndex\ in (1, 2):
#
colIndex = random.randint(0, height - 1)
rowIndex = random.randint(0, width - 1)
mapMatrix[rowIndex][colIndex] = 2
rockLocations.append((rowIndex, colIndex))
# add the road
if roadWidth > 0:
ct = int(roadWidth // 2)
road = int(height // 2)
# "//" already converts to integer, no need to do it a second time with "int(..)"
while ct > 0:
mapMatrix[road + ct] = [3] * width
mapMatrix[road - ct] = [3] * width
ct -= 1
mapMatrix[road] = [3] * width
# Instead of starting at the center, you can also start at an edge of the road:
road = height // 2 - roadWidth // 2
ct = 0
while ct < roadWidth:
mapMatrix[road + ct] = [3] * width
ct = ct + 1
# Not sure why you do this conversion to tuples below, you can keep the lists just as easy.
# convert to tuple
for i in range(height):
mapMatrix[i] = tuple(mapMatrix[i])
# return
return tuple(mapMatrix)
def showMap(screen, mapMatrix):
"""
:param screen: pygame screen object
images are blitted to this
:param mapMatrix: list matrix
contains the map matrix
:return: none
"""
# mapMatrix is actually a tuple of tuples, rather than a list.
# When you don't return a value, the ':return:' entry is normally left out.
# Note that not returning a value is different from "return None" even though
# Python gives the same value in both cases. The reason is that often None has
# a special value in these cases, which is then described in the comment, for
# example ":return: Number of birds found in the map, or None if there are no trees."
# variable declarations
height = pg.display.Info().current_h
width = pg.display.Info().current_w
x_pos = 0
y_pos = 0
grass = pg.image.load("grass.png").convert()
tree = pg.image.load("tree.png").convert()
rock = pg.image.load("rock.png").convert()
road = pg.image.load("road.png").convert()
for i in range(len(mapMatrix)):
for j in range(len(mapMatrix[i])):
if mapMatrix[i][j] == 0:
screen.blit(grass, [x_pos, y_pos])
elif mapMatrix[i][j] == 1:
screen.blit(tree, [x_pos, y_pos])
elif mapMatrix[i][j] == 2:
screen.blit(rock, [x_pos, y_pos])
elif mapMatrix[i][j] == 3:
screen.blit(road, [x_pos, y_pos])
x_pos += 16
y_pos += 16
x_pos = 0
# You use row and column elsewhere, and here you use i, j, x, and y. It's less
# confusing if you use the same name for the same thing everywhere.
#
# i and xpos are directly coupled, and j and ypos are directly coupled, you
# need only one set of coordinates here (for example, replace 'xpos' by 'i *
# 16', and 'ypos' by j * 16'. Alternatively, compute the second set from the
# first set (see below what I mean).
#
# Each "if" computes "mapMatrix[i][j]". You can also compute that once and
# store it.
for i in range(len(mapMatrix)):
x_pos = i * 16
for j in range(len(mapMatrix[i])):
y_pos = j * 16
mapVal = mapMatrix[i][j]
if mapVal == 0:
image = grass
elif mapVal == 1:
image = tree
elif mapVal == 2:
image = rock
elif mapVal == 3:
image = road
screen.blit(image, [x_pos, y_pos])
# One step further, the 'if' checks for a constant number, and then selects an image.
# You can do that in a dict as well:
inages = {0: grass,
1: tree,
2: rock,
3: road, }
for i in range(len(mapMatrix)):
x_pos = i * 16
for j in range(len(mapMatrix[i])):
y_pos = j * 16
mapVal = mapMatrix[i][j]
image = images[mapVal]
screen.blit(image, [x_pos, y_pos])
# And if you take out all the intermediates (not sure you want to do this):
inages = {0: grass, 1: tree, 2: rock, 3: road}
for i in range(len(mapMatrix)):
for j in range(len(mapMatrix[i])):
screen.blit(images[mapMatrix[i][j]],
[i * 16, j * 16])
def main():
"""
calls functions to allow the game to run
"""
# variable declarations
done = False
# initialize pygame
pg.init()
# make screen object
size = (1600, 900)
screen = pg.display.set_mode(size)
# set window caption
pg.display.set_caption("Bandit King")
# This comment just states what the code also says. Such comments is not
# useful. (You can assume the reader can read the code, or he/she wouldn't read
# your program.)
# In general, don't tell in comment what the code is doing (the code says that
# already). Instead, explain why, or explain the overall aim.
#manages FPS
clock = pg.time.Clock()
# This comment is useful, the code tells me you're reading the clock, but the
# code doesn't tell me why you are reading the clock. The comment does however.
#creates map
mapMatrix = createMap(30, 30, 140, 20, 2)
while not done:
# --- main event loop
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# --- game logic
# This comment states overall aim ("game logic is coming here"), so that's
# fine. The 3 comments below don't say much, you can just delete them, as the
# code says all already.
# --- drawing code
showMap(screen, mapMatrix)
# --- update screen
pg.display.flip()
# --- limit to 60 FPS
clock.tick(60)
# Try to avoid hard-coding magic numbers. Instead, make a constant for it
FPS = 60
clock = ...
...
while not done:
...
clock.tick(FPS)
# The number now has a name, which has meaning to the reader. Also, changing it
# to another value is much simpler now, as it's obvious which number to change.
#
# Look for other numbers that you have.
#print("height: ", pg.display.Info().current_h, "width: ", pg.display.Info().current_w)
pg.quit()
if __name__ == "__main__":
main()
Just inserted comments and code in your program, while keeping your code nearby for comparison.
Hope it is useful for you.