def main(): pg.init() resolution = (1200, 900) window = pg.display.set_mode(resolution, pg.DOUBLEBUF | pg.RESIZABLE) pg.display.set_caption("pathfinder") map = Terrain(8, 11, 100) # format (y, x) map.set_target([1, 6]) pawn = Pathfinder([6, 6], map.grid) clock = pg.time.Clock() run_flag = True while run_flag: clock.tick(20) for event in pg.event.get(): if event.type == pg.QUIT: run_flag = False if event.type == pg.KEYDOWN: if event.key == pg.K_w: map.add_block_manually(pg.mouse.get_pos()) if event.key == pg.K_e: map.set_target_manually(pg.mouse.get_pos()) if event.key == pg.K_q: map.free_block_manually(pg.mouse.get_pos()) window.fill((120, 120, 120)) map.draw(window) pg.display.update() pawn.make_move(map.grid, window, map)
class World: def __init__(self, window, gamers): self.terrain = Terrain(1200) self.worms = [] self.gamers = gamers for i in range(len(gamers)): self.worms += [ Worm(300 + i * 200, self.terrain.get_level(300 + i * 200) + 5, gamers[i], i) ] self.rockets = [] self.window = window self.control = [] def add_control(self, control): self.control = control def explode(self, x, y, type): self.terrain.explode(x, y, type) if self.control.music_control.playing: self.explosion_sound(type) for w in self.worms: w.explode(x, y, self.terrain, self) def draw(self, window): self.terrain.draw(window) for w in self.worms: w.draw(window) self.control.frame.draw() self.control.draw_pointer() def explosion_sound(self, type): if type == 0: fire = pygame.mixer.Sound("explosion1.wav") if type == 1: fire = pygame.mixer.Sound("explosion2.wav") if type == 2: fire = pygame.mixer.Sound("explosion3.wav") pygame.mixer.Sound.play(fire, loops=0)
class Window(pyglet.window.Window): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) pyglet.clock.schedule_interval(self.update, 1./60) self.strafe = [0, 0] def fps(dt): print 'FPS is %f' % pyglet.clock.get_fps() pyglet.clock.schedule_interval(fps, 2) # Current (x, y, z) position in the world, specified with floats. Note # that, perhaps unlike in class, the y-axis is the vertical axis. self.position = (12, 8, 12) self.press = {} self.press["light"] = 0 # First element is rotation of the player in the x-z plane (ground # plane) measured from the z-axis down. The second is the rotation # angle from the ground plane up. Rotation is in degrees. # # The vertical plane rotation ranges from -90 (looking straight down) to # 90 (looking straight up). The horizontal rotation range is unbounded. self.rotation = (0, 0) self.reticle = None self.t = Terrain(a=33,water_line=1.5,generate=False) self.spin = 180 self.fps = pyglet.clock.ClockDisplay() #self.skybox = SkyBox.fromDir("../example/texture/bluesky", "bluesky") #self.skybox = SkyBox.fromDir("../example/texture/lake2", "jajlake2") self.kostka = Kostka(self.t) def set_exclusive_mouse(self, exclusive): """ If `exclusive` is True, the game will capture the mouse, if False the game will ignore the mouse. """ super(Window, self).set_exclusive_mouse(exclusive) self.exclusive = exclusive def _update(self, dt): """ Private implementation of the `update()` method. This is where most of the motion logic lives, along with gravity and collision detection. Parameters ---------- dt : float The change in time since the last call. """ # walking speed = 5 d = dt * speed # distance covered this tick. dx, dy, dz = self.get_motion_vector() # New position in space, before accounting for gravity. dx, dy, dz = dx * d, dy * d, dz * d # collisions x, y, z = self.position self.position = (x + dx, y + dy, z + dz) self.kostka.update(dt) def update( self, dt ): self._update(dt) def get_motion_vector(self): """ Returns the current motion vector indicating the velocity of the player. Returns ------- vector : tuple of len 3 Tuple containing the velocity in x, y, and z respectively. """ if any(self.strafe): x, y = self.rotation strafe = degrees(atan2(*self.strafe)) y_angle = radians(y) x_angle = radians(x + strafe) m = cos(y_angle) dy = sin(y_angle) if self.strafe[1]: # Moving left or right. dy = 0.0 m = 1 if self.strafe[0] > 0: # Moving backwards. dy *= -1 # When you are flying up or down, you have less left and right # motion. dx = cos(x_angle) * m dz = sin(x_angle) * m # else: # dy = 0.0 # dx = cos(x_angle) # dz = sin(x_angle) else: dy = 0.0 dx = 0.0 dz = 0.0 return (dx, dy, dz) def on_resize(self, width, height): """ Called when the window is resized to a new `width` and `height`. """ # label #self.label.y = height - 10 # reticle if self.reticle: self.reticle.delete() x, y = self.width / 2, self.height / 2 n = 10 self.reticle = pyglet.graphics.vertex_list(4, ('v2i', (x - n, y, x + n, y, x, y - n, x, y + n)) ) def on_draw(self): self.clear() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) self.water_line = 1.5 self.water_color = (0.3,0.3,1,1) self.set_3d() glTranslatef(-16, 0, -16) # kostka self.t.draw(self.position[1]) self.kostka.draw() glPopMatrix() # set_3d camera transf self.set_2d() glColor3d(0, 0, 0) self.fps.draw() def set_2d(self): """ Configure OpenGL to draw in 2d. """ width, height = self.get_size() glDisable(GL_DEPTH_TEST) glDisable(GL_LIGHTING) glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0, width, 0, height, -1, 1) glMatrixMode(GL_MODELVIEW) glLoadIdentity() def set_3d(self): """ Configure OpenGL to draw in 3d. """ width, height = self.get_size() glEnable(GL_DEPTH_TEST) glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(65.0, width / float(height), 0.1, 500.0) # gluPerspective(65, width / float(height), 15, 0) glMatrixMode(GL_MODELVIEW) glLoadIdentity() # set camera glPushMatrix() rx, ry = self.rotation glRotatef(rx, 0, 1, 0) glRotatef(-ry, cos(radians(rx)), 0, sin(radians(rx))) x, y, z = self.position #y = self.t.Height(x, z, floating=True)+.3 glTranslatef(-x, -y, -z) #self.skybox.draw(yrot=rx, xrot=-ry) # set lightning source self.spin += self.press["light"] self.spin %= 360 glPushMatrix(); x = lambda s : sin(radians(s))*30 z = lambda s : cos(radians(s))*30 self.t.lightPos = ( x(self.spin), 15., z(self.spin) ) #self.shader.uniform3fv("lightPos[0]", *[x(self.spin), 15, z(self.spin)]) #self.shader.uniform3fv("lightPos[1]", *[x(self.spin+120), 15, z(self.spin+120)]) #self.shader.uniform3fv("lightPos[2]", *[x(self.spin+240), 15, z(self.spin+240)]) glTranslatef(0, 25, 0) glRotated(self.spin, 1.0, 0.0, 0.0); glTranslated (0.0, 0.0, 30.0); glLightfv(GL_LIGHT0, GL_POSITION, vec(0, 0, 0, 1)) glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, vec(0, -1, 0)) glDisable (GL_LIGHTING); glColor3f (0.0, 1.0, 1.0); glutWireCube (2.0); glEnable (GL_LIGHTING); glPopMatrix() def on_mouse_press(self, x, y, button, modifiers): """ Called when a mouse button is pressed. See pyglet docs for button amd modifier mappings. Parameters ---------- x, y : int The coordinates of the mouse click. Always center of the screen if the mouse is captured. button : int Number representing mouse button that was clicked. 1 = left button, 4 = right button. modifiers : int Number representing any modifying keys that were pressed when the mouse button was clicked. """ if self.exclusive: pass else: self.set_exclusive_mouse(True) def on_mouse_motion(self, x, y, dx, dy): """ Called when the player moves the mouse. Parameters ---------- x, y : int The coordinates of the mouse click. Always center of the screen if the mouse is captured. dx, dy : float The movement of the mouse. """ if self.exclusive: m = 0.15 x, y = self.rotation x, y = x + dx * m, y + dy * m y = max(-90, min(90, y)) self.rotation = (x, y) def on_key_press( self, symbol, modifier ): global piece, bottom if symbol == key.A: self.strafe[1] -= 1 elif symbol == key.W: self.strafe[0] -= 1 elif symbol == key.S: self.strafe[0] += 1 elif symbol == key.D: self.strafe[1] += 1 elif symbol == key.UP : self.kostka.speed += .1 elif symbol == key.DOWN : self.kostka.speed -= .1 elif symbol == key.LEFT : self.kostka.input[0] = True elif symbol == key.RIGHT : self.kostka.input[1] = True elif symbol == key.F : global WIREFRAME WIREFRAME = not WIREFRAME if WIREFRAME : glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) else : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) elif symbol == key.N : self.t = Terrain(water_line=1.5,generate=True) elif symbol == key.Q : exit(0) elif symbol == key.ESCAPE: self.set_exclusive_mouse(False) elif symbol == key.O : self.press["light"] = 1 elif symbol == key.P : self.press["light"] = -1 def on_key_release(self, symbol, modifiers): """ Called when the player releases a key. See pyglet docs for key mappings. Parameters ---------- symbol : int Number representing the key that was pressed. modifiers : int Number representing any modifying keys that were pressed. """ if symbol == key.W: self.strafe[0] += 1 elif symbol == key.S: self.strafe[0] -= 1 elif symbol == key.A: self.strafe[1] += 1 elif symbol == key.D: self.strafe[1] -= 1 elif symbol == key.LEFT : self.kostka.input[0] = False elif symbol == key.RIGHT : self.kostka.input[1] = False elif symbol == key.O : self.press["light"] = 0 elif symbol == key.P : self.press["light"] = 0
class Game: clock = pygame.time.Clock() fps = 120 def __init__(self, terrain_name): pygame.init() self.terrain = Terrain(terrain_name) self.window_size = [self.terrain.pixel_size[0] + 250, self.terrain.pixel_size[1] + 150] self.window = pygame.display.set_mode(self.window_size) self.init_content() def init_content(self): info_box = TextBox(200, 400, bgcolor=colors['white'], alpha=150) info_box.set_position([self.terrain.pixel_size[0] + 25, 250]) info_box.add_default_text("Tower Defence\n\nPlace turrets on the map to protect your base from enemies. You gain money by winning rounds and killing enemies.", align="center") self.box_group = pygame.sprite.Group(info_box) cannon_button = button.TowerButton(CannonTower, self.terrain, info_box) artillery_button = button.TowerButton(ArtilleryTower, self.terrain, info_box) self.tower_buttons = GridGroup(pos=[self.terrain.pixel_size[0] + 25, 15], cols=3, rows=3, margin=10) self.tower_buttons.add(cannon_button, artillery_button) for _ in range(7): empty = button.Button(colors['white'], 60, 60) self.tower_buttons.add(empty) empty.lock() start_button = button.Button(colors['green'], 200, 90) start_button.set_position((self.terrain.pixel_size[0] + 25, 670)) start_button.set_icon_text("Start", font_size=50) start_button.set_description("Start round 1", info_box) start_button.function = self.start_round self.description = info_box self.start_button = start_button self.buttons = pygame.sprite.Group(start_button) def start_round(self): pygame.time.set_timer(pygame.USEREVENT, 1000) self.start_button.lock() def mainloop(self): while True: mouse = pygame.mouse.get_pos() for event in pygame.event.get(): self.tower_buttons.update(event) self.buttons.update(event) self.terrain.handle_event(event, mouse) self.handle_event(event) # Logic testing: self.terrain.update() # Draw everything: self.draw_everything() # Delay framerate and update display: self.clock.tick(self.fps) pygame.display.flip() def handle_event(self, event): if event.type == QUIT: pygame.quit() quit() elif event.type == USEREVENT: self.terrain.enemy_group.add(Boss(self.terrain)) elif event.type == KEYDOWN: if event.key == K_r: self.terrain.static_tower_group.empty() self.terrain.enemy_group.empty() elif event.key == K_DELETE: get_active(self.terrain.static_tower_group).kill() def draw_everything(self): self.window.fill(colors['background']) self.terrain.draw(self.window) self.tower_buttons.draw(self.window) self.buttons.draw(self.window) self.box_group.draw(self.window)
class GLWidget(QGLWidget): GL_MULTISAMPLE = 0x809D rot = 0.0 lastMousePos = None maskCreated = pyqtSignal(np.ndarray) def __init__(self, parent): # OpenGL Widget setup f = QGLFormat() f.setSampleBuffers(True) f.setVersion(3,3) f.setProfile(QGLFormat.CoreProfile) QGLFormat.setDefaultFormat(f) if not QGLFormat.hasOpenGL(): QMessageBox.information(None, "OpenGL samplebuffers", "This system does not support OpenGL.") sys.exit(0) super(GLWidget, self).__init__(f, parent) self.setFocusPolicy(Qt.StrongFocus) self.list_ = [] self.width = 640.0 self.height = 480.0 self.startTimer(40) self.setWindowTitle("Sample Buffers") self.fov = 60.0 self.deltaTime = 0.0 self.lastFrame = None self.sketching = False self.sketchType = 0 self.sketchPoints = [] def initializeGL(self): GL.glClearColor(0.50, 0.50, 0.50, 1.0) #GL.glEnable(GL.GL_ALPHA_TEST) #GL.glAlphaFunc(GL.GL_NOTEQUAL, 0.0) self.heightMap = HeightMap('textures/atacama_height2.png') self.projection = QMatrix4x4() self.projection.perspective(self.fov, self.width / self.height, 0.01, 10000) self.cameraPos = QVector3D(0.0, 1.0, 1.0) self.terrainPos = QVector3D(0.0, 0.0, 0.0) self.roverPos = QVector3D(0.0, 0.0, 0.0) print(GL.glGetString(GL.GL_VERSION)) self.camera = Camera(self.cameraPos, self.heightMap) self.terrain = Terrain(self.terrainPos, self.heightMap) self.mask = np.zeros([1001,1001]) self.terrain.updateRewards(self.mask) # self.rover = Rover(roverPos) def resizeGL(self, w, h): self.width = float(w) self.height = float(h) GL.glViewport(0, 0, w, h) self.projection = QMatrix4x4() self.projection.perspective(self.fov, (self.width / self.height), 0.01, 10000) def setRoverPosition(self, x, y): self.camera.setPosition(x-500.5,y-500.5) def paintGL(self): currentFrame = QTime.currentTime() if (self.lastFrame): self.deltaTime = self.lastFrame.msecsTo(currentFrame) self.lastFrame = currentFrame else: self.lastFrame = currentFrame GL.glClearColor(0.90, 0.90, 0.90, 1.0) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) GL.glEnable(GL.GL_DEPTH_TEST) GL.glEnable(GL.GL_BLEND) GL.glEnable(GLWidget.GL_MULTISAMPLE) self.view = self.camera.getViewMatrix(self.roverPos) self.terrain.draw(self.projection, self.view) GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) def mousePressEvent(self, event): viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) if(self.sketching): self.sketchPoints.append([event.x(), viewport[3] - event.y()]) else: self.lastMousePos = event.pos() print("clicked") cursorX = event.x() cursorY = event.y() winX = float(cursorX) winY = float(viewport[3] - cursorY) # obtain Z position winZ = GL.glReadPixels(winX, winY, 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT); winVector = QVector3D(winX, winY, winZ) print(winVector) def mouseMoveEvent(self, event): #print(event.pos()) #print(self.lastMousePos) if(True): print(event.pos()) viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) if(self.sketching): self.sketchPoints.append([event.x(), viewport[3] - event.y()]) #self.painter.drawPoint(event.pos()) print("Testing mouse move") elif(self.lastMousePos is not None): print(event.pos()) dx = event.x() - self.lastMousePos.x() dy = event.y() - self.lastMousePos.y() self.camera.processMouseMovement(dx,dy) self.lastMousePos = event.pos() elif(event.button() == Qt.MiddleButton): # Dont use this : doesnt work well with trackpads print("Middle") elif(event.button() == Qt.RightButton): print("Right") def mouseReleaseEvent(self, event): print("Mouse Released") if(self.sketching): # print(self.sketchPoints) self.createSketchMask() self.sketching = False def createSketchMask(self): # obtain Z position # pass pixels = [] viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) for point in self.sketchPoints: winZ = GL.glReadPixels(point[0], point[1], 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT); winVector = QVector3D(point[0], point[1], winZ) # print(winVector) object_coord = self.terrain.getObjectCoord(winVector, self.projection, self.view, viewport) j = round(1001 - 1001 * ((0.5 * object_coord[2]) + 0.5) ) i = round( 1001 * ((0.5 * object_coord[0]) + 0.5) ) pixels.append([i,j]) pixelsNP = np.array([pixels]) cv.drawContours(self.mask, pixelsNP, 0, [self.sketchType], -1) self.sketchPoints = [] self.terrain.updateRewards(self.mask) self.maskCreated.emit(self.mask) def wheelEvent(self, event): self.camera.scroll((event.angleDelta().y())) def keyPressEvent(self, event): if event.key() == Qt.Key_1: self.camera.setViewType(0) elif event.key() == Qt.Key_2: self.camera.setViewType(1) elif event.key() == Qt.Key_3: self.camera.setViewType(2) elif event.key() == Qt.Key_4: self.sketching = True self.sketchType = -1 elif event.key() == Qt.Key_5: self.sketching = True self.sketchType = 0 elif event.key() == Qt.Key_6: self.sketching = True self.sketchType = 1 elif event.key() == Qt.Key_W: self.camera.processKeyboard('F', self.deltaTime) elif event.key() == Qt.Key_S: self.camera.processKeyboard('B', self.deltaTime) elif event.key() == Qt.Key_A: self.camera.processKeyboard('L', self.deltaTime) elif event.key() == Qt.Key_D: self.camera.processKeyboard('R', self.deltaTime) def timerEvent(self, event): self.update() def setRewards(self, mask): self.learnedRewards = mask self.terrain.updatelearnedRewards(self.learnedRewards) def setPath(self, mask): self.pathMask = mask self.terrain.updatePaths(self.pathMask)
class Game(pyglet.event.EventDispatcher): def __init__(self): self.window = pyglet.window.Window(fullscreen=True) self.terrain = Terrain(self.window.width, self.window.height-100) self.players = [ Player(name, self, i) for i, name in enumerate(sys.argv[1:])] self.moving = [] # Setup events self.window.event(self.on_draw) Game.register_event_type('next_player') Game.register_event_type('start_moving') Game.register_event_type('stop_moving') Game.register_event_type('explosion') # Start the game. self.reset() # Run the f****r pyglet.app.run() def reset(self): self.terrain.reset() for p in self.players: p.reset() random.shuffle(self.players) for p in self.players: new_x = None while not new_x: new_x = int(random.uniform(20, self.window.width - 20)) for other in self.players: if other != p and abs(other.pos[0] - new_x) < 50: new_x = None break p.pos = new_x, self.terrain.flatten(new_x) self.next_player() def next_round(self): for p in self.players: p.next_round() self.next_player() def next_player(self): live_players = list(filter(lambda p: p.alive, self.players)) if len(live_players) < 2: for p in live_players: p.score += 10000 self.reset() return for p in live_players: if not p.ready: p.take_turn() return self.moving = [ Shot(p.pos, p.angle, p.power, p) for p in self.players if p.alive] pyglet.clock.schedule(self.move, 1/60) def move(self, *args, **kwargs): for s in self.moving: s.move(self) def explosion(self, x, y, r, player=None): self.terrain.explosion(x, y, r) for p in self.players: p.explosion(x, y, r, player) def stop_moving(self, thing): self.moving.remove(thing) if not self.moving: pyglet.clock.unschedule(self.move) self.next_round() def start_moving(self, thing): self.moving.append(thing) def on_draw(self): self.window.clear() self.terrain.draw(self.window.width, self.window.height-100, 100) for p in self.players: p.draw() for m in self.moving: m.draw(self.window.height-100)
class Robot(object): def __init__(self, maze_dim): """ Used to set up attributes that the robot will use to learn and navigate the maze. """ # Position-related attributes self.robot_pos = {'location': [0, 0], 'heading': 'up'} # Current pos self.steps_first_round = 0 self.steps_final_round = 0 self.maze_dim = maze_dim self.maze_representation = None # Goal-related attributes center = maze_dim/2 self.center_locations = [ [center, center], [center - 1, center], [center, center - 1], [center - 1, center - 1]] self.reached_destination = False # For exploring state self.exploring = False self.steps_exploring = 0 self.consecutive_explored_cells = 0 # Initialize terrain self.terrain = Terrain(maze_dim) # Algorithm to use: self.algorithm = None if str(sys.argv[2]).lower() == 'ff': self.algorithm = FloodFill() elif str(sys.argv[2]).lower() == 'ar': self.algorithm = AlwaysRight() elif str(sys.argv[2]).lower() == 'mr': self.algorithm = ModifiedRight() else: raise ValueError( "Incorrect algorithm name. Options are: " "\n- 'ff': flood-fill" "\n- 'ar': always-right" "\n- 'mr': modified-right (prefers unvisited cells)" ) # Explore after reaching center of the maze: if str(sys.argv[3]).lower() == 'true': self.explore_after_center = True elif str(sys.argv[3]).lower() == 'false': self.explore_after_center = False else: raise ValueError( "Incorrect explore value: Options are: " "\n- 'true': to keep exploring after reaching the center" "\n- 'false': to end run immediately after reaching the center" ) def next_move(self, sensors): """ Use this function to determine the next move the robot should make, based on the input from the sensors after its previous move. Sensor inputs are a list of three distances from the robot's left, front, and right-facing sensors, in that order. Outputs should be a tuple of two values. The first value indicates robot rotation (if any), as a number: 0 for no rotation, +90 for a 90-degree rotation clockwise, and -90 for a 90-degree rotation counterclockwise. Other values will result in no rotation. The second value indicates robot movement, and the robot will attempt to move the number of indicated squares: a positive number indicates forwards movement, while a negative number indicates backwards movement. The robot may move a maximum of three units per turn. Any excess movement is ignored. If the robot wants to end a run (e.g. during the first training run in the maze) then returning the tuple ('Reset', 'Reset') will indicate to the tester to end the run and return the robot to the start. """ # Store current location and direction x, y, heading = self.get_current_position() # Get walls for current location walls = self.get_walls_for_current_location(x, y, heading, sensors) # If we have reached the center of the maze if self.is_at_center_of_the_maze(x, y): # Move backwards rotation = 0 movement = -1 # Update terrain (visual representation) self.terrain.update(x, y, heading, walls, self.exploring) # State that we have reached destination self.reached_destination = True # Set flags to exploring self.exploring = True # Else, first update distances, then get next move else: # 1) Push current location to stack self.terrain.cells_to_check.append([x, y]) # 2) Add current cell to stack of visited destinations if [x, y] not in self.terrain.visited_before_reaching_destination: self.terrain.visited_before_reaching_destination.append([x, y]) # 4) Update terrain and distances self.terrain.update(x, y, heading, walls, self.exploring) # 4) Get next move rotation, movement = self.get_next_move(x, y, heading, sensors) self.update_location(rotation, movement) # If we have reached destination, reset values if rotation == 'Reset' and movement == 'Reset': self.reset_values() # If we are about to hit the goal in the second round if self.robot_pos['location'] in self.center_locations \ and self.steps_final_round != 0: self.report_results() return rotation, movement # -------------------------------------------- # LOCATION-RELATED # -------------------------------------------- def reset_values(self): self.robot_pos = {'location': [0, 0], 'heading': 'up'} def get_current_position(self): heading = self.robot_pos['heading'] location = self.robot_pos['location'] x = location[0] y = location[1] return x, y, heading def is_at_starting_position(self, x, y): return x == 0 and y == 0 def is_at_center_of_the_maze(self, x, y): return [x, y] in self.center_locations def is_at_a_dead_end(self, sensors): x, y, heading = self.get_current_position() adj_distances, adj_visited = \ self.terrain.get_adj_info(x, y, heading, sensors, False) return sensors == [0, 0, 0] or adj_distances == list(MAX_DISTANCES) def get_walls_for_current_location(self, x, y, heading, sensors): if self.is_at_starting_position(x, y): walls = [1, 0, 1, 1] # If it had been visited before, just get those values elif self.terrain.grid[x][y].visited != '': walls = self.terrain.grid[x][y].get_total_walls() # Else, get current walls. Note that it can only have real walls # since the location has never been visited, and imaginary walls # are the result of dead ends that force the robot to the prev location else: # Placeholder walls = [0, 0, 0, 0] # Change sensor info to wall info walls_sensors = [1 if x == 0 else 0 for x in sensors] # Map walls to correct x and y coordinates for i in range(len(walls_sensors)): dir_sensor = dir_sensors[heading][i] index = wall_index[dir_sensor] walls[index] = walls_sensors[i] # Update missing wall index (the cell right behind the robot) index = wall_index[dir_reverse[heading]] walls[index] = 0 return walls def get_new_direction(self, rotation): if rotation == -90: return dir_sensors[self.robot_pos['heading']][0] elif rotation == 90: return dir_sensors[self.robot_pos['heading']][2] else: return self.robot_pos['heading'] def update_location(self, rotation, movement): if movement == 'Reset' or rotation == 'Reset': return else: movement = int(movement) # Perform rotation if rotation == -90: self.robot_pos['heading'] = \ dir_sensors[self.robot_pos['heading']][0] elif rotation == 90: self.robot_pos['heading'] = \ dir_sensors[self.robot_pos['heading']][2] # Advance if movement == -1: self.robot_pos['location'][0] -= \ dir_move[self.robot_pos['heading']][0] self.robot_pos['location'][1] -= \ dir_move[self.robot_pos['heading']][1] else: while movement > 0: self.robot_pos['location'][0] += \ dir_move[self.robot_pos['heading']][0] self.robot_pos['location'][1] += \ dir_move[self.robot_pos['heading']][1] movement -= 1 def number_of_walls(self, sensors): number_of_walls = 0 for sensor in sensors: if sensor == 0: number_of_walls += 1 return number_of_walls # -------------------------------------------- # MOVEMENT-RELATED # -------------------------------------------- def get_next_move(self, x, y, heading, sensors): if self.reached_destination and self.exploring: rotation, movement = self.explore(x, y, heading, sensors) self.steps_exploring += 1 elif not self.reached_destination and not self.exploring: if self.algorithm.name == 'flood-fill' and self.is_at_a_dead_end(sensors): rotation, movement = self.deal_with_dead_end(x, y, heading) else: adj_distances, adj_visited = self.terrain.get_adj_info( x, y, heading, sensors) valid_index = self.algorithm.get_valid_index(adj_distances, adj_visited) rotation, movement = self.convert_from_index(valid_index) self.steps_first_round += 1 else: # Final round (optimized movements) if self.steps_final_round == 0: print('******* FINAL REPORT *******') self.terrain.draw() rotation, movement = self.final_round(x, y, heading, sensors) self.steps_final_round += 1 return rotation, movement def get_valid_index(self, x, y, heading, sensors, exploring): if not exploring: # 1) Get adjacent distances from sensors adj_distances, adj_visited = self.terrain.get_adj_info( x, y, heading, sensors) # Get min index (guaranteed to not be a wall) valid_index = adj_distances.index(min(adj_distances)) # Prefer unvisited cells possible_distance = WALL_VALUE best_index = WALL_VALUE for i, dist in enumerate(adj_distances): if dist != WALL_VALUE and adj_visited[i] is '': if dist <= possible_distance: best_index = i if best_index == 1: break if best_index != WALL_VALUE: valid_index = best_index else: # 1) Get adjacent distances from sensors adj_distances, adj_visited = self.terrain.get_adj_info( x, y, heading, sensors) # Convert WALL_VALUES to -1 (robot will follow max distance) adj_distances = [-1 if dist == WALL_VALUE else dist for dist in adj_distances] # Get max index (guaranteed to not be a wall) valid_index = None # Prefer cells that have not been visited for i, dist in enumerate(adj_distances): if dist != -1 and adj_visited[i] is '': self.consecutive_explored_cells = 0 valid_index = i break if valid_index is None: self.consecutive_explored_cells += 1 possible_candidate = None for i, dist in enumerate(adj_distances): if dist != -1 and adj_visited[i] is '*': if possible_candidate is None: possible_candidate = i else: a = adj_distances[possible_candidate] b = adj_distances[i] if b > a: possible_candidate = i valid_index = possible_candidate if valid_index is None: possible_candidate = None for i, dist in enumerate(adj_distances): if dist != -1 and adj_visited[i] is 'e': if possible_candidate is None: possible_candidate = i else: a = adj_distances[possible_candidate] b = adj_distances[i] if b > a: possible_candidate = i valid_index = possible_candidate return valid_index def explore(self, x, y, heading, sensors): if self.should_end_exploring(x, y) or not self.explore_after_center: rotation = 'Reset' movement = 'Reset' self.exploring = False self.terrain.set_imaginary_walls_for_unvisited_cells() self.terrain.update_distances(last_update=True) else: # If we reach a dead end: if self.is_at_a_dead_end(sensors): rotation, movement = self.deal_with_dead_end(x, y, heading) else: valid_index = self.get_valid_index(x, y, heading, sensors, True) if valid_index is not None: rotation, movement = self.convert_from_index(valid_index) else: rotation, movement = 'Reset', 'Reset' return rotation, movement def convert_from_index(self, index): # Move Left if index == 0: rotation = -90 movement = 1 # Move Up elif index == 1: rotation = 0 movement = 1 # Move Right elif index == 2: rotation = 90 movement = 1 # Minimum distance is behind, so just rotate clockwise else: rotation = 90 movement = 0 return rotation, movement def deal_with_dead_end(self, x, y, heading): # 1) Move back one step rotation = 0 movement = -1 # 2) Get reference to cell cell = self.terrain.grid[x][y] # 3) Place imaginary wall behind the robot before exiting location reverse_direction = dir_reverse[heading] index = self.terrain.get_index_of_wall(reverse_direction) cell.imaginary_walls[index] = 1 # 4) Change the value of visited to signify dead end cell.visited = 'x' cell.distance = WALL_VALUE # 5) Update imaginary walls and distances self.terrain.update_imaginary_walls(x, y, cell.imaginary_walls) return rotation, movement def should_end_exploring(self, x, y): """ The robot should end exploring in all these cases: - It has already explored more than 90% of the cells - It has already taken 30 steps (in the exploration phase) - It has reached the center of the maze (again) - It has reached the starting location (again) """ # Check for % of cells covered if self.terrain.get_percentage_of_maze_explored() > 80: return True if self.consecutive_explored_cells >= 3: return True # Check for number of steps if self.steps_exploring > 15: return True # Check for center of the maze: if self.is_at_center_of_the_maze(x, y): return True if self.is_at_starting_position(x, y): return True return False def final_round(self, x, y, heading, sensors): """ Returns the correct rotation and maximum numbers of steps that the robot can take to optimize the score while staying on track """ rotation = None movement = None current_distance = self.terrain.grid[x][y].distance adj_distances, adj_visited = \ self.terrain.get_adj_info( x, y, heading, sensors, False) # Change sensor info to max allowed moves, when it applies sensors = [3 if step > 3 else step for step in sensors] for i, steps in enumerate(sensors): # If we found a movement, exit and apply it if movement is not None: break # Otherwise, iterate through steps to see if one matches with the # next correct and logical distance for that number of steps elif self.is_a_possible_move(adj_distances, adj_visited, i): for idx in range(steps): step = steps - idx rotation = rotations[str(i)] new_direction = self.get_new_direction(rotation) furthest_distance = self.terrain.get_distance( x, y, new_direction, step) if furthest_distance == current_distance - step: movement = step break return rotation, movement def is_a_possible_move(self, adj_distances, adj_visited, i): """ Distances are valid if they are not walls, unvisited, or dead ends. """ return (adj_visited[i] is not '' and adj_visited[i] is not 'x' and adj_distances[i] is not WALL_VALUE) def report_results(self): distance = self.terrain.grid[0][0].distance percentage = self.terrain.get_percentage_of_maze_explored() first_round = self.steps_first_round + self.steps_exploring final_round = self.steps_final_round print('ALGORITHM USED: {}'.format(self.algorithm.name.upper())) print('EXPLORING AFTER CENTER: {}'.format(self.explore_after_center)) print('NUMBER OF MOVES FIRST ROUND: {}'.format(first_round)) print('PERCENTAGE OF MAZE EXPLORED: {}%'.format(percentage)) print('DISTANCE TO CENTER: {}'.format(distance)) print('NUMBER OF MOVES FINAL ROUND: {}'.format(final_round)) print('********************************')
class MainScreen(arcade.Window): ''' Main application class. ''' def __init__(self, width, height, title): super().__init__(width, height, title) arcade.set_background_color(arcade.color.WHITE_SMOKE) def setup(self): ''' Setting up the environment. ''' # Setting up space. self.space = pymunk.Space() self.space.gravity = GRAVITY self.space.sleep_time_threshold = 1 # Creating cars. self.cars = [] for _ in range(NUM_CARS): self.cars.append(BoxCar(self.space)) # Setting up terrain and checkpoints. self.terrain = Terrain(self.space) self.checkpoints = self.terrain.get_checkpoints() # Setting up extra UI elements. self.goal = arcade.create_line(self.checkpoints[-1].x - 40, self.checkpoints[-1].y, self.checkpoints[-1].x - 40, self.checkpoints[-1].y + 50, arcade.color.GREEN, 2) self.generation_number = 1 self.best_score = 0 self.plot_history = [] self.score_history = [] self.score_list = arcade.ShapeElementList() def on_draw(self): ''' Render the screen. ''' arcade.start_render() self.goal.draw() self.show_status() self.terrain.draw() for car in self.cars: car.draw() def on_update(self, delta_time): ''' Update the simulation. ''' self.space.step(DT) # To check if all cars in the current generation are alive. all_dead = True for car in self.cars: alive = car.update_lifespan(self.checkpoints) if alive: all_dead = False if self.best_score < car.get_checkpoint_index(): self.best_score = car.get_checkpoint_index() if all_dead: self.new_generation() def new_generation(self): ''' Generate a new generation of cars. ''' gene_scores = [] for car in self.cars: g_s = car.get_chromosome_and_score() gene_scores.append(g_s) new_genes = evolve(gene_scores) constraints = self.space.constraints for c in constraints: self.space.remove(c) for car in self.cars: self.space.remove(car.chassis.body, car.chassis.shape) for wheel in car.wheels: self.space.remove(wheel.body, wheel.shape) self.cars = [] for gene in new_genes: self.cars.append(BoxCar(self.space, gene)) self.plot_history.append(self.best_score) plt.clf() plt.plot(self.plot_history) plt.xlabel('Number of Generations') plt.ylabel('Best Score per Generation') plt.draw() plt.show(block=False) self.score_history.append({ 'gen': self.generation_number, 'score': self.best_score }) self.score_history = sorted(self.score_history, key=lambda x: x['score'], reverse=True)[:25] self.best_score = 0 self.generation_number += 1 def show_status(self): ''' Show the current generation and history stats. ''' score = f"Current Generation: {self.generation_number}\nBest Car Info | Score: {self.best_score}" arcade.draw_text(score, 50, 50, arcade.color.RED, anchor_y='top') arcade.draw_text('Top 25 Scores', 1100, 780, arcade.color.DARK_BLUE, anchor_x='center', anchor_y='top') for i, score in enumerate(self.score_history): arcade.draw_text( f"Generation: {score['gen']} | Score: {score['score']}\n", 1120, 760 - i * 30, arcade.color.DARK_ELECTRIC_BLUE, anchor_x='center', anchor_y='top')
class GLWidget(QGLWidget): GL_MULTISAMPLE = 0x809D rot = 0.0 lastMousePos = None maskCreated = pyqtSignal(np.ndarray) def __init__(self, parent): # OpenGL Widget setup f = QGLFormat() f.setSampleBuffers(True) f.setVersion(3, 3) f.setProfile(QGLFormat.CoreProfile) QGLFormat.setDefaultFormat(f) if not QGLFormat.hasOpenGL(): QMessageBox.information(None, "OpenGL samplebuffers", "This system does not support OpenGL.") sys.exit(0) super(GLWidget, self).__init__(f, parent) self.setFocusPolicy(Qt.StrongFocus) self.list_ = [] self.width = 640.0 self.height = 480.0 self.startTimer(40) self.setWindowTitle("Sample Buffers") self.fov = 60.0 self.deltaTime = 0.0 self.lastFrame = None self.sketching = False self.sketchType = 0 self.sketchPoints = [] # RoverCAM State Variables self.captureMode = False self.captured = False def initializeGL(self): GL.glClearColor(0.50, 0.50, 0.50, 1.0) self.mode = 2 self.xN = 200 self.yN = 200 self.yawN = 0 self.pathX = np.linspace(450, 550, 100) self.pathNode = 0 self.renderMode = 0 self.heightMap = HeightMap('textures/atacama_height2.png') self.projection = QMatrix4x4() self.projection.perspective(self.fov, self.width / self.height, 0.01, 10000) self.cameraPos = QVector3D(0.0, 1.0, 1.0) self.terrainPos = QVector3D(0.0, 0.0, 0.0) self.roverPos = QVector3D(0.0, 0.0, 0.0) print(GL.glGetString(GL.GL_VERSION)) self.camera = Camera(self.cameraPos, self.heightMap) self.camera.setProjection(self.fov, self.width, self.height, 0.01, 1000) self.roverCamera = Camera(self.cameraPos, self.heightMap) self.roverCamera.setProjection(self.fov, self.width, self.height, 0.01, 1000) self.terrain = Terrain(self.terrainPos, self.heightMap) self.mask = np.zeros([1001, 1001]) self.terrain.updateRewards(self.mask) # set up frame buffer self.fbo = GL.glGenFramebuffers(1) GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo) # Attachments for frame buffer : Texture self.Frametexture = GL.glGenTextures(1) GL.glBindTexture(GL.GL_TEXTURE_2D, self.Frametexture) GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, 640, 480, 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, None) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR) GL.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0, GL.GL_TEXTURE_2D, self.Frametexture, 0) self.rbo = GL.glGenRenderbuffers(1) GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, self.rbo) GL.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH24_STENCIL8, 640, 480) GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, 0) GL.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, self.rbo) if (GL.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER) != GL.GL_FRAMEBUFFER_COMPLETE): print("Framebuffer not complete") GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0) GL.glBindTexture(GL.GL_TEXTURE_2D, 0) # self.rover = Rover(roverPos) def resizeGL(self, w, h): self.width = float(w) self.height = float(h) GL.glViewport(0, 0, w, h) self.projection = QMatrix4x4() self.projection.perspective(self.fov, (self.width / self.height), 0.01, 10000) self.camera.setProjection(self.fov, self.width, self.height, 0.01, 1000) def paintGL(self): if self.mode == 0: self.simplePaint() elif self.mode == 1: self.saveat() self.mode = 0 elif self.mode == 2: self.createDataset() elif self.mode == 3: self.renderMode = 1 self.createDataset() def simplePaint(self): GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0) currentFrame = QTime.currentTime() if (self.lastFrame): self.deltaTime = self.lastFrame.msecsTo(currentFrame) self.lastFrame = currentFrame else: self.lastFrame = currentFrame GL.glClearColor(0.90, 0.90, 0.90, 1.0) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) GL.glEnable(GL.GL_DEPTH_TEST) GL.glEnable(GLWidget.GL_MULTISAMPLE) self.terrain.draw(self.camera, self.renderMode) GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) def saveat(self): image = self.setCameraPosition(self.pathX[self.pathNode], ((2 * self.pathX[self.pathNode]) - 345), 0, -20) self.pathNode = self.pathNode + 1 if self.pathNode > 99: self.pathNode = 0 def createDataset(self): image = self.setCameraPosition(self.xN, self.yN, self.yawN, -20) imageName = "ima" + str(self.xN) + "_" + str(self.yN) + '_' + str( self.yawN) + ".jpeg" cv.imwrite('dataset/' + imageName, cv.flip(image, 0)) self.yN = self.yN + 1 if self.yN > 400: self.yN = 200 self.xN = self.xN + 1 if self.xN > 400: self.xN = 200 self.mode = 0 self.yawN = self.yawN + 45 def setCameraPosition(self, x, y, yaw, pitch): self.roverCamera.setPosition(x - 500.5, y - 500.5) self.roverCamera.setYaw(yaw) self.roverCamera.setPitch(pitch) GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo) GL.glClearColor(0.52, 0.80, 0.92, 1.0) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) GL.glEnable(GL.GL_DEPTH_TEST) GL.glEnable(GLWidget.GL_MULTISAMPLE) self.terrain.draw(self.roverCamera, self.renderMode) GL.glBindTexture(GL.GL_TEXTURE_2D, self.Frametexture) abc = GL.glGetTexImage(GL.GL_TEXTURE_2D, 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE) # self.cde = int.from_bytes(self.abc, byteorder='big') image = np.reshape(np.fromstring(abc, dtype=np.uint8), (480, 640, 3)) # cv.imwrite('framebuffer.jpeg', cv.flip(self.image, 0)) return image def mousePressEvent(self, event): self.mode = 1 viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) # self.createDataset() if (self.sketching): self.sketchPoints.append([event.x(), viewport[3] - event.y()]) else: self.lastMousePos = event.pos() print("clicked") cursorX = event.x() cursorY = event.y() winX = float(cursorX) winY = float(viewport[3] - cursorY) # obtain Z position winZ = GL.glReadPixels(winX, winY, 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT) winVector = QVector3D(winX, winY, winZ) print(winVector) self.paintGL() def mouseMoveEvent(self, event): # print(event.pos()) if (event.button() == Qt.LeftButton): viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) if (self.sketching): self.sketchPoints.append([event.x(), viewport[3] - event.y()]) # self.painter.drawPoint(event.pos()) elif (self.lastMousePos is not None): dx = event.x() - self.lastMousePos.x() dy = event.y() - self.lastMousePos.y() self.camera.processMouseMovement(dx, dy) self.lastMousePos = event.pos() elif (event.button() == Qt.MiddleButton): # Dont use this : doesnt work well with trackpads print("Middle") elif (event.button() == Qt.RightButton): print("Right") def mouseReleaseEvent(self, event): print("Mouse Released") if (self.sketching): # print(self.sketchPoints) self.createSketchMask() self.sketching = False def createSketchMask(self): # obtain Z position # pass pixels = [] viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT)) for point in self.sketchPoints: winZ = GL.glReadPixels(point[0], point[1], 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT) winVector = QVector3D(point[0], point[1], winZ) # print(winVector) object_coord = self.terrain.getObjectCoord(winVector, self.projection, self.view, viewport) j = round(1001 - 1001 * ((0.5 * object_coord[2]) + 0.5)) i = round(1001 * ((0.5 * object_coord[0]) + 0.5)) pixels.append([i, j]) pixelsNP = np.array([pixels]) cv.drawContours(self.mask, pixelsNP, 0, [self.sketchType], -1) self.sketchPoints = [] self.terrain.updateRewards(self.mask) self.maskCreated.emit(self.mask) def wheelEvent(self, event): self.camera.scroll((event.angleDelta().y())) def keyPressEvent(self, event): if event.key() == Qt.Key_1: self.camera.setViewType(0) elif event.key() == Qt.Key_2: self.camera.setViewType(1) elif event.key() == Qt.Key_3: self.camera.setViewType(2) elif event.key() == Qt.Key_4: self.sketching = True self.sketchType = -1 elif event.key() == Qt.Key_5: self.sketching = True self.sketchType = 0 elif event.key() == Qt.Key_6: self.sketching = True self.sketchType = 1 elif event.key() == Qt.Key_G: self.renderMode = 1 elif event.key() == Qt.Key_H: self.renderMode = 0 elif event.key() == Qt.Key_W: self.camera.processKeyboard('F', self.deltaTime) elif event.key() == Qt.Key_S: self.camera.processKeyboard('B', self.deltaTime) elif event.key() == Qt.Key_A: self.camera.processKeyboard('L', self.deltaTime) elif event.key() == Qt.Key_D: self.camera.processKeyboard('R', self.deltaTime) def timerEvent(self, event): self.update() def setRewards(self, mask): self.learnedRewards = mask self.terrain.updatelearnedRewards(self.learnedRewards) def setPath(self, mask): self.pathMask = mask self.terrain.updatePaths(self.pathMask)
class Game(Scene): """ This is the game class, this is the most important scene, and keeps track of the whole game """ def __init__(self): """ Initializes the game scene """ self.mainmenu = None self.winscreen = None self.initEvents() self.initTerrain() self.initTeams() self.createGameObjects() self.startNewGame() def initEvents(self): """ Initializes the eventreader """ SceneManager().registerEventReader(self.do_action) def do_action(self, event): """ Check the events, and do something when needed @param event: The event """ # check events if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: for snail in TurnManager().currentTeam.orderedSnailList: if snail.hasTurn: snail.shoot() if event.key == pygame.K_ESCAPE: SceneManager().setScene(self.mainmenu) def clean(self): """ Clean everything up, so that application doesn't crash """ self.turnManager.stopTimer() SceneManager().unregisterEventReader(self.do_action) def initTeams(self): """ Initializes the team """ self.teams = [] self.teamsAlive = 0 def initTerrain(self): """ Initializes the terrain """ self.terrain = Terrain() def createGameObjects(self): """ Create the terrain """ self.terrain.create(15) def addTeam(self, name, numberOfSnails, gravity_direction): """ Add a team to the game @param name: The name of the team @param numberOfSnails: The amount of snails the team has @param gravity_direction: The gravity direction of the team """ team = Team(name) team.setGravity(gravity_direction) team.addSnails(numberOfSnails) team.setTeamImage((gravity_direction+1)) self.teams.append(team) def startNewGame(self): """ Start a new game """ for i in range(0, Settings.GAME_PLAYERS): self.addTeam('team '+str(i+1), Settings.GAME_SNAILS, i) self.turnManager = TurnManager() self.turnManager.setTeams(self.teams) self.gamemode = GameModes.GAME_PLACING_SNAILS def stopGame(self): """ Stop the game """ self.turnManager.timer.cancel() teamColors = {1:'green', 2:'red', 3:'yellow', 4:'blue'} livingTeamColor = str(teamColors[self.teams[0].colorIndex]) SceneManager().setScene(WinScreen(self.mainmenu, livingTeamColor)) #SceneManager().scene = self.mainmenu def update(self, input): """ Update the game @param input: The input class """ self.terrain.update() self.updateTeams(input) self.updateGameMode() def updateTeams(self, input): """ Update every team @param input: The input class """ self.teamsAlive = 0 for team in self.teams: team.update(input, self.terrain) self.teamsAlive += 1 def updateGameMode(self): """ Update the gamemodes """ if self.gamemode == GameModes.GAME_PLACING_SNAILS: for team in self.teams: for snail in team.sprites(): if snail.isPlaced == False: return self.turnManager.startTimer() self.gamemode = GameModes.GAME_PLAYING if self.gamemode == GameModes.GAME_PLAYING: if self.teamsAlive <= 1: self.stopGame() def draw(self, surface): """ Draw the game on a surface @param surface: The surface the game should be drawed on """ self.terrain.draw(surface) for team in self.teams: team.draw(surface) #self.bullets.draw(surface) if self.gamemode == GameModes.GAME_PLAYING: self.turnManager.draw(surface)
class Game: def __init__(self, screen: pygame.Surface): global current_game current_game = self self.screen = screen spritesheet = SpriteSheet('./assets/sprites.png') self.life_image = pygame.transform.scale( spritesheet.image_at((423, 345, 16, 8), color_key=-1), (64, 32)) self.checkpoint_image = pygame.transform.scale( spritesheet.image_at((389, 335, 8, 8), color_key=-1), (32, 32)) self.simplex = OpenSimplex() self.font = pygame.font.Font('freesansbold.ttf', 24) self.next_hole = self.screen.get_width( ) + random.random() * HOLE_SPACE_VAR self.holes = [] self.checkpoint = 0 self.checkpoint_score = 0 self.lives = 3 self.score = 0 self.highest_score = 0 self.terrain = Terrain(screen, MARTIAN_BROWN, self.terrain_point) self.all_sprites = pygame.sprite.Group() self.player = Player(START_LOCATION, 0) self.all_sprites.add(self.player) def tick(self): if not self.player.dead: self.generate_holes() self.terrain.move(SCROLL_SPEED) self.score += 1 if self.score > self.highest_score: self.highest_score = self.score if self.player_collide_with_hole(): self.explode() if self.terrain.x_pos >= self.checkpoint + CHECKPOINT_DISTANCE: self.checkpoint += CHECKPOINT_DISTANCE self.checkpoint_score = self.score self.remove_holes_before_checkpoint() self.all_sprites.update() def draw(self): self.screen.fill(pygame.Color(0, 0, 0)) self.terrain.draw() self.draw_checkpoints() self.all_sprites.draw(self.screen) self.draw_hud() def explode(self): self.all_sprites.remove(self.player) self.player.dead = True explosion = Explosion(self.player.rect[0] - WHEEL_SIZE / 2, self.player.rect[1], self.take_damage) self.all_sprites.add(explosion) def take_damage(self, explosion): self.all_sprites.remove(explosion) if self.lives > 0: self.goto_checkpoint() self.lives -= 1 else: gameover = GameOver(self.screen.get_width() * 0.2, self.screen.get_height() * 0.2, int(self.screen.get_width() * 0.6), int(self.screen.get_height() * 0.6), self.highest_score, self.reset) self.all_sprites.add(gameover) def player_collide_with_hole(self): if not self.player.on_ground: return False for hole in self.holes: player_x = self.player.x_pos + ROVER_SIZE / 2 hole_x = hole - self.terrain.x_pos if hole_x < player_x and player_x - 50 < hole_x + HOLE_WIDTH: return True return False def draw_checkpoints(self): next_checkpoint = CHECKPOINT_DISTANCE - self.terrain.x_pos % CHECKPOINT_DISTANCE - 32 self.screen.blit(self.checkpoint_image, (next_checkpoint, self.screen.get_height() - 100)) def draw_hud(self): padding = 10 score_text = self.font.render(f'Score: {self.score}', True, WHITE) self.screen.blit(score_text, (padding, padding, score_text.get_width(), score_text.get_height())) life_width, life_height = self.life_image.get_size() for i in range(self.lives): x = self.screen.get_width() - (padding + life_width) * (i + 1) y = padding self.screen.blit(self.life_image, (x, y, life_width, life_height)) def generate_holes(self): end_x_pos = self.terrain.get_end_pos() if end_x_pos > self.next_hole: self.holes.append(self.next_hole) self.next_hole = self.next_hole + random.random( ) * HOLE_SPACE_VAR + HOLE_SPACE_MIN next_checkpoint = self.checkpoint + CHECKPOINT_DISTANCE if (self.next_hole + HOLE_WIDTH > next_checkpoint - HOLE_SPACE_MIN and self.next_hole < next_checkpoint + HOLE_SPACE_MIN): self.next_hole = next_checkpoint + CHECKPOINT_SAFE_SPACE def terrain_point(self, x): y = 200 for i in range(1, 5): y += self.simplex.noise2d(x / (10 * i**3), i * 100) * 2**i * 3 new_hole = next( (hole for hole in self.holes if hole < x < hole + HOLE_WIDTH), None) if new_hole: x_root = HOLE_WIDTH / 2 stretch = HOLE_DEPTH / x_root**2 x_offset = x - new_hole - x_root y += stretch * (x_offset + x_root) * (x_offset - x_root) return y def remove_holes_before_checkpoint(self): self.holes = [hole for hole in self.holes if hole > self.checkpoint] def goto_checkpoint(self): self.score = self.checkpoint_score self.player.x_pos = START_LOCATION self.terrain.goto(self.checkpoint) self.all_sprites.add(self.player) self.player.dead = False def reset(self, gameover): self.all_sprites.remove(gameover) self.lives = 3 self.score = 0 self.highest_score = 0 self.next_hole = self.screen.get_width( ) + random.random() * HOLE_SPACE_VAR self.holes = [] self.terrain.goto(0) self.player.x_pos = START_LOCATION self.all_sprites.add(self.player) self.player.dead = False