Related
The program should ask what its first coordinates are. Then movement is asked and its answer should be with
r,R,l,L,u,U,d,D.
Coordinate are input with a comma (x,y), and then again if 'u','d','l','r' are input then the x or y axis will increase or decrease.
'u' and 'd' for the y-axis; 'r' and 'l' for the x-axis.
def program_for_grid(x,y,where):
if where == 'r' or where == 'R':
# move x upp +
for r in range(len(where)):
r == 'r' or r == 'R'
x + 1
elif where == 'l' or where == 'L':
# move x down -
for l in range(len(where)):
l == 'l' or l == 'L'
x - 1
elif where == 'u' or where == 'U':
# move y up +
for u in range(len(where)):
u == 'u' or u == 'U'
y + 1
elif where == 'd' or where == 'D':
# move y down -
for d in range(len(where)):
d == 'd' or d == 'D'
y - 1
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(x_axis)
movement = input('Movement: ')
grid = program_for_grid(int_x_axix,int_y_axix,movement)
print(grid)
Something like this?
def delta(movement):
"""
Parse the string for movement instructions and
return resulting relative coordinates
"""
x = 0
y = 0
for letter in movement.lower():
if letter == "d": y -= 1
elif letter == "u": y += 1
elif letter == "l": x -= 1
elif letter == "r": x += 1
else:
print("warning: skipping undefined", letter)
return x, y
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(y_axis)
while True:
movement = input('Movement: ')
xdelta, ydelta = delta(movement)
int_x_axix += xdelta
int_y_axix += ydelta
print(f"resulting position: ({int_x_axix},{int_y_axix})")
I'm am beginning my journey into the world of python. I have done a couple of small projects, but the game of life really piqued my interest. Unfortunately, when I tried to replicate the game, everything worked except for the death of my cells. Per the rules, if a live cell has less than 2 neighbors, or more than 3, it should die. Cells are being born, but alas, they seem to be immortal. Can anyone spot my mistake? I'm including the entire code because I have no idea where I went wrong.
import random, time, copy
height = 10
width = 10
next = []
for x in range(width):
column = []
for y in range(height):
if random.randint(0, 1) == 0:
column.append(" ")
else:
column.append("#")
next.append(column)
while True:
print("\n\n\n\n")
current = copy.deepcopy(next)
for y in range(height):
for x in range(width):
print(current[x][y], end=" ")
print()
for x in range(width):
for y in range(height):
leftco = (x - 1) % width
rightco = (x + 1) % width
botco = (y - 1) % height
topco = (y + 1) % height
neighbors = 0
if current[leftco][topco] == "#":
neighbors = neighbors + 1
if current[leftco][y] == "#":
neighbors = neighbors + 1
if current[leftco][botco] == "#":
neighbors = neighbors + 1
if current[x][topco] == "#":
neighbors = neighbors + 1
if current[x][botco] == "#":
neighbors = neighbors + 1
if current[rightco][topco] == "#":
neighbors = neighbors + 1
if current[rightco][y] == "#":
neighbors = neighbors + 1
if current[rightco][botco] == "#":
neighbors = neighbors + 1
if current[x][y] == "#" and (neighbors == 2 or neighbors == 3):
next[x][y] = "#"
elif current[x][y] == " " and neighbors == 3:
next[x][y] == "#"
else:
next[x][y] == " "
time.sleep(1)
In this section, you confused == and = when you try to assign to a value of next[x][y]
if current[x][y] == "#" and (neighbors == 2 or neighbors == 3):
next[x][y] = "#"
elif current[x][y] == " " and neighbors == 3:
next[x][y] == "#"
else:
next[x][y] == " "
So it should be:
if current[x][y] == "#" and (neighbors == 2 or neighbors == 3):
next[x][y] = "#"
elif current[x][y] == " " and neighbors == 3:
next[x][y] = "#"
else:
next[x][y] = " "
By the way, you see next is highlighted as a special name. That's because next() is a special function in Python standard library, and you shouldn't name your own variables like that. It's harmless in your current program, because you don't use it, but develop a habit to use more specific names, that don't shadow the standard ones
I tried a math formula from analytic geometry but I didn't get the desired results.
As you can see in the code, the user draw two lines and a green circle(point of intersection) appear.
I have tried to fix the problem for hours but I failed. Sometimes the point doesn't appear or appear but in the wrong position.
Source of the formula I used in the code
Docs of pygame the library I used
#!/usr/bin/env python
from pygame import *
from time import sleep
init();
win = display.set_mode((500,500));
lines = []
cords = []
preview = False
xi = -100
yi = -100
def drawlines(flines):
for fcords in flines:
draw.line(win,(0,0,0),fcords[0],fcords[1],4)
while True:
win.fill((255,255,255))
for ev in event.get():
if ev.type == QUIT:
quit();
exit();
elif ev.type == MOUSEBUTTONDOWN:
if ev.button == 1:
preview = True
a = mouse.get_pos()
cords.append(mouse.get_pos())
elif ev.type == MOUSEBUTTONUP:
if ev.button == 1:
cords.append(mouse.get_pos())
lines.append((cords))
cords = []
preview = False
######################################## THIS BROKEN PART #################################
if len(lines) == 2:
#line 1
points_line1 = lines[0]
point1_line1 = points_line1[0]
point2_line1 = points_line1[1]
line1_vector = (point2_line1[0]-point1_line1[0],point2_line1[1]-point1_line1[1])
a_line1 = line1_vector[1]
b_line1 = -line1_vector[0]
c_line1 = -((a_line1*point1_line1[0]) + (b_line1*point1_line1[1]))
#line2
points_line2 = lines[1]
point1_line2 = points_line2[0]
point2_line2 = points_line2[1]
line2_vector = (point2_line2[0]-point1_line2[0],point2_line2[1]-point1_line2[1])
a_line2 = line2_vector[1]
b_line2 = -line2_vector[0]
c_line2 = -((a_line2*point1_line2[0]) + (b_line2*point1_line2[1]))
if (a_line2 != 0 and b_line2 != 0):
if (a_line1 / a_line2) != ( b_line1/b_line2):
#intersection between line1 and line2
yi = ((((a_line1*c_line2) / a_line2) + c_line1) / -b_line1)
xi = (((-b_line1*yi)-c_line1) / a_line1)
###########################################################################################
elif preview:
draw.line(win,(0,0,0),a,mouse.get_pos(),4)
drawlines(lines)
draw.circle(win,(0,200,0),(int(xi),int(yi)),10)
display.flip()
It's hard to dig through your code. Especially c_line1 = -((a_line1*point1_line1[0]) + (b_line1*point1_line1[1])) seems to be odd, because it calculates the Dot product of vector and a point.
Therefore I don't know exactly what's wrong with your code, but I know how to intersect lines. See Problem with calculating line intersections and Collision and Intersection - Line and line.
Write functions that can add, subtract, scale vectors and rotate vectors by 90°. Write a function which calculates the Dot product:
def add(a, b):
return a[0]+b[0], a[1]+b[1]
def sub(a, b):
return a[0]-b[0], a[1]-b[1]
def rot90(v):
return -v[1], v[0]
def mul(v, s):
return v[0]*s, v[1]*s
def dot(a, b):
return a[0]*b[0] + a[1]*b[1]
Calculate the vector from the beginning of the lines to the end of the lines and the normal vectors to the lines. The normal vector is obtained by rotating the line vector by 90°:
#line 1
point1_line1, point2_line1 = lines[0]
line1_vector = sub(point2_line1, point1_line1)
line1_norml = rot90(line1_vector)
#line2
point1_line2, point2_line2 = lines[1]
line2_vector = sub(point2_line2, point1_line2)
line2_norml = rot90(line2_vector)
Calculate the intersection of the line segments. Make sure the lines are not parallel and that the intersection point is on the line segments:
# vector from start point of line 2 to start point of line 1
l2p1_l1p1 = sub(point1_line1, point1_line2)
# intersection
d = dot(line2_vector, line1_norml)
if d != 0: # prallel lines
t = dot(l2p1_l1p1, line1_norml) / d
u = dot(l2p1_l1p1, line2_norml) / d
if 0 <= t <= 1 and 0 <= u <= 1: # intersection on line segments
xi, yi = add(point1_line2, mul(line2_vector, t))
Minimal example:
from pygame import *
init()
win = display.set_mode((500,500))
lines = []
cords = []
preview = False
xi, yi = -100, -100
def drawlines(flines):
for fcords in flines:
draw.line(win,(0,0,0),fcords[0],fcords[1],4)
def add(a, b):
return a[0]+b[0], a[1]+b[1]
def sub(a, b):
return a[0]-b[0], a[1]-b[1]
def rot90(v):
return -v[1], v[0]
def mul(v, s):
return v[0]*s, v[1]*s
def dot(a, b):
return a[0]*b[0] + a[1]*b[1]
run = True
while run:
for ev in event.get():
if ev.type == QUIT:
run = False
elif ev.type == MOUSEBUTTONDOWN:
if ev.button == 1:
preview = True
a = ev.pos
cords.append(a)
elif ev.type == MOUSEBUTTONUP:
if ev.button == 1:
cords.append(ev.pos)
lines.append((cords))
cords = []
preview = False
intersections = []
for i, line1 in enumerate(lines):
for line2 in lines[i+1:]:
#line 1
point1_line1, point2_line1 = line1
line1_vector = sub(point2_line1, point1_line1)
line1_norml = rot90(line1_vector)
#line2
point1_line2, point2_line2 = line2
line2_vector = sub(point2_line2, point1_line2)
line2_norml = rot90(line2_vector)
# vector from start point of line 2 to start point of line 1
l2p1_l1p1 = sub(point1_line1, point1_line2)
# intersection
d = dot(line2_vector, line1_norml)
if d != 0: # prallel lines
t = dot(l2p1_l1p1, line1_norml) / d
u = dot(l2p1_l1p1, line2_norml) / d
if 0 <= t <= 1 and 0 <= u <= 1: # intersection on line segments
xi, yi = add(point1_line2, mul(line2_vector, t))
intersections.append((xi, yi))
win.fill((255,255,255))
if len(cords) % 2 == 1:
draw.line(win,(0,0,0), a, mouse.get_pos(), 4)
drawlines(lines)
for p in intersections:
draw.circle(win, (0,200,0), (round(p[0]), round(p[1])), 10)
display.flip()
quit()
exit()
I have imported a text file with numbers as the following example:
3 0 0 0 0 1 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 2 3 3 3 0 3 0 0 0 0 3 3 3 3 0 3 0 0 0 0 3 3 3 3 0 3 2 2 0 0 3 3 3 3 3 3 3 3 2 0 3 3 3
The goal is to read in the text file, format it as a grid (i.e a 10 by 10 grid) which I am able to do, and then sort through the list of lists to reach the solution where the number 3 is an obstacle, number 1 is start point and number 2 is the solution, I am attempting to use a BFS algorithm where the agent can move UP, DOWN, LEFT, RIGHT.
I am trying to print the sequence of steps that is taken to reach the closest solution (i.e 2) from the beginning point(i.e 1). The numbers are formatted as strings/ text. The program I have written seems to be running but it never prints a solution or terminates. The move sequence that is to be printed as a solutions is in the format of:
'Move Down'
'Move UP'
ETC. where each move is on a newline
I am attaching my code below and any help that can be offered would be greatly appreciated
import queue
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
def read_to_grid():
file_name = read_user_input()
for nums in open(file_name):
line = list(nums.split())
result = []
for _ in range(0, len(line), 10):
result.append(line[_:_ + 10])
return result
file_name.close()
def print_grid(result, path=''):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
pos.add((j, i))
for j, row in enumerate(result):
for i, col in enumerate(row):
if (j, i) in pos:
print('#', end='')
else:
print(col + ' ', end='')
print()
def valid(result, moves):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
if not (0 <= i < len(result[0]) and 0 <= j < len(result)):
return False
elif (result[i][j] == '3'):
return False
return True
def find_goal(result, moves):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
if result[j][i] == '2':
print('Found: ' + moves)
print_grid(result, moves)
return True
return False
nums = queue.Queue()
nums.put('')
add = ''
result = read_to_grid()
while not find_goal(result, add):
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + j
if valid(result, put):
nums.put(put)
Ok Ryan answer already says everything, here however is your code working though is not efficient in anyway, the only things I changed that is worth is that instead of using a list of list you can just use a list, and the valid function now check the traveled path so that it can know where it has been so it won't loop.
import queue
# Read name file from user
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
# Read file and return list of list[10]
def read_to_grid():
with open(read_user_input()) as file:
for nums in file:
line = list(nums.split())
return line
# Shows a text grid
def print_grid(result, path=[]):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start
#j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
i -= 10
elif move == 'Move Down':
i += 10
pos.add(i)
for i, celd in enumerate(result):
if i % 10 == 0:
print()
if i in pos:
print('# ', end='')
else:
print(celd + ' ', end='')
# Validates coordinates and traveled path
def valid(result, moves):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start % 10
j = start // 10
# Where we start
travel = [(j,i)]
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
# Check if we have already been there
if (j, i) in travel:
return False
else:
travel += [(j,i)]
# Check coordinates
if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
return False
elif result[i+j*10] == '3':
return False
return True
# Return true if 2 is reached
def find_goal(result, moves):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start
#j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
i -= 10
elif move == 'Move Down':
i += 10
if result[i] == '2':
print('Found: ',' '.join(moves))
print_grid(result, moves[0:-1])
return True
return False
nums = queue.Queue()
result = read_to_grid()
add = []
while not find_goal(result, add):
if not nums.empty():
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + [j]
if valid(result, put):
nums.put(put)
EDIT:
I cleaned up a little:
import queue
# Read name file from user
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
# Read file and return list of list[10]
def read_to_grid():
with open(read_user_input()) as file:
for nums in file:
line = list(nums.split())
return line
# Shows a text grid
def print_grid(result, path=[]):
pos = set()
for (x,y), _ in path:
i = x + y*10
pos.add(i)
for i, celd in enumerate(result):
if i % 10 == 0:
print()
if i in pos:
print('# ', end='')
else:
print(celd + ' ', end='')
# Validates coordinates and traveled path
def valid(result, moves):
# Unpack
(i,j), _ = moves[-1]
# Check if already traveled
if any(x == i and y == j for (x,y), __ in moves[:-1]):
return False
# Check coordinates
if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
return False
elif result[i+j*10] == '3':
return False
return True
# Return true if 2 is reached
def find_goal(result, moves):
# Unpack
(i,j), _ = moves[-1]
if result[i+j*10] == '2':
#Print moves
output = 'Found: '
for (x,y), _ in moves:
output += " "+_
print(output)
#Print grid
print_grid(result, moves[1:-1])
return True
return False
# Return new position and which movement was done.
def move(pos, dir):
(x, y), _ = pos
if dir == 'Move Left':
x -= 1
elif dir == 'Move Right':
x += 1
elif dir == 'Move Up':
y -= 1
elif dir == 'Move Down':
y += 1
return (x, y), dir
nums = queue.Queue()
result = read_to_grid()
# Find the starting position
for x, pos in enumerate(result):
if pos == '1':
start = x
add = [((start % 10, start // 10),'')]
while not find_goal(result, add):
if not nums.empty():
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + [move(add[-1],j)]
if valid(result, put):
nums.put(put)
Whilst debugging your code I ran into some endless loops and other bugs when it came to your 'valid' and 'find_goal' functions.
In my experience with breadth first search its best to treat each point as a node (coordinates in this case) and to have your queue consist of lists of paths that are currently being tried. Where each path is a list of each node thats transversed. Typically you don't want to visit the same node more than once in a given path so you'll have to keep track of this information rather than soley 'left', 'right', etc...
All that said, I built-off your code and created a function that would return the valid adjacent nodes when given a node accounting for grid bound, not being a 3 and wether the node has been visited or not. Then for the BFS part the queue starts with a list containing the starting node (I made a function to find where the 1 was). Then while a queue exists the BFS will pop off the current path, get the last node in that path, find all valid adjacent nodes. With each valid adjacent node a new path entry will be added to the queue consisting of the old path + the adjacent node. If one of the adjacent nodes is a goal it will end the search and return the path. I've included the directional information in the path so that you can parse that out.
This should print off a path to the nearest 2 as such:
[((5, 0), ''), ((5, 1), 'Down'), ((6, 1), 'Right'), ((6, 2), 'Down'), ((7, 2), 'Right'), ((7, 3), 'Down'), ((7, 4), 'Down'), ((7, 5), 'Down')]
You'll see the ...sorted(path_queue, key=lambda... that line isn't needed but is a lazy way to prioritize the queue, always trying the shortest current path. If you remove it you'll see you still get a valid path but its much longer.
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
def read_to_grid():
file_name = read_user_input()
for nums in open(file_name):
line = list(nums.split())
result = []
for _ in range(0, len(line), 10):
result.append(line[_:_ + 10])
int_result = []
for i, row in enumerate(result):
int_result.append([])
for col in row:
int_result[i].append(int(col))
return int_result
def print_grid(result, path=''):
for x, pos in enumerate(result[0]):
if pos == 0:
start = x
i = start
j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
pos.add((j, i))
for j, row in enumerate(result):
for i, col in enumerate(row):
if (j, i) in pos:
print('#', end='')
else:
print(str(col) + ' ', end='')
print()
def find_start_node(grid):
for i, row in enumerate(grid):
if 1 in row:
return ((row.index(1), i), '')
return (None, None)
def valid_adj(cur_node, grid, visited):
x = cur_node[0][0]
y = cur_node[0][1]
adj = []
if ((y + 1) < 10) and (grid[y + 1][x] != 3) and not (any((x, y + 1) in node for node in visited)):
adj.append(((x, y + 1), 'Down'))
if ((x + 1) < 10) and (grid[y][x + 1] != 3) and not (any((x + 1, y) in node for node in visited)):
adj.append(((x + 1, y), 'Right'))
if ((y - 1) >= 0) and (grid[y - 1][x] != 3) and not (any((x, y - 1) in node for node in visited)):
adj.append(((x, y - 1), 'Up'))
if ((x - 1) >= 0) and (grid[y][x - 1] != 3) and not (any((x - 1, y) in node for node in visited)):
adj.append(((x - 1, y), "Left"))
return adj
def BFS(grid):
start_node = find_start_node(grid)
path_queue = [[start_node]]
while path_queue:
path_queue = sorted(path_queue, key=lambda x: len(x), reverse=True) # More optimized to guarantee shortest path, not needed
cur_path = path_queue.pop()
cur_node = cur_path[-1]
if cur_node not in cur_path[:].pop():
adj = valid_adj(cur_node, grid, cur_path)
for node in adj:
new_path = list(cur_path)
new_path.append(node)
path_queue.append(new_path)
if grid[node[0][1]][node[0][0]] == 2:
print('path found')
return new_path
return -1
grid = read_to_grid()
print_grid(grid)
print(BFS(grid))
I just currently finished making the game 'snake' as a practice to learn how to program, as I am new to programming for about 3 months.
Although the game is completed and runs the way I intended, I want to try to simplify my code and reduce the amount of lines as much as possible, and possibly make the script tidier as the current majority of my codes are cluster in the while loop.
Until now I haven't touched upon class objects, and I want everything in the while loop to go into individual classes that get called out from the while loop to reduce the amount of lines in it.
off-topic: by reading through the script, how else can I improve it to be run more efficiently, including simplifying some code as I may have over-complicated it?
I looked up how class object is used from w3school and other programming tutorials, but I still don't fully understand it as it only shows examples in using print. I did play around and experimented with class object examples and attempted to call them without using print, but I lack the knowledge of how to use them properly.
from graphics import *
from threading import Timer
import keyboard, random, time
# configurations
width = 400
gridHeight = width
height = 470
timer = False
game = True
score = 0
bonus = 0
x = 70
y = 30
radius = 10
length = radius * 2
playerLength = 3
poisonLength = playerLength
i = 0
k = 0
pointRadius = 5
points = False
cherryPoints = False
key = "Right"
countDown = 0
# set coordinations
cX = 90
cY = 30
coordX = [10]
coordY = [10]
while coordX[len(coordX)-1] != width-10:
cX+=20
coordX.append(cX)
while coordY[len(coordY)-1] != 390:
cY+=20
coordY.append(cY)
randomX = random.choice(coordX)
randomY = random.choice(coordY)
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
# window set up
win = GraphWin("SNAKE", width, height, autoflush = False)
win.setBackground(color_rgb(15,15,15))
# grid
lineX = 20
while lineX < width:
gridX = Line(Point(lineX,0),Point(lineX,gridHeight))
gridX.setOutline(color_rgb(25,25,25))
gridX.draw(win)
lineX += 20
lineY = 20
while lineY <= gridHeight:
gridX = Line(Point(0,lineY),Point(width,lineY))
gridX.setOutline(color_rgb(25,25,25))
gridX.draw(win)
lineY += 20
# snake banner
UI = Rectangle(Point(0,400),Point(width,height))
UI.setFill(color_rgb(102,51,0))
UI.setOutline(color_rgb(102,51,0))
UI.draw(win)
snakeTitle = Text(Point(width/2,420),"SNAKE")
snakeTitle.setTextColor("green")
snakeTitle.setSize(20)
snakeTitle.draw(win)
scoreTitle = Text(Point(320,424),"SCORE")
scoreTitle.setTextColor("white")
scoreTitle.setSize(10)
scoreTitle.draw(win)
scoreUI = Text(Point(320,435),score)
scoreUI.setTextColor("white")
scoreUI.setSize(10)
scoreUI.draw(win)
# make player
player = {}
player[0] = Rectangle(Point(x-20-radius,y-radius), Point(x-20+radius, y+radius))
player[1] = Rectangle(Point(x-40-radius,y-radius), Point(x-40+radius, y+radius))
player[2] = Rectangle(Point(x-60-radius,y-radius), Point(x-60+radius, y+radius))
# make poison
poison = {}
def main():
global timer, scoreUI, score, bonus, playerLength, poisonLength, x, y, points, cherryPoints, randomX, randomY, cherryRandomX, cherryRandomY, poisonRandomX, poisonRandomY, key, countDown, k, game
while(game==True):
# score update
scoreUI.undraw()
scoreUI = Text(Point(320,435),score)
scoreUI.setTextColor("white")
scoreUI.setSize(10)
scoreUI.draw(win)
# generating new body blocks
if len(player) < playerLength:
i+=1
player[i] = player[i-1].clone()
# body following player
player[0].undraw()
for i in range(1,len(player)):
player[len(player)-i].undraw()
player[len(player)-i] = player[len(player)-i-1].clone()
player[len(player)-i].draw(win)
# update player's head coordinate
player[0] = Rectangle(Point(x-radius,y-radius), Point(x+radius,y+radius))
player[0].setFill("green")
player[0].setWidth(2)
player[0].draw(win)
# player movement
if keyboard.is_pressed("Up") and key != "Down":
key = "Up"
elif keyboard.is_pressed("Left") and key != "Right":
key = "Left"
elif keyboard.is_pressed("Down") and key != "Up":
key = "Down"
elif keyboard.is_pressed("Right") and key != "Left":
key = "Right"
if key == "Up":
y -= length
elif key == "Left":
x -= length
elif key == "Down":
y += length
elif key == "Right":
x += length
# point
if points == False: # generates new point when eaten
point = Rectangle(Point(randomX-pointRadius,randomY-pointRadius),Point(randomX+pointRadius,randomY+pointRadius))
point.setFill("white")
point.setWidth(2)
point.draw(win)
points = True
if player[0].getCenter().getX() == point.getCenter().getX() and player[0].getCenter().getY() == point.getCenter().getY(): # when player eats the point
point.undraw()
playerLength += 1
poisonLength += 1
score += 200+bonus
randomX = random.choice(coordX)
randomY = random.choice(coordY)
for i in range(len(player)):
if (point.getCenter().getX() == player[i].getCenter().getX() and point.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoints == True and cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and cherry
randomX = random.choice(coordX)
randomY = random.choice(coordY)
for i in range(len(poison)): # regenerate x and y coordinate if point shares the same coordinate to other array of poisons
if point.getCenter().getX() == poison[i].getCenter().getX() and point.getCenter().getY() == poison[i].getCenter().getY():
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
points = False
# cherry
if countDown == 150:
countDown = 0
if cherryPoints == False: # generates new cherry from countdown
cherryPoint = Rectangle(Point(cherryRandomX-pointRadius,cherryRandomY-pointRadius),Point(cherryRandomX+pointRadius,cherryRandomY+pointRadius))
cherryPoint.setFill(color_rgb(213,0,50))
cherryPoint.setWidth(2)
cherryPoint.draw(win)
cherryPoints = True
if cherryPoints == True:
for i in range(2, 6): # cherry blinks between countdown 40 to 100
if countDown == 20*i:
cherryPoint.undraw()
elif countDown == 10+(20*i):
cherryPoint.draw(win)
if countDown >= 100: # when countdown becomes 100, remove cherry and reset count down
cherryPoints = False
countDown = 0
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
if cherryPoints==True and player[0].getCenter().getX() == cherryPoint.getCenter().getX() and player[0].getCenter().getY() == cherryPoint.getCenter().getY(): # when player eats the cherry
cherryPoint.undraw()
score += 500
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
for i in range(len(player)):
if (cherryPoint.getCenter().getX() == player[i].getCenter().getX() and cherryPoint.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
for i in range(len(poison)): # regenerate x and y coordinate if cherry shares the same coordinate to other array of poisons
if cherryPoint.getCenter().getX() == poison[i].getCenter().getX() and cherryPoint.getCenter().getY() == poison[i].getCenter().getY():
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
cherryPoints = False
# poison
if poisonLength % 5 == 0: # generates a poison block each time the player size reaches the multiple of 5
poison[k] = Rectangle(Point(poisonRandomX-pointRadius,poisonRandomY-pointRadius),Point(poisonRandomX+pointRadius,poisonRandomY+pointRadius))
poison[k].setFill("green")
poison[k].setWidth(2)
poison[k].draw(win)
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
for i in range(len(player)):
if (poison[k].getCenter().getX() == player[i].getCenter().getX() and poison[k].getCenter().getY() == player[i].getCenter().getY()) or (poison[k].getCenter().getX() == point.getCenter().getX() and poison[k].getCenter().getY() == point.getCenter().getY()) or (cherryPoints==True and poison[k].getCenter().getX() == cherryPoint.getCenter().getX() and poison[k].getCenter().getY() == cherryPoint.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point and cherry
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
for i in range(len(poison)):
if poison[k].getCenter().getX() == poison[i].getCenter().getX() and poison[k].getCenter().getY() == poison[i].getCenter().getY(): # regenerate x and y coordinate if new poison shares the same coordinate to other array of poisons
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
bonus+=50
k+=1
poisonLength+=1
# game over requirements
for i in range(len(poison)): # if player touches poison
if player[0].getCenter().getX() == poison[i].getCenter().getX() and player[0].getCenter().getY() == poison[i].getCenter().getY():
game = False
for i in range(2, len(player)): # if player touches its own body or reach out of window
if (player[0].getCenter().getX() == player[i].getCenter().getX() and player[0].getCenter().getY() == player[i].getCenter().getY()) or x < 0 or x > width or y < 0 or y > gridHeight:
game = False
# FPS
update(10)
countDown += 1
# GAME OVER
gameOver = Text(Point(width/2,200), "GAME OVER")
gameOver.setTextColor("red")
gameOver.setSize(30)
gameOver.draw(win)
update()
time.sleep(2)
win.close()
main()
Ideally the result should replace each code in the while loop with individual classes outside of the function to reduce the amount of lines in the main() function and make the script easier to read.
Classes are essentially just bundles of code that contain various attributes and methods.
A Snake class might have a list of coordinates for each section of the body (the first is the head).
class Snake:
def __init__(self, x, y):
self.positions = [(x, y)]
def get_head(self):
return self.positions[0]
def move_forward(self):
self.positions.pop()
self.positions.insert(0, self.get_head()[1] + 1)
def move_backward(self):
self.positions.pop()
self.positions.insert(0, self.get_head()[1] - 1)
...
And so on. Classes, at this level, let you think of objects as concrete entities, distinct from each other but easily manipulated.