So in this simulation I have bears, fish, and plants doing various things on a grid. Once the simulation starts I have variables from the simulation function update the number of bears and fish in the world class constructor under fishCount and bearCount. I have another method in the World class that turtle.writes the amount of bears and turtles as a counter. Two problems. The initial count number for each creature is wrong on startup and when the counts are updated via breeding the showCounts method keeps writing over the previous count, making it hard to read. I want it to update the counts on the screen each time a new bear/fish is made or when one dies.
Here is my code:
from math import *
from turtle import *
from random import *
import turtle
import random
import math
def mainSimulation():
numberOfBears = 5
numberOfFish = 5
numberOfPlants = 5
worldLifeTime = 2500
worldWidth = 30
worldHeight = 25
myworld = World(worldWidth,worldHeight,numberOfBears,numberOfFish)
myworld.draw()
myworld.getBearStarvePass()
myworld.getBearBreedPass()
myworld.setBearStarvePass(25)
myworld.setBreedPass(3)
myworld.getFishOvercrowdNum()
myworld.getFishBreedPass()
myworld.setFishOvercrowdNum(5)
myworld.setFishBreedPass(2)
for i in range(numberOfFish):
newfish = Fish(myworld)
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
while not myworld.emptyLocation(x,y):
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
myworld.addThing(newfish,x,y)
for i in range(numberOfBears):
newbear = Bear(myworld)
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
while not myworld.emptyLocation(x,y):
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
myworld.addThing(newbear,x,y)
for i in range(numberOfPlants):
newplant = Plant()
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
while not myworld.emptyLocation(x,y):
x = random.randrange(myworld.getMaxX())
y = random.randrange(myworld.getMaxY())
myworld.addThing(newplant,x,y)
for i in range(worldLifeTime):
myworld.liveALittle()
myworld.showCounts()
myworld.freezeWorld()
THIS IS THE WORLD CLASS
class World:
def __init__(self,mx,my,bear,fish):
self.maxX = mx
self.maxY = my
self.thingList = []
self.grid = []
self.bearCount = bear
self.fishCount = fish
self.fishOvercrowdNum = 0
self.fishBreedNum =0
self.bearStarvePass = 0
self.bearBreedPass = 0
for arow in range(self.maxY):
row=[]
for acol in range (self.maxX):
row.append(None)
self.grid.append(row)
self.wturtle = turtle.Turtle()
self.wscreen = turtle.Screen()
self.wscreen.setworldcoordinates(0,0,self.maxX-1,self.maxY-1)
self.wscreen.addshape("E:/Python/Lib/idlelib/Bear.gif")
self.wscreen.addshape("E:/Python/Lib/idlelib/Fish.gif")
self.wscreen.addshape("E:/Python/Lib/idlelib/Plant.gif")
self.wturtle.hideturtle()
def getBearStarvePass(self):
return self.bearStarvePass
def getBearBreedPass(self):
return self.bearBreedPass
def setBearStarvePass(self, newNum):
self.bearStarvePass = newNum
def setBreedPass(self, newNum):
self.bearBreedPass = newNum
def getFishOvercrowdNum(self):
return self.fishOvercrowdNum
def getFishBreedPass(self):
return self.fishBreedNum
def setFishOvercrowdNum(self, newNum):
self.fishOvercrowdNum = newNum
def setFishBreedPass(self, newNum):
self.fishBreedNum = newNum
def showCounts(self):
bearCount = self.bearCount
fishCount = self.fishCount
self.wturtle.write("Bear: %d Fish: %d " % (bearCount, fishCount), move = False)
def getNumBears(self):
return self.bearCount
def getNumFish(self):
return self.fishCount
def incBear(self):
self.bearCount = self.bearCount + 1
def incFish(self):
self.fishCount = self.fishCount + 1
def decBear(self):
self.bearCount = self.bearCount - 1
def decFish(self):
self.fishCount = self.fishCount - 1
def draw(self):
self.wscreen.tracer(0)
self.wturtle.forward(self.maxX-1)
self.wturtle.left(90)
self.wturtle.forward(self.maxY-1)
self.wturtle.left(90)
self.wturtle.forward(self.maxX-1)
self.wturtle.left(90)
self.wturtle.forward(self.maxY-1)
self.wturtle.left(90)
for i in range(self.maxY-1):
self.wturtle.forward(self.maxX-1)
self.wturtle.backward(self.maxX-1)
self.wturtle.left(90)
self.wturtle.forward(1)
self.wturtle.right(90)
self.wturtle.forward(1)
self.wturtle.right(90)
for i in range(self.maxX-2):
self.wturtle.forward(self.maxY-1)
self.wturtle.backward(self.maxY-1)
self.wturtle.left(90)
self.wturtle.forward(1)
self.wturtle.right(90)
self.wscreen.tracer(1)
def freezeWorld(self):
self.wscreen.exitonclick()
def addThing(self,athing,x,y):
a = 0
athing.setX(x)
athing.setY(y)
self.grid[y][x] = athing
athing.setWorld(self)
self.thingList.append(athing)
athing.appear()
if isinstance(athing, Bear):
self.bearCount = self.bearCount + 1
elif isinstance(athing, Fish):
self.fishCount = self.fishCount + 1
def delThing (self, athing):
athing.hide()
self.grid[athing.getY()][athing.getX()] = None
self.thingList.remove(athing)
def moveThing(self,oldx,oldy,newx,newy):
self.grid[newy][newx] = self.grid[oldy][oldx]
self.grid[oldy][oldx] = None
def getMaxX(self):
return self.maxX
def getMaxY(self):
return self.maxY
def liveALittle(self):
if self.thingList != [ ]:
athing = random.randrange(len(self.thingList))
randomthing = self.thingList[athing]
randomthing.liveALittle()
def emptyLocation(self,x,y):
if self.grid[y][x] == None:
return True
else:
return False
def lookAtLocation(self,x,y):
return self.grid[y][x]
This is the Bear Class
class Bear:
def __init__(self, theWorld):
self.turtle=turtle.Turtle()
self.turtle.up()
self.turtle.hideturtle()
self.turtle.shape("E:/Python/Lib/idlelib/Bear.gif")
self.offsetList = [(-1,1) ,(0,1) ,(1,1),
(-1,0) ,(1,0),
(-1,-1),(0,-1),(1,-1)]
self.theWorld = theWorld
self.bearStarvePass = self.theWorld.bearStarvePass
self.bearBreedPass = self.theWorld.bearBreedPass
self.xpos=0
self.ypos=0
self.world=None
self.starveTick=0
self.breedTick=0
def setX(self, newx):
self.xpos = newx
def setY(self, newy):
self.ypos = newy
def getX(self):
return self.xpos
def getY(self):
return self.ypos
def setWorld(self, aworld):
self.world = aworld
def appear(self):
self.turtle.goto(self.xpos, self.ypos)
self.turtle.showturtle()
def hide(self):
self.turtle.hideturtle()
def move(self, newx, newy):
self.world.moveThing(self.xpos, self.ypos, newx, newy)
self.xpos = newx
self.ypos = newy
self.turtle.goto(self.xpos, self.ypos)
def liveALittle(self):
self.breedTick = self.breedTick + 1
if self.breedTick >= self.bearBreedPass:
self.tryToBreed()
self.tryToEat()
if self.starveTick == self.bearStarvePass:
self.world.delThing(self)
else:
self.tryToMove()
def tryToMove(self):
randomOffsetIndex = random.randrange(len(self.offsetList))
randomOffset = self.offsetList[randomOffsetIndex]
nextx=self.xpos + randomOffset[0]
nexty=self.ypos + randomOffset[1]
while not(0 <= nextx < self.world.getMaxX() and
0 <= nexty < self.world.getMaxY() ):
randomOffsetIndex = random.randrange(len(self.offsetList))
randomOffset = self.offsetList[randomOffsetIndex]
nextx=self.xpos + randomOffset[0]
nexty=self.ypos + randomOffset[1]
if self.world.emptyLocation(nextx,nexty):
self.move(nextx,nexty)
def tryToBreed(self):
randomOffsetIndex = random.randrange(len(self.offsetList))
randomOffset = self.offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
while not(0 <= nextx < self.world.getMaxX() and 0 <= nexty < self.world.getMaxY()):
randomOffsetIndex = random.randrange(len(self.offsetList))
randomOffset = self.offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
if self.world.emptyLocation(nextx, nexty):
childThing = Bear(self.theWorld)
self.world.addThing(childThing, nextx, nexty)
self.breedTick = 0
def tryToEat(self):
adjprey = []
for offset in self.offsetList:
newx = self.xpos + offset[0]
newy = self.ypos + offset[1]
if 0 <= newx < self.world.getMaxX() and 0 <= newy < self.world.getMaxY():
if (not self.world.emptyLocation(newx,newy)) and isinstance(self.world.lookAtLocation(newx,newy),Fish):
adjprey.append(self.world.lookAtLocation(newx,newy))
if len(adjprey)>0:
randomprey = adjprey[random.randrange(len(adjprey))]
preyx = randomprey.getX()
preyy = randomprey.getY()
self.world.delThing(randomprey)
self.move(preyx,preyy)
self.starveTrick = 0
else:
self.starveTick = self.starveTick + 1
This is the CLass Fish
class Fish:
def __init__(self, theworld):
self.turtle = turtle.Turtle()
self.turtle.up()
self.turtle.hideturtle()
self.turtle.shape("E:/Python/Lib/idlelib/Fish.gif")
self.offsetList = [(-1, 1), (0, 1), (1, 1),
(-1, 0) , (1, 0),
(-1, -1), (0, -1), (1, -1)]
self.theWorld = theworld
self.overcrowd = self.theWorld.fishOvercrowdNum
self.breed = self.theWorld.fishBreedNum
self.xpos = 0
self.ypos = 0
self.world = None
self.breedTick = 0
def setX(self, newx):
self.xpos = newx
def setY(self, newy):
self.ypos = newy
def getX(self):
return self.xpos
def getY(self):
return self.ypos
def setWorld(self, aworld):
self.world = aworld
def appear(self):
self.turtle.goto(self.xpos, self.ypos)
self.turtle.showturtle()
def hide(self):
self.turtle.hideturtle()
def move(self, newx, newy):
self.world.moveThing(self.xpos, self.ypos, newx, newy)
self.xpos = newx
self.ypos = newy
self.turtle.goto(self.xpos, self.ypos)
def liveALittle(self):
adjfish = 0
for offset in self.offsetList:
newx = self.xpos + offset[0]
newy = self.ypos + offset[1]
if 0 <= newx < self.world.getMaxX() and 0 <= newy < self.world.getMaxY():
if (not self.world.emptyLocation(newx, newy)) and isinstance(self.world.lookAtLocation(newx, newy), Fish):
adjfish = adjfish + 1
if adjfish >= self.overcrowd:
self.world.delThing(self)
else:
self.breedTick = self.breedTick + 1
if self.breedTick >= self.breed:
self.tryToBreed()
self.tryToMove()
def tryToBreed(self):
offsetList = [(-1, 1), (0, 1), (1, 1),
(-1, 0) , (1, 0),
(-1, -1), (0, -1), (1, -1)]
randomOffsetIndex = random.randrange(len(offsetList))
randomOffset = offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
while not(0 <= nextx < self.world.getMaxX() and 0 <= nexty < self.world.getMaxY()):
randomOffsetIndex = random.randrange(len(offsetList))
randomOffset = offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
if self.world.emptyLocation(nextx, nexty):
childThing = Fish(self.theWorld)
self.world.addThing(childThing, nextx, nexty)
self.breedTick = 0
def tryToMove(self):
offsetList = [(-1, 1), (0, 1), (1, 1),
(-1, 0) , (1, 0),
(-1, -1), (0, -1), (1, -1)]
randomOffsetIndex = random.randrange(len(offsetList))
randomOffset = offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
while not(0 <= nextx < self.world.getMaxX() and 0 <= nexty < self.world.getMaxY()):
randomOffsetIndex = random.randrange(len(offsetList))
randomOffset = offsetList[randomOffsetIndex]
nextx = self.xpos + randomOffset[0]
nexty = self.ypos + randomOffset[1]
if self.world.emptyLocation(nextx, nexty):
self.move(nextx, nexty)
This is the Class Plant
class Plant:
def __init__(self):
self.turtle = turtle.Turtle()
self.turtle.up()
self.turtle.hideturtle()
self.turtle.shape("E:/Python/Lib/idlelib/Plant.gif")
self.xpos = 0
self.ypos = 0
self.world = None
self.breedTick = 0
#accessor and mutators
def setX(self, newx):
self.xpos = newx
def setY(self, newy):
self.ypos = newy
def getX(self):
return self.xpos
def getY(self):
return self.ypos
def setWorld(self, aworld):
self.world = aworld
def appear(self):
self.turtle.goto(self.xpos, self.ypos)
self.turtle.showturtle()
def hide(self):
self.turtle.hideturtle()
def tryToBreed(self):
offsetList=[(-1,1),(0,1),(1,1),
(-1,0) ,(1,0),
(-1,-1),(0,-1),(1,-1)]
randomOffsetIndex=random.randrange(len(offsetList))
randomOffset=offsetList[randomOffsetIndex]
nextx=self.xpos+randomOffset[0]
nexty=self.ypos+randomOffset[1]
while not(0<=nextx<self.world.getMaxX()and
0<=nexty<self.world.getMaxY() ):
randomOffsetIndex=random.randrange(len(offsetList))
randomOffset=offsetList[randomOffsetIndex]
nextx=self.xpos+randomOffset[0]
nexty=self.ypos+randomOffset[1]
if self.world.emptyLocation(nextx,nexty):
childThing=Plant()
self.world.addThing(childThing,nextx,nexty)
self.breedTick=0
def liveALittle(self):
offsetList = [(-1, 1), (0, 1), (1, 1),
(-1, 0) , (1, 0),
(-1, -1), (0, -1), (1, -1)]
self.breedTick = self.breedTick + 1
if self.breedTick >= 5:
self.tryToBreed()
adjplant = 0
for offset in offsetList:
newx = self.xpos + offset[0]
newy = self.ypos + offset[1]
if 0 <= newx < self.world.getMaxX() and 0 <= newy < self.world.getMaxY():
if (not self.world.emptyLocation(newx, newy)) and isinstance(self.world.lookAtLocation(newx, newy), Plant):
adjplant = adjplant + 1
if adjplant >= 2:
self.world.delThing(self)
else:
self.breedTick = self.breedTick + 1
if self.breedTick >= 12:
self.tryToBreed()
if __name__ == '__main__':
mainSimulation()
The problem with #Tankobot's wturtle.clear() suggestion is that it wipes everything wturtle may have done. So, you'll want a separate turtle just for drawing the text. A related approach is not to clear, but to undo the last write command, if you're using a current Python 3 turtle module:
The scenario for the undo approach is:
Initial Setup:
Create a separate turtle, make it invisible (hideturtle) and pen up.
(You don't need to have the pen down to do turtle.write())
Move your turtle to where you want the counters to appear.
Write out an initial (zero'd) set of counters.
Runtime:
Before you call turtle.write(), call turtle.undo() to remove the previous counters.
Call turtle.write() to display the updated counters. You don't need to move the turtle into position first if it was located correctly for the previous write.
An example I wrote for a turtle user who wanted to know How to output variable on turtle screen
Related
My code
`
import pgzero.music
import pgzrun
import pygame
import time
from pgzero.actor import Actor
WIDTH = 600
HEIGHT = 700
RED = 200, 0, 0
Darkviolet = 104, 34, 139
COLOR1 = 71,60,139
WHITE = (255, 255, 255)
BOX = Rect((20, 100), (100,10))
RADIUS = 13
ball_speed_x = -3
ball_speed_y = 3
hearts = [Actor("heart", (20, 20)), Actor("heart", (55, 20)), Actor("heart", (90, 20))]
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add(self, other):
return Vector(self.x + other.x, 0)
def __sub__(self, other):
return Vector(self.x - other.x, 0)
class Paddle:
def __init__(self, l, h, s1, s2):
self.position = [l, h]
self.size = s1, s2
def draw(self):
screen.draw.filled_rect(Rect(self.position, self.size), RED)
my_paddle = Paddle(200, 600, 100, 20)
class Rectangle:
def __init__(self,x, y, w, h):
self.x = x
self.y = y
self.h = h
self.w = w
def draw(self):
rect = Rect(self.x, self.y, self.h, self.w)
screen.draw.filled_rect(rect, "blue")
rectangles = []
rectan_count = 0
x = 30
y = 50
while rectan_count < 18:
rectangles.append(Rectangle(x, y, 20, 50))
rectan_count += 1
x += 80
if rectan_count == 7:
y += 30
x = 70
elif rectan_count == 13:
y += 30
x = 110
class Ball:
def __init__(self, x, y):
self.position = Vector(x, y)
def draw(self):
screen.draw.filled_circle((self.position.x, self.position.y), RADIUS, "darkviolet")
def update(self):
pass
ball = Ball(300, 300)
def draw():
global ball_speed_x, ball_speed_y
screen.clear()
my_paddle.draw()
ball.draw()
if hearts:
for heart in hearts:
heart.draw()
for rectangle in rectangles:
if (rectangle.x <= ball.position.x <= (rectangle.x + rectangle.w)) and (rectangle.y <= ball.position.y <= (rectangle.y + rectangle.h)):
ball_speed_y *= -1
rectangles.remove(rectangle)
rectangle.draw()
else:
screen.draw.text("Sorry, you lose!!", (150, 200), color="blue", fontsize=50)
quit()
def on_mouse_move(pos):
x = pos[0]
my_paddle.position[0] = x
def update_ball(dt, paddle_x, paddle_y):
global ball_speed_x, ball_speed_y
ball.position.x -= ball_speed_x
ball.position.y -= ball_speed_y
if (ball.position.x >= WIDTH) or (ball.position.x <= 0):
ball_speed_x *= -1
if (ball.position.y >= HEIGHT) or (ball.position.y <= 0):
ball_speed_y *= -1
if ((paddle_x + 100) >= ball.position.x >= paddle_x) and (paddle_y <= ball.position.y):
ball_speed_y *= -1
if ball.position.y >= HEIGHT:
ball.position.x = WIDTH // 2
ball.position.y = HEIGHT // 2
if hearts:
hearts.pop()
ball_speed_y += 1
def update(dt):
update_ball(dt, my_paddle.position[0], my_paddle.position[1])
pgzrun.go()`
I'm doing ping pong on pgzrun and I don't understand exactly how to make lose and win labels and have the game pause and the ball stop and then restart it again. And I still don't understand how to make rectangular barriers that my ball can bounce off. Many thanks in advance for your help!!
I've been following the Raytracing in one weekend tutorial series (which is coded in C++), but I've run into a problem. I know Python is not really the language for raytracing, and it shows. Rendering 4 spheres with 100 samples per pixel at 600x300px with multithreading took a little over an quarter of an hour.
I figured that multithreading wasn't really doing anything. I needed multiprocessing. I tried to implement it, but it gave me the error that pygame.Surface wasn't serializable. Is there a solution for this, or maybe even a better way of implementing this? Or is the best way forward to recreate this in C#/C++?
The render:
Multithreaded code:
vec3.py
import math
import random
class vec3:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
return vec3(
self.x + other.x,
self.y + other.y,
self.z + other.z
)
def __sub__(self, other):
return vec3(
self.x - other.x,
self.y - other.y,
self.z - other.z
)
def __mul__(self, val):
if type(val) == vec3:
return vec3(
self.x * val.x,
self.y * val.y,
self.z * val.z
)
else:
return vec3(
self.x * val,
self.y * val,
self.z * val
)
def __truediv__(self, val):
return vec3(
self.x / val,
self.y / val,
self.z / val
)
def __pow__(self, pow):
return vec3(
self.x ** pow,
self.y ** pow,
self.z ** pow
)
def __rshift__(self, val):
return vec3(
self.x >> val,
self.y >> val,
self.z >> val
)
def __repr__(self):
return "vec3({}, {}, {})".format(self.x, self.y, self.z)
def __str__(self):
return "({}, {}, {})".format(self.x, self.y, self.z)
def dot(self, other):
return (
self.x * other.x +
self.y * other.y +
self.z * other.z
)
def cross(self, other):
return vec3(
(self.y * other.z) - (self.z * other.y),
-(self.x * other.z) - (self.z * other.x),
(self.x * other.y) - (self.y * other.x)
)
def length(self):
return math.sqrt(
(self.x ** 2) +
(self.y ** 2) +
(self.z ** 2)
)
def squared_length(self):
return (
(self.x ** 2) +
(self.y ** 2) +
(self.z ** 2))
def make_unit_vector(self):
return self / (self.length() + 0.0001)
def reflect(self, normal):
return self - normal * (self.dot(normal) * 2)
Raytracing.py
import uuid
import math
import pygame
from random import random
from vec3 import vec3
from threading import Thread
#import time
imprecision = 0.00000001
mindist = 0.001
maxdist = 999999999
class Material:
def random_in_unit_sphere():
p = vec3(random(), random(), random())*2 - vec3(1, 1, 1)
while p.squared_length() >= 1:
p = vec3(random(), random(), random())*2 - vec3(1, 1, 1)
return p
class Matte:
def __init__(self, albedo):
self.albedo = albedo
def scatter(self, ray, rec, scattered):
target = rec.p + rec.normal + Material.random_in_unit_sphere()
scattered.origin = rec.p
scattered.direction = target - rec.p
return True, self.albedo
class Metal:
def __init__(self, albedo, fuzz = 0):
self.albedo = albedo
self.fuzz = fuzz
def scatter(self, ray, rec, scattered):
reflected = ray.direction.make_unit_vector().reflect(rec.normal)
scattered.origin = rec.p
scattered.direction = reflected + (Material.random_in_unit_sphere() * self.fuzz)
return (scattered.direction.dot(rec.normal) > 0), self.albedo
class Transparent:
def __init__(self, refractive_index):
self.ri = refractive_index
def refract(self, v, normal, ni_over_nt):
uv = v.make_unit_vector()
dt = uv.dot(normal)
discriminant = 1 - (ni_over_nt * ni_over_nt * (1-dt*dt))
if discriminant > 0:
refracted = (uv - normal*dt) * ni_over_nt - (normal * math.sqrt(discriminant))
return True, refracted
else:
return False, None
def scatter(self, ray, rec, scattered):
reflected = ray.direction.reflect(rec.normal)
if ray.direction.dot(rec.normal) > 0:
outward_normal = 0 - rec.normal
ni_over_nt = self.ri
else:
outward_normal = rec.normal
ni_over_nt = 1 / self.ri
refracted = self.refract(ray.direction, outward_normal, ni_over_nt)
return bool(refracted[0]), vec3(1, 1, 1)
class Object:
class Sphere:
def __init__(self, pos, r, material=Material.Matte(0.5)):
self.center = pos
self.r = r
self.material = material
def hit(self, ray, t_min, t_max, rec):
oc = ray.origin - self.center
a = ray.direction.dot(ray.direction)
b = oc.dot(ray.direction)
c = oc.dot(oc) - (self.r * self.r)
discriminant = (b*b) - (a*c)
if discriminant < 0:
return False
else:
t = (-b - math.sqrt(discriminant)) / (a + imprecision)
if (t_min < t < t_max):
rec.t = t
rec.p = ray.point_at_parameter(t)
rec.normal = (rec.p - self.center) / self.r
rec.material = self.material
return True
t = (-b + math.sqrt(discriminant)) / (a + imprecision)
if (t_min < t < t_max):
rec.t = t
rec.p = ray.point_at_parameter(t)
rec.normal = (rec.p - self.center) / self.r
rec.material = self.material
return True
class Scene:
def genUUID(self):
id = uuid.uuid4()
while id in self.objects.keys():
id = uuid.uuid4()
return id
def __init__(self):
self.objects = {}
def add(self, object):
id = self.genUUID()
self.objects[id] = object
return id
def hit(self, ray, t_min, t_max, hit_record):
tmp_hit_record = Camera.HitRec()
hit_any = False
closest = maxdist
for object in self.objects.keys():
if self.objects[object].hit(ray, t_min, closest, tmp_hit_record):
hit_any = True
closest = tmp_hit_record.t
hit_record.t = tmp_hit_record.t
hit_record.p = tmp_hit_record.p
hit_record.normal = tmp_hit_record.normal
hit_record.material = tmp_hit_record.material
return hit_any
class Camera:
class Ray:
def __init__(self, A, B):
self.origin = A
self.direction = B
def point_at_parameter(self, t):
return self.origin + (self.direction * t)
class HitRec:
def __init__(self):
self.t = 0
self.p = vec3(0, 0, 0)
self.normal = vec3(0, 0, 0)
def __init__(self, w, screen, scene = Scene(), screen_ratio = 16/9, focal_length = 1):
self.w = w
self.h = int(w / screen_ratio)
self.screen = screen
self.scene = scene
self.screen_ratio = screen_ratio
self.screen_unit_width = 4
self.focal_length = focal_length
self.sampels_per_pixel = 1
self.max_hit_depth = 5
self.max_block_size = 100
# self.depthT = pygame.Surface((w, self.h))
self.origin = vec3(0, 0, 0)
self.horizontal = vec3(self.screen_unit_width, 0, 0)
self.vertical = vec3(0, (-self.screen_unit_width / self.screen_ratio), 0)
self.top_left_corner = \
self.origin - (self.horizontal/2) - (self.vertical/2) - vec3(0, 0, self.focal_length)
def color(self, ray, depth=0):
# Check for sphere hit
rec = self.HitRec()
if self.scene.hit(ray, mindist, maxdist, rec):
scattered = self.Ray(vec3(0, 0, 0), vec3(0, 0, 0))
hit = rec.material.scatter(ray, rec, scattered) # Scattered is the output ray
if depth < self.max_hit_depth and hit[0]:
return self.color(scattered, depth + 1) * hit[1]
else:
return hit[1]
# target = rec.p + rec.normal + self.random_in_unit_sphere()
# return self.color(self.Ray(rec.p, target - rec.p)) * 0.5
## return vec3(rec.normal.x + 1, rec.normal.y + 1, rec.normal.z + 1) * 0.5, rec.t
# Background if nothing's hit
unit_dir = ray.direction.make_unit_vector()
t = 0.5 * (unit_dir.y + 1)
return (vec3(1, 1, 1) * (1 - t)) + (vec3(0.5, 0.7, 1) * t)#, maxdist
def getRay(self, u, v):
return self.Ray(
self.origin,
self.top_left_corner + (self.horizontal * (u + imprecision)) + (self.vertical * (v + imprecision))
)
def renderSquare(self, xoff, yoff, w, h):
pygame.draw.rect(self.screen, (255, 255, 255), (xoff, yoff, w, h))
pygame.display.update()
for y in range(h):
for x in range(w):
col = vec3(0, 0, 0)
for n in range(self.sampels_per_pixel):
u = (x + xoff + random()-0.5) / self.w
v = (y + yoff + random()-0.5) / self.h
r = self.getRay(u, v)
col += (self.color(r) / self.sampels_per_pixel)
col = vec3(math.sqrt(col.x), math.sqrt(col.y), math.sqrt(col.z))
self.screen.set_at((x + xoff, y + yoff), (col.x * 255, col.y * 255, col.z * 255))
pygame.display.update()
def render(self):
blocks = []
for i in range(math.ceil(self.w / self.max_block_size)):
for j in range(math.ceil(self.h / self.max_block_size)):
blocks.append(Thread( target = self.renderSquare, args=(
i * self.max_block_size,
j * self.max_block_size,
self.max_block_size,
self.max_block_size
)))
for job in blocks:
job.start()
for job in blocks:
job.join()
if __name__ == '__main__':
from Raytracing import Camera, Scene, Object
import pygame
pygame.init()
WIDTH, HEIGHT = 600, 300
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Multithreaded raytracer')
scene = Scene()
scene.add(Object.Sphere(vec3( 0, 0, -1), 0.5, Material.Matte(vec3(0, 0.3, 0.8))))
scene.add(Object.Sphere(vec3(-1, 0, -1), 0.5, Material.Metal(vec3(0.8, 0.8, 0.8), 0.1)))
scene.add(Object.Sphere(vec3( 1, 0, -1), 0.5, Material.Metal(vec3(0.8, 0.6, 0.2), 1)))
scene.add(Object.Sphere(vec3(0, -100.5, -1), 100, Material.Matte(vec3(0.8, 0.8, 0))))
tracer = Camera(WIDTH, screen, scene, screen_ratio=2 / 1)
# tracer.render()
tracer.sampels_per_pixel = 100
tracer.render()
loop = True
while loop:
for event in pygame.event.get():
if event.type==pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
loop = False
if event.type==pygame.QUIT:
loop = False
pygame.display.update()
pygame.quit()
For the past few days I've been trying to implement the so called "Smart dots" game. I first saw it on Code Bullet youtube channel: https://www.youtube.com/watch?v=BOZfhUcNiqk. Unfortunately it was coded in Processing language, while the only language I barely know is Python. I finished my python version of the game but some bugs have appeared.
The problem is that on the second generation dots that are selected to be the best just stop moving almost instantly. I think that it has something to do with me being bad at OOP and copying the Brain class wrong. Steps(which i use for movement) Jump from zero(that is set at the beginning) to max value(200) at the first or second looping of the main loop. But the problems don't stop there. At the next generation, when i try to set brain step to zero, it just breaks with:
AttributeError: 'NoneType' object has no attribute 'brain'
I tried setting up the new brain manually but i still get the same errors. If anyone who already made this or has time to spare can help me with this error or even project i would appreciate it.
I know the code has a lot of unused things but that's just the product of me trying to fix it
:(
The commented out code is some of the old code i used.
main2.py(main loop):
import pygame
import klase2
pygame.init()
def main():
win = pygame.display.set_mode((klase2.WIN_W, klase2.WIN_H))
clock = pygame.time.Clock()
population = klase2.Population()
dots = population.return_dots(1000)
goal = klase2.Goal()
run = True
while run:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
goal.draw_goal(win)
for dot in dots:
dot.draw_dot(win)
dot.update_dot()
if population.all_dots_dead():
# natural selection
population.natural_selection()
# mutation
dots = population.mutate_dots()
population.gen += 1
print(population.gen)
pygame.display.update()
main()
kase2(handles all the functions and classes):
import pygame
import numpy as np
from pygame import gfxdraw
import math
import random
pygame.init()
WIN_W = 500
WIN_H = 500
class Brain:
def __init__(self, size):
self.step = 0
self.size = size
self.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.size / 2)))
self.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.size / 2)))
def clone(self):
self.size = self.size
self.directionsx = self.directionsx
self.directionsy = self.directionsy
self.step = 0
class Goal:
def __init__(self):
self.x = WIN_W / 2
self.y = 10
self.color = (255, 20, 20)
self.r = 5
def draw_goal(self, win):
pygame.gfxdraw.aacircle(win, int(self.x), int(self.y), self.r, self.color)
pygame.gfxdraw.filled_circle(win, int(self.x), int(self.y), self.r, self.color)
class Dot:
goal = Goal()
def __init__(self):
self.tick = 0
self.goal = Goal()
self.brain = Brain(400)
self.velx = 0
self.vely = 0
self.accx = 0
self.accy = 0
self.x = WIN_W / 2
self.y = WIN_H - 10
self.r = 3
self.color = (0, 0, 0)
self.alive = True
self.velLimit = 5
self.fitness = 0
def draw_dot(self, win):
pygame.gfxdraw.aacircle(win, int(self.x), int(self.y), self.r, self.color)
pygame.gfxdraw.filled_circle(win, int(self.x), int(self.y), self.r, self.color)
def move_dot(self):
if self.brain.size / 2 > self.brain.step:
self.accx = self.brain.directionsx[self.brain.step]
self.accy = self.brain.directionsy[self.brain.step]
self.brain.step += 1
else:
self.alive = False
self.velx += self.accx
self.vely += self.accy
if self.velx > self.velLimit:
self.velx = self.velLimit
elif self.velx < -self.velLimit:
self.velx = -self.velLimit
if self.vely > self.velLimit:
self.vely = self.velLimit
elif self.vely < -self.velLimit:
self.vely = -self.velLimit
self.x += self.velx
self.y += self.vely
def update_dot(self):
if not self.reached_goal():
self.tick += 1
if self.alive:
self.move_dot()
if self.x < 0 + self.r or self.x > WIN_W - self.r or self.y < 0 + self.r or self.y > WIN_H - self.r or self.reached_goal():
self.alive = False
def distance_to_goal(self):
a = abs(self.x - self.goal.x)
b = abs(self.y - self.goal.y)
return math.sqrt(a**2 + b**2)
def reached_goal(self):
if self.distance_to_goal() <= self.r + self.goal.r:
return True
return False
def fitness_dot(self):
if self.reached_goal():
self.fitness = 1 / (self.brain.step)
else:
self.fitness = 1 / (self.distance_to_goal()**2)
return self.fitness
class Population:
def __init__(self):
self.dots = []
self.newDots = []
self.gen = 0
self.mutateChance = 800
self.size = 0
self.fitness_sum = 0
def return_dots(self, size):
self.size = size
for _ in range(size):
self.dots.append(Dot())
return self.dots
def all_dots_dead(self):
for i in range(len(self.dots)):
if self.dots[i].alive:
return False
return True
def sort_dots(self):
self.dots = sorted(self.dots, key=lambda dot: dot.fitness, reverse=True)
def sum_fitness(self):
for dot in self.dots:
self.fitness_sum += dot.fitness_dot()
return self.fitness_sum
def get_parent(self):
rand = random.uniform(0, self.fitness_sum)
running_sum = 0
for dot in self.dots:
running_sum += dot.fitness
if running_sum >= rand:
return dot
def natural_selection(self):
for dot in self.dots:
dot.fitness_dot()
self.sort_dots()
self.newDots.append(self.dots[0])
self.sum_fitness()
for i in range(1, len(self.dots)):
parent = self.get_parent()
self.newDots.append(Dot())
self.newDots[i].brain = parent.brain
self.newDots[i].brain.step = 0
self.dots = self.newDots
def mutate_dots(self):
for i in range(1, len(self.dots)):
rand = random.randint(0, 1000)
if rand > self.mutateChance:
self.dots[i].brain.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.dots[i].brain.size / 2)))
self.dots[i].brain.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.dots[i].brain.size / 2)))
return self.dots
# def natural_selection(self):
# self.selectedDots = []
# for dot in self.dots:
# dot.fitness_dot()
# self.sort_dots()
# for i in range(0, int(len(self.dots) / 3)):
# self.selectedDots.append(self.dots[i])
# self.selectedDots[i].tick = 0
# self.selectedDots[i].velx = 0
# self.selectedDots[i].vely = 0
# self.selectedDots[i].accx = 0
# self.selectedDots[i].accy = 0
# self.selectedDots[i].x = WIN_W / 2
# self.selectedDots[i].y = WIN_H - 10
# self.selectedDots[i].alive = True
# self.selectedDots[i].fitness = 0
# self.selectedDots[i].brain.step = 0
# self.selectedDots[i].goal = Goal()
#
# def new_dots(self):
# for i in range(len(self.selectedDots), len(self.dots)):
# self.selectedDots.append(Dot())
# self.dots = self.selectedDots
#
# def mutate_dots(self):
# for i, dot in enumerate(self.dots):
# isMutating = random.randint(0, 1000)
# if self.mutateChance > isMutating and i > int(len(self.dots) / 3) and i < (2 * int((len(self.dots) / 3))):
# for j in range(len(dot.brain.directionsx)):
# isMutatingDir = random.randint(0, 1000)
# if isMutatingDir >= 800:
# dot.brain.directionsx[j] = np.random.uniform(low=-2.5, high=2.5, size=1)
# for j in range(len(dot.brain.directionsy)):
# isMutatingDir = random.randint(0, 1000)
# if isMutatingDir >= 800:
# dot.brain.directionsy[j] = np.random.uniform(low=-2.5, high=2.5, size=1)
# return self.dots
'''
def natural_selection(self):
self.selectedDots = []
for dot in self.dots:
dot.fitness_dot()
self.sort_dots()
self.selectedDots = self.dots[0:int(0.3 * len(self.dots))]
def new_dots(self):
for i in range(len(self.dots) - int(0.3 * len(self.dots))):
self.selectedDots.append(self.dots[i])
self.dots = []
def mutate_dots(self):
for i, selectedDot in enumerate(self.selectedDots):
self.tick = 0
self.x = WIN_W / 2
self.y = WIN_H - 10
self.r = 3
self.alive = True
self.velLimit = 5
self.fitness = 0
self.dots = self.selectedDots
return self.dots
'''
'''
def mutate_dots(self):
for i, selectedDot in enumerate(self.selectedDots):
selectedDot.alive = True
if i >= 1:
isMutating = random.randint(0, 1000)
if isMutating <= self.mutateChance:
for j in range(len(selectedDot.brain.directionsx)):
isMutatingDir = random.randint(0, 1000)
if isMutatingDir >= 800:
selectedDot.brain.directionsx[j] = np.random.uniform(low=-2.5, high=2.5, size=1)
for j in range(len(selectedDot.brain.directionsy)):
isMutatingDir = random.randint(0, 1000)
if isMutatingDir >= 800:
selectedDot.brain.directionsy[j] = np.random.uniform(low=-2.5, high=2.5, size=1)
elif isMutating <= 800:
selectedDot.brain.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=200))
selectedDot.brain.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=200))
self.newDots.append(selectedDot)
return self.newDots
'''
The NoneType error is caused by the get_parent method. It searches for a child dot, but has no return value if the search fails (same effect as return None). This code will get past that error
def get_parent(self):
rand = random.uniform(0, self.fitness_sum)
running_sum = 0
for dot in self.dots:
running_sum += dot.fitness
if running_sum >= rand:
return dot
return self.dots[0] # search failed, return 1st dot
I did a packman game using Python but Im having 2 problems. First my score is not updating, so the score remains 1. Second after a while my packman game crashes it, it shows me 2 error message saying an attribute error: in packman.handle_collide() and self.check_collide().I dnt knw how to fix it.
here is my code:
# Create a SNAPMAN
from livewires import games, color
import random
games.init(screen_width = 200, screen_height = 150, fps = 50)
explosion_files = ["explosion1.bmp",
"explosion2.bmp",
"explosion3.bmp",
"explosion4.bmp",
"explosion5.bmp",
"explosion6.bmp",
"explosion7.bmp",
"explosion8.bmp",
"explosion9.bmp"]
def display_score(score):
display_score = games.Text(value = score + 1, size = 25, color = color.black,
top = 5, right = games.screen.width - 10)
games.screen.add(display_score)
#increase the score
score = score + 1
class Packman(games.Sprite):
"""Create a Gumpy that is controlled by the mouse"""
image = games.load_image("packman.png")
def __init__(self, x = games.mouse.x, y = games.mouse.y):
"""Initialise packman"""
super(Packman,self).__init__(image = Packman.image,
x = games.mouse.x,
y = games.mouse.y)
oldx = games.mouse.x
oldy = games.mouse.y
def display_score(score):
display_score = games.Text(value = score + 1, size = 25, color = color.black,
top = 5, right = games.screen.width - 10)
games.screen.add(display_score)
#increase the score
score = score + 1
def update(self):
"""Move Packman's x and y coordinates"""
oldx = self.x
oldy = self.y
self.x = games.mouse.x
self.y = games.mouse.y
if(self.x < oldx):
self.angle = 180
if(self.x > oldx):
self.angle = 0
self.check_collide()
def check_collide(self):
"""Check if collides with ball"""
for packman in self.overlapping_sprites:
packman.handle_collide()
class Ball(games.Sprite):
""" Create the moving balls"""
def update(self):
"""Change the direction when the ball reached the edge"""
if self.right > games.screen.width or self.left < 0:
self.dx = -self.dx
if self.bottom > games.screen.height or self.top < 0:
self.dy = -self.dy
def handle_collide(self):
"""Something must happen when the ball collides"""
#Explosive sound
sound = games.load_sound("explosion.wav")
sound.play()
explosion = games.Animation(images = explosion_files, x = self.x, y = self.y,
n_repeats = 5, repeat_interval = 5,
is_collideable = False)
games.screen.add(explosion)
self.x = random.randrange(games.screen.width)
self.y = random.randrange(games.screen.height)
display_score(1)
def randomX():
"""Generate random x values"""
rx = random.randrange(games.screen.width)
return rx
def ramdomY():
"""Generate random y values"""
ry = random.randrange(games.screen.width)
return ry
def main():
#Set background
wall_image = games.load_image("wall.jpg", transparent = False)
games.screen.background = wall_image
games.mouse.is_visible = False
games.screen.event_grab = True
#Display score
display_score(0)
#Load and display the balls( red, green and blue)
ball_image = games.load_image("ball_red.png")
ball_red = Ball(image = ball_image,
x = random.randrange(games.screen.width),
y = random.randrange(games.screen.height),
dx = 1, dy = 1)
games.screen.add(ball_red)
ball_image = games.load_image("ball_green.png")
ball_green = Ball(image = ball_image,
x = random.randrange(games.screen.width),
y = random.randrange(games.screen.height),
dx = 1, dy = 1)
games.screen.add(ball_green)
ball_image = games.load_image("ball_blue.png")
ball_blue = Ball(image = ball_image,
x = random.randrange(games.screen.width),
y = random.randrange(games.screen.height),
dx = 1, dy = 1)
games.screen.add(ball_blue)
packman_1 = Packman()
games.screen.add(packman_1)
games.screen.mainloop()
main()
The score is not updating because you are incrementing a local variable of the method display_score. You can make it a class member of Packman, for example:
def __init__(self, x = games.mouse.x, y = games.mouse.y):
"""Initialise packman"""
# do something ...
# initialize the score variable
self._score = 0
And then,
def display_score(self):
display_score = games.Text(value = self._score, size = 25,
color = color.black, top = 5, right = games.screen.width - 10)
games.screen.add(display_score)
#increase the score
self._score += 1 # add one to the current value
You missed the self argument in the definition of the method!
I have a python grid class in which I am trying to create a method to get the diagonal line of tiles that a certain tile belongs to. I have succeeded in doing this for going down from left to right in a diagonal line, and would like to know how to change it so that I can go from right to left by changing the "direction" parameter. Here is my method:
def getDiagonal(self, tile, direction = 1):
index = self.index(tile)
diagonal = []
currentIndex = [i - index[0] for i in index]
while currentIndex[1] != self.y:
diagonal.append(self[currentIndex[0]][currentIndex[1]])
currentIndex = [i + 1 for i in currentIndex]
return diagonal
And here is the entire module in which the Grid class is contained:
# Grid library for Pygame by Bobby Clarke
# GNU General Public License 3.0
# Version 1.1
import pygame
import math
from fractions import gcd
from functools import reduce
def _isEven(i):
return i % 2 == 0
def product(_list):
return reduce(lambda x, y: x * y, _list, 1)
def _range(start, stop, step=1):
"""Range function which can handle floats."""
while start < stop:
yield start
start += step
def _simplify(a, b):
hcf = gcd(a, b)
return (a / hcf, b / hcf)
class Tile(pygame.Rect):
def __init__(self, point, size, colour = None, imgs = [], tags = []):
self.size = [int(i) for i in size]
self.point = point
self.colour = colour
for img in imgs:
if isinstance(img, tuple):
imgs[imgs.index(img)] = pygame.image.fromstring(img[0],
img[1],
img[2])
self.imgs = imgs[:]
self.tags = tags[:]
pygame.Rect.__init__(self, self.point, self.size)
def __lt__(self, other):
return (self.point[0] < other.point[0] or
self.point[1] < other.point[1])
def __gt__(self, other):
return (self.point[0] > other.point[0] or
self.point[1] > other.point[1])
def __le__(self, other):
return (self.point[0] <= other.point[0] or
self.point[1] <= other.point[1])
def __ge__(self, other):
return (self.point[0] >= other.point[0] or
self.point[1] >= other.point[1])
def toData(self, imgFormat = "RGBA"):
return (self.point, self.size, self.colour,
[(pygame.image.tostring(img, imgFormat),
img.get_size(), imgFormat) for img in self.imgs], self.tags)
def fromData(data, baseTile = None):
tile = Tile(*data)
if baseTile and isinstance(baseTile, Tile):
baseTile = tile
else:
return tile
def getRect(self):
return self
def getColour(self):
return self.colour
def setColour(self, colour):
self.colour = colour
def getPoint(self):
return self.point
def addTag(self, *tags):
if isinstance(tags[0], list):
self.tags.extend(tags[0])
else:
self.tags.extend(tags)
def hasTag(self, tag):
return (tag in self.tags)
def delTag(self, tag):
self.tags.remove(tag)
def clearTags(self):
self.tags = []
def addImg(self, img, resize = False):
if isinstance(img, pygame.Surface):
if img.get_rect() != self and resize:
img = pygame.transform.scale(img, (self.size))
self.imgs.append(img)
elif img is not None:
raise TypeError("Images must be pygame.Surface object")
def delImg(self, img):
self.imgs.remove(img)
def clearImgs(self):
self.imgs = []
def isClicked(self):
return self.collidepoint(pygame.mouse.get_pos())
def draw(self, surface):
if self.colour is not None:
surface.fill(self.colour, self)
for img in self.imgs:
surface.blit(img, self)
class Grid():
def __init__(self, surface, num, colour = None, tiles = None,
force_square = False):
self.WIDTH = surface.get_width()
self.HEIGHT = surface.get_height()
self.surface = surface
aspect_ratio = _simplify(self.WIDTH, self.HEIGHT)
if isinstance(num, int):
if aspect_ratio == (1, 1) or force_square:
self.x = math.sqrt(num)
self.y = math.sqrt(num)
else:
self.x = aspect_ratio[0] * (num / product(aspect_ratio))
self.y = aspect_ratio[1] * (num / product(aspect_ratio))
else:
try:
self.x = num[0]
self.y = num[1]
except TypeError:
raise TypeError("2nd argument must be int or subscriptable")
self.tilesize = (self.WIDTH / self.x,
self.HEIGHT / self.y)
self.num = num
self.colour = colour
if tiles:
if hasattr(tiles, "__getitem__") and isinstance(tiles[0], Tile):
self.tiles = tiles
else:
self.tiles = [[Tile.fromData(tile) for tile in column]
for column in tiles]
else:
self.tiles = self.maketiles(colour)
def __getitem__(self, index):
return self.tiles[index]
def __setitem__(self, index, new):
self.tiles[index] = new
def __len__(self):
return len(self.tiles)
def index(self, tile):
for column in self.tiles:
if tile in column:
return self.tiles.index(column), column.index(tile)
def getTiles(self):
"""Get all tiles. Returns a generator"""
for column in self.tiles:
for tile in column:
yield tile
def tagSearch(self, tag):
"""Search for tiles by tag. Returns a generator"""
for tile in self.getTiles():
if tile.hasTag(tag):
yield tile
def pointSearch(self, point):
"""Search for tiles by point. Returns a tile"""
for tile in self.getTiles():
if tile.collidepoint(point):
return tile
def rectSearch(self, rect):
"""Search for tiles by rect. Returns a generator"""
for tile in self.getTiles():
if tile.colliderect(rect):
yield tile
def getColumn(self, i):
return self.tiles[i]
def getRow(self, i):
return [column[i] for column in self.tiles]
def checker(self, colour1, colour2 = None):
for column in self.tiles:
for tile in column:
if _isEven(self.tiles.index(column) + column.index(tile)):
tile.setColour(colour1)
else:
if colour2:
tile.setColour(colour2)
def getDiagonal(self, tile, direction = 1):
index = self.index(tile)
diagonal = []
currentIndex = [i - index[0] for i in index]
while currentIndex[1] != self.y:
diagonal.append(self[currentIndex[0]][currentIndex[1]])
currentIndex = [i + 1 for i in currentIndex]
return diagonal
def getBetweenTiles(self, tile1, tile2):
"""Inefficient and badly implemented"""
index1 = self.index(tile1)
index2 = self.index(tile2)
if index1[0] != index2[0] and index1[1] != index2[1]:
raise ValueError("Tiles must be in same row or column")
for column in self.tiles:
if tile1 in column and tile2 in column:
return column[column.index(tile1) : column.index(tile2)]
for i in range(self.y):
row = self.getRow(i)
if tile1 in row and tile2 in row:
return row[row.index(tile1) : row.index(tile2)]
def getSurroundingTiles(self, tile, adjacent = True, diagonal = True):
di = (0, 1, 0, -1, 1, 1, -1, -1)
dj = (1, 0, -1, 0, 1, -1, 1, -1)
# indices 0 - 3 are for horizontal, 4 - 7 are for vertical
index = list(self.getTiles()).index(tile)
max_x = self.x - 1 # Offset for 0 indexing
max_y = self.y - 1
i = int(math.floor(index / self.x))
j = int(index % self.y)
surroundingTiles = []
startat = 0 if adjacent else 4
stopat = 8 if diagonal else 4
for k in range(startat, stopat):
ni = i + di[k]
nj = j + dj[k]
if ni >= 0 and nj >= 0 and ni <= max_x and nj <= max_y:
surroundingTiles.append(self[ni][nj])
surroundingTiles.reverse()
return sorted(surroundingTiles)
def draw(self, drawGrid = False, gridColour = (0, 0, 0), gridSize = 1):
for tile in self.getTiles():
tile.draw(self.surface)
if drawGrid:
pygame.draw.rect(self.surface, gridColour, tile, gridSize)
def maketiles(self, colour):
"""Make the tiles for the grid"""
tiles = []
width = self.WIDTH / self.x
height = self.HEIGHT / self.y
for i in _range(0, self.WIDTH, width):
column = []
for j in _range(0, self.HEIGHT, height):
sq = Tile((i, j), (width, height), colour)
column.append(sq)
tiles.append(column)
return tiles
def toData(self):
return (self.num, self.colour,
[[tile.toData() for tile in column] for column in self.tiles])
def fromData(data, surface):
return Grid(*([surface] + list(data)))
Update:
I have pictorial examples of what I want to do here:
https://dl.dropboxusercontent.com/u/127476718/Images/this%20tile.png
https://dl.dropboxusercontent.com/u/127476718/Images/diags.png
To find the cells along the diagonal through another cell, you must go from this cell in the 4 diagonal directions. The following algorithm finds all cells along the diagonal in a grid of size rows x columns, starting with the cell (row, column):
cells_on_diagonals.append((row, column))
for dir in [(1, 1), (1, -1), (-1, 1), (-1, -1)]:
rn, cn = row + dir[1], column + dir[0]
while 0 <= cn < columns and 0 <= rn < rows:
cells_on_diagonals.append((rn, cn))
rn += dir[1]
cn += dir[0]
Minimal example
import pygame
pygame.init()
window = pygame.display.set_mode((350, 350))
clock = pygame.time.Clock()
grid_x, grid_y = 25, 25
tile_size, rows, columns = 20, 15, 15
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
mx, my = pygame.mouse.get_pos()
column, row = (mx - grid_x) // tile_size, (my - grid_y) // tile_size
cells_on_diagonals = []
if 0 <= column < columns and 0 <= row < rows:
cells_on_diagonals.append((row, column))
for dir in [(1, 1), (1, -1), (-1, 1), (-1, -1)]:
rn, cn = row + dir[1], column + dir[0]
while 0 <= cn < columns and 0 <= rn < rows:
cells_on_diagonals.append((rn, cn))
rn += dir[1]
cn += dir[0]
window.fill(0)
for row in range(rows):
for column in range(columns):
if (row, column) in cells_on_diagonals:
rect = (grid_x + column*tile_size, grid_y + row*tile_size, tile_size, tile_size)
pygame.draw.rect(window, "red", rect)
for column in range(columns+1):
x = grid_x + column*tile_size
pygame.draw.line(window, "white", (x, grid_y), (x, grid_x + rows*tile_size))
for row in range(rows+1):
y = grid_y + row*tile_size
pygame.draw.line(window, "white", (grid_x, y), (grid_x + columns*tile_size, y))
pygame.display.flip()
pygame.quit()
exit()