From de1001dd57fe468aa36f41d00fb8fb0506a2b7c4 Mon Sep 17 00:00:00 2001 From: "Waylon S. Walker" Date: Sat, 22 Nov 2025 22:14:46 -0600 Subject: [PATCH] wip --- .gitignore | 1 + aforest.py | 108 ++++ camera.py | 180 +++++++ constraints.py | 250 ++++++++++ creeper_adventure/assets/bee/idle/1.png | Bin creeper_adventure/assets/creeper/idle/1.png | Bin creeper_adventure/assets/creeper/idle/2.png | Bin creeper_adventure/assets/creeper/idle/3.png | Bin creeper_adventure/assets/creeper/idle/4.png | Bin creeper_adventure/assets/creeper/idle/5.png | Bin creeper_adventure/assets/leaf.png | Bin creeper_adventure/assets/oak_trees/1.png | Bin creeper_adventure/assets/oak_trees/2.png | Bin creeper_adventure/assets/oak_trees/4.png | Bin creeper_adventure/assets/oak_trees/5.png | Bin creeper_adventure/assets/spotlight.png | Bin creeper_adventure/creeper.py | 7 +- creeper_adventure/game.py | 0 forest.py | 63 +++ gpt-menu.py | 85 ++++ gradients.py | 42 ++ grass.png | Bin 0 -> 3673 bytes iso.py | 56 +++ pyproject.toml | 1 + requirements.txt | 0 site/static/creeper-1.png | Bin spacegame.py | 99 ++++ tetris.py | 521 ++++++++++++++++++++ tmp.py | 41 ++ wall.png | Bin 0 -> 5347 bytes 30 files changed, 1451 insertions(+), 3 deletions(-) create mode 100644 aforest.py create mode 100644 camera.py create mode 100644 constraints.py mode change 100755 => 100644 creeper_adventure/assets/bee/idle/1.png mode change 100755 => 100644 creeper_adventure/assets/creeper/idle/1.png mode change 100755 => 100644 creeper_adventure/assets/creeper/idle/2.png mode change 100755 => 100644 creeper_adventure/assets/creeper/idle/3.png mode change 100755 => 100644 creeper_adventure/assets/creeper/idle/4.png mode change 100755 => 100644 creeper_adventure/assets/creeper/idle/5.png mode change 100755 => 100644 creeper_adventure/assets/leaf.png mode change 100755 => 100644 creeper_adventure/assets/oak_trees/1.png mode change 100755 => 100644 creeper_adventure/assets/oak_trees/2.png mode change 100755 => 100644 creeper_adventure/assets/oak_trees/4.png mode change 100755 => 100644 creeper_adventure/assets/oak_trees/5.png mode change 100755 => 100644 creeper_adventure/assets/spotlight.png mode change 100755 => 100644 creeper_adventure/creeper.py mode change 100755 => 100644 creeper_adventure/game.py create mode 100644 forest.py create mode 100644 gpt-menu.py create mode 100644 gradients.py create mode 100644 grass.png create mode 100644 iso.py mode change 100755 => 100644 requirements.txt mode change 100755 => 100644 site/static/creeper-1.png create mode 100644 spacegame.py create mode 100644 tetris.py create mode 100644 tmp.py create mode 100644 wall.png diff --git a/.gitignore b/.gitignore index 1930ca0..26750c3 100644 --- a/.gitignore +++ b/.gitignore @@ -956,3 +956,4 @@ FodyWeavers.xsd # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode +.null-ls_574349_tetris.py diff --git a/aforest.py b/aforest.py new file mode 100644 index 0000000..47db5fe --- /dev/null +++ b/aforest.py @@ -0,0 +1,108 @@ +import math + +import noise +import pygame + +# set up pygame +pygame.init() + +# set up the window +screen_width, screen_height = 1280, 800 +screen = pygame.display.set_mode((screen_width, screen_height)) +# set up the noise surface +noise_width, noise_height = 1280, 800 +noise_scale = 10 +noise_width = int(screen_width / noise_scale) +noise_height = int(screen_height / noise_scale) +# noise_width, noise_height = round(1280 / 10), round(800 / 10) +noise_surface = pygame.Surface((noise_width, noise_height)) +# set up the colors +BLACK = (0, 0, 0) +GREEN = (0, 255, 0) +BLUE = (0, 0, 255) +colors = [(0, 128, 0), (0, 64, 0), (0, 32, 0)] +# set up the noise +octaves = 1 +freq = 256 * octaves +# generate the noise +noise_map = [ + [ + noise.pnoise2(x / freq, y / freq, octaves, repeatx=3024, repeaty=3024) + for x in range(screen_width) + ] + for y in range(screen_height) +] +# set up the clock +clock = pygame.time.Clock() +# set up the animation +frame = 0 +# set up the noise surface cache +noise_surface_cache = [] +# set up the noise surface cache index +noise_surface_cache_index = 0 +# set up the noise surface cache size +noise_surface_cache_size = 120 +# set up the noise surface cache +for i in range(noise_surface_cache_size): + + print(f"caching {i} of {noise_surface_cache_size}") + # draw the background + for y in range(noise_height): + for x in range(noise_width): + color = colors[2] + if ( + noise_map[y * noise_scale][x * noise_scale] + + math.sin(i * (3.14 / noise_surface_cache_size)) + > 0.2 + ): + color = colors[1] + elif ( + noise_map[y * noise_scale][x * noise_scale] + + math.sin(i * (3.14 / noise_surface_cache_size)) + < 0.15 + ): + color = colors[0] + noise_surface.set_at((x, y), color) + # cache the noise surface + + noise_surface_scaled = pygame.transform.scale( + noise_surface, (screen_width, screen_height) + ) + # noise_surface_scaled_pil = Image.frombytes( + # "RGB", + # (screen_width, screen_height), + # pygame.image.tostring(noise_surface_scaled, "RGB", False), + # ) + + # noise_surface_scaled_pil = noise_surface_scaled_pil.filter( + # ImageFilter.GaussianBlur(radius=20) + # ) + # noise_surface_scaled = pygame.image.fromstring( + # noise_surface_scaled_pil.tobytes(), (screen_width, screen_height), "RGB" + # ) + noise_surface_cache.append(noise_surface_scaled) + +noise_surface_cache.extend([n for n in noise_surface_cache[::-1]]) +# main loop +running = True +print("running") +while running: + # keep loop running at the right speed + clock.tick(60) + # process events + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + # increment the frame + frame += 0.2 + if frame > noise_surface_cache_size - 1: + frame = 0 + # draw the noise surface onto the screen + screen.blit(noise_surface_cache[int(frame)], (0, 0)) + + # display the fps on the screen + pygame.display.set_caption(str(clock.get_fps())) + # update the display + pygame.display.flip() + +pygame.quit() diff --git a/camera.py b/camera.py new file mode 100644 index 0000000..807f8e7 --- /dev/null +++ b/camera.py @@ -0,0 +1,180 @@ +"""Basic showcase on how the transform property on SpaceDebugDrawOptions can +be used as a camera to allow panning. Use arrows to move the camera. +""" + +__docformat__ = "reStructuredText" + +import random +import sys + +import pygame +import pymunk.pygame_util +from pymunk.vec2d import Vec2d + +random.seed(0) + + +def main(): + pygame.init() + screen = pygame.display.set_mode((600, 600)) + clock = pygame.time.Clock() + running = True + font = pygame.font.Font(None, 16) + text = font.render( + "Use Arrows (up, down, left, right) to move the camera, " + "a and z to zoom in / out and s and x to rotate.", + True, + pygame.Color("black"), + ) + + ### Physics stuff + space = pymunk.Space() + space.gravity = Vec2d(0.0, 900.0) + draw_options = pymunk.pygame_util.DrawOptions(screen) + + ## Balls + balls = [] + + body = pymunk.Body() + body.position = pymunk.Vec2d(407, 354) + s1 = pymunk.Segment(body, Vec2d(-300, -30), Vec2d(0, 0), 1.0) + s2 = pymunk.Segment(body, Vec2d(0, 0), Vec2d(0, -100), 1.0) + s1.density = 0.1 + s2.density = 0.1 + s1.friction = 1 + s2.friction = 1 + space.add(body, s1, s2) + + c1 = pymunk.constraints.DampedSpring( + space.static_body, + body, + (427, 200), + (0, -100), + Vec2d(407, 254).get_distance((427, 200)), + 2000, + 100, + ) + + c2 = pymunk.constraints.DampedSpring( + space.static_body, + body, + (87, 200), + (-300, -30), + Vec2d(107, 324).get_distance((87, 200)), + 2000, + 100, + ) + space.add(c1, c2) + + # extra to show how constraints are drawn when very small / large + body = pymunk.Body(1, 100) + body.position = 450, 305 + c3 = pymunk.constraints.DampedSpring( + space.static_body, body, (450, 300), (0, 0), 5, 1000, 100 + ) + space.add(body, c3) + body = pymunk.Body(1, 100) + body.position = 500, 2025 + c3 = pymunk.constraints.DampedSpring( + space.static_body, body, (500, 25), (0, 0), 2000, 1000, 100 + ) + space.add(body, c3) + + ticks_to_next_ball = 10 + + translation = pymunk.Transform() + scaling = 1 + rotation = 0 + + while running: + for event in pygame.event.get(): + if ( + event.type == pygame.QUIT + or event.type == pygame.KEYDOWN + and event.key == pygame.K_ESCAPE + ): + running = False + elif event.type == pygame.KEYDOWN and event.key == pygame.K_p: + pygame.image.save(screen, "camera.png") + + keys = pygame.key.get_pressed() + left = int(keys[pygame.K_LEFT]) + up = int(keys[pygame.K_UP]) + down = int(keys[pygame.K_DOWN]) + right = int(keys[pygame.K_RIGHT]) + + zoom_in = int(keys[pygame.K_a]) + zoom_out = int(keys[pygame.K_z]) + rotate_left = int(keys[pygame.K_s]) + rotate_right = int(keys[pygame.K_x]) + + translate_speed = 10 + translation = translation.translated( + translate_speed * left - translate_speed * right, + translate_speed * up - translate_speed * down, + ) + + zoom_speed = 0.1 + scaling *= 1 + (zoom_speed * zoom_in - zoom_speed * zoom_out) + + rotation_speed = 0.1 + rotation += rotation_speed * rotate_left - rotation_speed * rotate_right + + # to zoom with center of screen as origin we need to offset with + # center of screen, scale, and then offset back + draw_options.transform = ( + pymunk.Transform.translation(300, 300) + @ pymunk.Transform.scaling(scaling) + @ translation + @ pymunk.Transform.rotation(rotation) + @ pymunk.Transform.translation(-300, -300) + ) + + ticks_to_next_ball -= 1 + if ticks_to_next_ball <= 0: + ticks_to_next_ball = 100 + mass = 10 + radius = 25 + inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0)) + body = pymunk.Body(mass, inertia) + x = random.randint(115, 350) + body.position = x, 100 + if random.random() > 0.5: + shape = pymunk.Circle(body, radius) + else: + shape = pymunk.Poly.create_box( + body, size=(radius * 2, radius * 2), radius=2 + ) + shape.friction = 1 + space.add(body, shape) + balls.append(shape) + + ### Clear screen + screen.fill(pygame.Color("white")) + + ### Draw stuff + space.debug_draw(draw_options) + + balls_to_remove = [] + for ball in balls: + if ball.body.position.y > 500: + balls_to_remove.append(ball) + + for ball in balls_to_remove: + space.remove(ball, ball.body) + balls.remove(ball) + + screen.blit(text, (5, 5)) + + ### Update physics + dt = 1.0 / 60.0 + space.step(dt) + + ### Flip screen + pygame.display.flip() + clock.tick(50) + pygame.display.set_caption("fps: " + str(clock.get_fps())) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/constraints.py b/constraints.py new file mode 100644 index 0000000..a9d518d --- /dev/null +++ b/constraints.py @@ -0,0 +1,250 @@ +""" +Pymunk constraints demo. Showcase of all the constraints included in Pymunk. + +Adapted from the Chipmunk Joints demo: +https://github.com/slembcke/Chipmunk2D/blob/master/demo/Joints.c +""" + +import inspect +import math + +import pygame +import pymunk.pygame_util +from pymunk.vec2d import Vec2d + +pygame.init() +screen = pygame.display.set_mode((1200, 600)) +clock = pygame.time.Clock() +font = pygame.font.Font(None, 24) + + +help_txt = font.render( + "Pymunk constraints demo. Use mouse to drag/drop. Hover to see descr.", + True, + pygame.Color("darkgray"), +) + +space = pymunk.Space() +space.gravity = (0.0, 900.0) +draw_options = pymunk.pygame_util.DrawOptions(screen) + +# containers +box_size = 200 +w = screen.get_width() +h = screen.get_height() +for i in range(6): + sw = pymunk.Segment(space.static_body, (0, i * box_size), (w, i * box_size), 1) + sw.friction = 1 + sw.elasticity = 1 + sh = pymunk.Segment( + space.static_body, (i * box_size, 0), (i * box_size, h - box_size), 1 + ) + sh.friction = 1 + sh.elasticity = 1 + space.add(sw, sh) + + +def add_ball(space, pos, box_offset): + body = pymunk.Body() + body.position = Vec2d(*pos) + box_offset + shape = pymunk.Circle(body, 20) + shape.mass = 1 + shape.friction = 0.7 + space.add(body, shape) + return body + + +def add_bar(space, pos, box_offset): + body = pymunk.Body() + body.position = Vec2d(*pos) + box_offset + shape = pymunk.Segment(body, (0, 40), (0, -40), 6) + shape.mass = 2 + shape.friction = 0.7 + space.add(body, shape) + return body + + +def add_lever(space, pos, box_offset): + body = pymunk.Body() + body.position = pos + Vec2d(*box_offset) + (0, -20) + shape = pymunk.Segment(body, (0, 20), (0, -20), 5) + shape.mass = 1 + shape.friction = 0.7 + space.add(body, shape) + return body + + +def main(): + txts = {} + + box_offset = 0, 0 + b1 = add_ball(space, (50, 60), box_offset) + b2 = add_ball(space, (150, 60), box_offset) + c: pymunk.Constraint = pymunk.PinJoint(b1, b2, (20, 0), (-20, 0)) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size, 0 + b1 = add_ball(space, (50, 60), box_offset) + b2 = add_ball(space, (150, 60), box_offset) + c = pymunk.SlideJoint(b1, b2, (20, 0), (-20, 0), 40, 80) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 2, 0 + b1 = add_ball(space, (50, 60), box_offset) + b2 = add_ball(space, (150, 60), box_offset) + c = pymunk.PivotJoint(b1, b2, Vec2d(*box_offset) + (100, 60)) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 3, 0 + b1 = add_ball(space, (50, 60), box_offset) + b2 = add_ball(space, (150, 60), box_offset) + c = pymunk.GrooveJoint(b1, b2, (50, 50), (50, -50), (-50, 0)) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 4, 0 + b1 = add_ball(space, (50, 60), box_offset) + b2 = add_ball(space, (150, 60), box_offset) + c = pymunk.DampedSpring(b1, b2, (30, 0), (-30, 0), 20, 5, 0.3) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 5, 0 + b1 = add_bar(space, (50, 80), box_offset) + b2 = add_bar(space, (150, 80), box_offset) + # Add some joints to hold the circles in place. + space.add(pymunk.PivotJoint(b1, space.static_body, (50, 80) + Vec2d(*box_offset))) + space.add(pymunk.PivotJoint(b2, space.static_body, (150, 80) + Vec2d(*box_offset))) + c = pymunk.DampedRotarySpring(b1, b2, 0, 3000, 60) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = 0, box_size + b1 = add_lever(space, (50, 100), box_offset) + b2 = add_lever(space, (150, 100), box_offset) + # Add some joints to hold the circles in place. + space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset))) + space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset))) + # Hold their rotation within 90 degrees of each other. + c = pymunk.RotaryLimitJoint(b1, b2, math.pi / 2, math.pi / 2) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size, box_size + b1 = add_lever(space, (50, 100), box_offset) + b2 = add_lever(space, (150, 100), box_offset) + # Add some pin joints to hold the circles in place. + space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset))) + space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset))) + # Ratchet every 90 degrees + c = pymunk.RatchetJoint(b1, b2, 0, math.pi / 2) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 2, box_size + b1 = add_bar(space, (50, 100), box_offset) + b2 = add_bar(space, (150, 100), box_offset) + # Add some pin joints to hold the circles in place. + space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset))) + space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset))) + # Force one to sping 2x as fast as the other + c = pymunk.GearJoint(b1, b2, 0, 2) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + box_offset = box_size * 3, box_size + b1 = add_bar(space, (50, 100), box_offset) + b2 = add_bar(space, (150, 100), box_offset) + # Add some pin joints to hold the circles in place. + space.add(pymunk.PivotJoint(b1, space.static_body, (50, 100) + Vec2d(*box_offset))) + space.add(pymunk.PivotJoint(b2, space.static_body, (150, 100) + Vec2d(*box_offset))) + # Make them spin at 1/2 revolution per second in relation to each other. + c = pymunk.SimpleMotor(b1, b2, math.pi) + txts[box_offset] = inspect.getdoc(c) + space.add(c) + + # TODO add one or two advanced constraints examples, such as a car or rope + + mouse_joint = None + mouse_body = pymunk.Body(body_type=pymunk.Body.KINEMATIC) + + # Build rendered help texts + box_texts = {} + for k in txts: + l = 0 + box_texts[k] = [] + # Only take the first 5 lines. + for line in txts[k].splitlines()[:5]: + txt = font.render(line, True, pygame.Color("black")) + box_texts[k].append(txt) + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + exit() + elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: + exit() + elif event.type == pygame.MOUSEBUTTONDOWN: + if mouse_joint is not None: + space.remove(mouse_joint) + mouse_joint = None + + p = Vec2d(*event.pos) + hit = space.point_query_nearest(p, 5, pymunk.ShapeFilter()) + if hit is not None and hit.shape.body.body_type == pymunk.Body.DYNAMIC: + shape = hit.shape + # Use the closest point on the surface if the click is outside + # of the shape. + if hit.distance > 0: + nearest = hit.point + else: + nearest = p + mouse_joint = pymunk.PivotJoint( + mouse_body, + shape.body, + (0, 0), + shape.body.world_to_local(nearest), + ) + mouse_joint.max_force = 50000 + mouse_joint.error_bias = (1 - 0.15) ** 60 + space.add(mouse_joint) + + elif event.type == pygame.MOUSEBUTTONUP: + if mouse_joint is not None: + space.remove(mouse_joint) + mouse_joint = None + + screen.fill(pygame.Color("white")) + + screen.blit(help_txt, (5, screen.get_height() - 20)) + + mouse_pos = pygame.mouse.get_pos() + + # Display help message + x = mouse_pos[0] // box_size * box_size + y = mouse_pos[1] // box_size * box_size + + if (x, y) in box_texts: + txts = box_texts[(x, y)] + i = 0 + for txt in txts: + pos = (5, box_size * 2 + 10 + i * 20) + screen.blit(txt, pos) + i += 1 + + mouse_body.position = mouse_pos + + space.step(1.0 / 60) + + space.debug_draw(draw_options) + pygame.display.flip() + + clock.tick(60) + pygame.display.set_caption(f"fps: {clock.get_fps()}") + + +if __name__ == "__main__": + main() diff --git a/creeper_adventure/assets/bee/idle/1.png b/creeper_adventure/assets/bee/idle/1.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/creeper/idle/1.png b/creeper_adventure/assets/creeper/idle/1.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/creeper/idle/2.png b/creeper_adventure/assets/creeper/idle/2.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/creeper/idle/3.png b/creeper_adventure/assets/creeper/idle/3.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/creeper/idle/4.png b/creeper_adventure/assets/creeper/idle/4.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/creeper/idle/5.png b/creeper_adventure/assets/creeper/idle/5.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/leaf.png b/creeper_adventure/assets/leaf.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/oak_trees/1.png b/creeper_adventure/assets/oak_trees/1.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/oak_trees/2.png b/creeper_adventure/assets/oak_trees/2.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/oak_trees/4.png b/creeper_adventure/assets/oak_trees/4.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/oak_trees/5.png b/creeper_adventure/assets/oak_trees/5.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/assets/spotlight.png b/creeper_adventure/assets/spotlight.png old mode 100755 new mode 100644 diff --git a/creeper_adventure/creeper.py b/creeper_adventure/creeper.py old mode 100755 new mode 100644 index 8cb48a8..f9eccd1 --- a/creeper_adventure/creeper.py +++ b/creeper_adventure/creeper.py @@ -1,10 +1,12 @@ -import random from copy import copy from itertools import cycle, repeat from pathlib import Path +import random +from typing import List, Optional -import pygame from more_itertools import flatten +from pydantic import BaseModel +import pygame from creeper_adventure.game import Game @@ -348,7 +350,6 @@ class MainMenu(Menu): h = 50 x = self.game.screen.get_size()[0] / 2 - w / 2 y = 300 - print(self.game.frames) return Button(self.game, self.surf, self.button_text, x, y, w, h, self.toggle) def set_button_text(self): diff --git a/creeper_adventure/game.py b/creeper_adventure/game.py old mode 100755 new mode 100644 diff --git a/forest.py b/forest.py new file mode 100644 index 0000000..3a67d9c --- /dev/null +++ b/forest.py @@ -0,0 +1,63 @@ +import math + +from noise import pnoise2 +import pygame + +# Initialize pygame +pygame.init() + +base = 0 +persistence = 0.4 +lacunarity = 2.0 +more_x = 0 +# Set up the drawing window +screen = pygame.display.set_mode([800, 600]) + + +def S(): + i = 0 + while True: + i += 0.1 + yield math.sin(i) + + +s = S() + +# Run until the user asks to quit +running = True +while running: + + # Did the user click the window close button? + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Fill the background with white + screen.fill((255, 255, 255)) + + m = next(s) + # Generate Perlin noise + for x in range(800): + for y in range(600): + noise = pnoise2( + x / 10, + y / 10, + octaves=4, + persistence=persistence + m / 10, + lacunarity=lacunarity, + repeatx=1024, + repeaty=1024, + base=base, + ) + if noise > 0.2: + pygame.draw.rect(screen, (0, 255, 0), (x, y, 1, 1)) + elif noise > 0: + pygame.draw.rect(screen, (0, 128, 0), (x, y, 1, 1)) + else: + pygame.draw.rect(screen, (0, 64, 0), (x, y, 1, 1)) + + # Flip the display + pygame.display.flip() + +# Done! Time to quit. +pygame.quit() diff --git a/gpt-menu.py b/gpt-menu.py new file mode 100644 index 0000000..12c26fe --- /dev/null +++ b/gpt-menu.py @@ -0,0 +1,85 @@ +import pygame + +# Initialize Pygame +pygame.init() + +EVENTS = [] +# Set screen size +screen = pygame.display.set_mode((800, 600)) + +# Set title +pygame.display.set_caption("My RPG Game") + +# Load font +font = pygame.font.Font(None, 30) + +# Define button class +class Button: + def __init__(self, text, x, y, w, h, on_click=lambda: ...): + self.text = text + self.x = x + self.y = y + self.w = w + self.h = h + self.on_click = on_click + + def draw(self, surface): + pygame.draw.rect(surface, (255, 255, 255), (self.x, self.y, self.w, self.h)) + label = font.render(self.text, True, (0, 0, 0)) + label_rect = label.get_rect() + label_rect.center = (self.x + self.w / 2, self.y + self.h / 2) + surface.blit(label, label_rect) + for event in EVENTS: + if event.type == pygame.MOUSEBUTTONDOWN: + if self.is_clicked(event.pos): + self.on_click() + # elif quit_button.is_clicked(event.pos): + # running = False + # if self.is_clicked: + # self.on_click() + + def is_clicked(self, pos): + if pos[0] > self.x and pos[0] < self.x + self.w: + if pos[1] > self.y and pos[1] < self.y + self.h: + return True + return False + + +# Create buttons +start_button = Button( + "Start Game", 300, 300, 200, 50, lambda: print("start this thing") +) +running = True + + +def stop(): + global running + running = False + + +quit_button = Button("Quit Game", 300, 400, 200, 50, stop) + +# Main loop +while running: + EVENTS = pygame.event.get() + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Check for button clicks + + # Draw background + screen.fill((0, 0, 0)) + + # Draw title + title = font.render("Creeper Adventure", True, (255, 255, 255)) + screen.blit(title, (250, 200)) + + # Draw buttons + start_button.draw(screen) + quit_button.draw(screen) + + pygame.display.update() + +# Quit Pygame +pygame.quit() diff --git a/gradients.py b/gradients.py new file mode 100644 index 0000000..4834b3b --- /dev/null +++ b/gradients.py @@ -0,0 +1,42 @@ +from noise import pnoise2 +import pygame +import random + +# Initialize pygame +pygame.init() + +# Set the size of the window +width, height = 800, 600 +screen = pygame.display.set_mode((width, height)) + +# Create a list of 3 random blues +blues = [] +for i in range(3): + blues.append( + (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + ) + +# Create a perlin noise surface +noise_surface = pygame.Surface((width, height)) +for x in range(width): + for y in range(height): + # Calculate the perlin noise value + noise_value = pnoise2(x / 100, y / 100) + # Map the noise value to a color + color_index = int(noise_value * (len(blues) - 1)) + color = blues[color_index] + # Set the color of the pixel + noise_surface.set_at((x, y), color) + +# Blit the noise surface to the screen +screen.blit(noise_surface, (0, 0)) + +# Update the display +pygame.display.flip() + +# Keep the window open until it is closed +while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + exit() diff --git a/grass.png b/grass.png new file mode 100644 index 0000000000000000000000000000000000000000..894be7d2fda1bfa7ef7e7ba3867c17e634f1bf07 GIT binary patch literal 3673 zcmZ`*Wjq{?<34OUrcHN@sT1E5)6+9Grt>)FCTDy z?fd@!|2*~iJg=V@PweYg>SV->!~g(*OjASE;BPAaCqn$cUIy`80su(T0*y?43~c?_ zJ-pn(kav#kJ^>z%?2i5rFaY2`caZ+pcTa{~?E!>jM9d(V$>$rENL{WQl*C?nPb^Jt z#o3#u@qGLN0n80O{P{W*dQI|zfI4r($e{I_gonli^b2$E*D)`+kGZEun*@izmgL$! zvQFD>Z|zZAHv_+~gCnw--UY#zG!)i1fTO?HAd1yMS#mV&H|fSAeCf+bQP!=CKP5VV zH>kg5$7(ZkL`kDJCVng1bHY6I1a*NsLL7XVYSOp28TSocdlrS+p15rRe&~|!4`Tk_ zzi2!X@E}8r)!IUw^Qv#PB^+%*owhfT`)5Pd?ajMq<4ag*Wikfk=Of82o;pOdEAR>4 z9hG2o12HBxlgd z!V?}rb#D8a+J*r?KIAUA%zSP;ECVm{_3b6L+b)@d*{T7FDv4noI8wuOR_# zbjyaB#fhn@P-%=n%O?ZDlsE6I7A#xxrm-mZe|yqU^$S|%EH~$)_Ij3qhlQBc_iVpH z3pl_V`Y*G6l?$JOvmgUQUx!*(=1v7Gn#Vt;n!LRTpHsS!bh#w+%E<>)xFAhuF#SN6 zEV=;3pJmyqJ*3?hHSEGjK%_5nHr0*rRkGnw)!Gv^pImT00$9O`kFYhP{|vXeieD&h z&R__M%)BK8<&WMgBms#Rl@9e5mXzEkE%op4HH=F@-|v3TG}~Zzy{1s-m?LZx-kt1d z2j0Exs0e|JZIoP=^>eKp*9v3KX;EjVUViw%wt@Z14=j7nEd@sMH+1s(nCBZ*gx99MgD}c{zb1G6#N7w=Y7MF8m12xPuHYxUw9^?0LWX?mHgA+qywujy0Eax_C)m zH4MJ4z$Tif705QBFf5&R71TPG;w1C5l#+5hP~+!Dh71`YVrAAD zq+#StB`~&H1RcI-I+ea4IE@ZuV+`xWX_o+yNf)G8C8t=`Wu9-Nt55t8t~ado_Dw@? z`WQT!2HN(ILDgD`cFD$0&S8v^g}jrzcxw}INtDbn)#7Peag9@WM)Xf1ns0#+wdnZD zAweG0v;M+~Ir_Q&w(If4GTFkhk3y%TPp8SANJ2MKR(rm)$)ME-{0Y0-ib*edfQdnp z&}N+=t)nJq55BoZeF2@mIluKjvAB$0+(W}-NAtG#FLIQx{5G0li@LjF?s9`2$3p(@ z(20-p^}OVw1_uqqDutvsw)~0#(&oyXqR2RIZYy zdaI#Sym?I9Bs3T@eWyF@ynZ^tq{<$Gz}?c?>mSorDvuq* z%Jx)#r;nVUQU5=#`+MKd`(D{vWYaMuzxwm3c*ht4nzzS;#|IH^B zrAvHSg9kdWD}4$2#@e85q`xmd>n*I+Nc?P~?^@L>fBc9^x8$|mnX~+k4vz>icS)VR z(|Z~Xg@xsv4aPiIH#8iiN|7YDbr|*&>}J#-t~&>7+Q)IgDeAOlV14C)>pngM&nz*j^x($|Low_lQRc+U8hacEoVIKd^EA90=zcsL1~#3hS(Hf&cOhQrz3 zlBl2iBDi2ON+OsGuH2b<*6d-&{e52X$KI0@lOxg%0I^>D7mPCZuW45ix7#0Wa}{d# z5D2-Z3`i{o)%bqeH3s3Bw535k#%ol!Xh#B5V&Bg)Aaqa9)g3UMT4E+EXW;SYV;Mwx zFgv+O%+9FOb`_ZVcG1#7tyeAUhw;x$Y@jQW}TZlbJgSnqULZ^bpiA}z!4qGYY1 zSNlR_`LrE7VI)ATES-+7Ed064sE0uBN9y@fgVP!s|Ls>(-5)I8$yd=Z2J5*0Cd`@J z{8raf>92)1*r{cbw)4bd>MLw5N$A(!hrnhoiMHf0H{p(z1(@5kz~;HsX+ymTSKEu( zNl<*k09rvs5GJd}Vs4)5SOGbZ&s-ZnlbYfftxwa0q{r>@k(9l#(=k6H8aOmHN=7e%O}vv%Y2!H6|)jHUh@lEhEY;3lVlX7LWuT%?l&Hg+B z9b<7;0H}z{HuuxuPRx9J{}M8dRD>0V2tb}4APW_FD?evHe=(uV(pj*rN*}8-pBZ1{ zvl=8jJGu4U+?w`frOFFS5X?>6{BvR*Y;%rXr&%(ku!rL{vM^)}w+C`Eo@lPqUUWc7 zP+NSNWLT>1utg?m6uc9Xlv^*Y*U@f&C^e3(6cePF6dCdVHcY;WF#JYmki60@9yla{7 znqu2csw4E(k;05?yP9=s)wuNX)4&hkvFhjdk>9}+R~c<=hF2t5RCVcX<0bR64%@of z3V&^^iesxf&lxaP`Yw84;KYgdYs5nEpBJf9ITRVzp0mNK*bn{p?V*{U@&DkokeELj zV1~=gi})r*w5RCu$76VP1e8p9TZf7n_;X&PT-?0#FA({SpjO9VcLx+u%@TwGk3QRn zarEGq^gwgy1%=RnGkdJ_O*(uS$m-IMvcz z-0zleoSS3cmvgI47f5ERnBs5PlrWaMF-hl)K)?-Z7(3 z5KRKgD#T0g3CYB+1=O5>b?R{_+Y|m#;B)|gbg(B3iH|YBQK1NlAxr@IQ&Wn6_0uR9 zBUt8B#?J|78ZNF5AKdbywT-)wUa`V=e{_YP6V`;%6ZxWa(Lo3$gEl$+Ag$$QTKD3g z`y-!L;m>@N^0AaZGkM1DUKF45Tf`ALRi5iVYEya*DXQ(V&poH>`! zW}=8=R}Ri#1;_pzj+hcc;nM*p&+Fo?Fu8rnW8!OahaV#B*5reI<_qnyB}8aqdFDIe zCuu!AHAGZ_e0IwMB7`1E?$X_+agx%_GQNy67g<^JGBPh4aSbfqjcKgzZ7BX3n>J>) z%w@YraTp`}M%D7z?#BU>7YuJqv5ZJ6*8Gl{6J;b9DS+!phA0-S7nprKu9?tWKXbF2 z);qmZ+crgmn}&2|kRwu~tSOzFw^!~dmkG$@HFguHAh(*YA*Z;c^4G8ZP^?c6)_oST p0 MOVESIDEWAYSFREQ: + if movingLeft and isValidPosition(board, fallingPiece, adjX=-1): + fallingPiece['x'] -= 1 + elif movingRight and isValidPosition(board, fallingPiece, adjX=1): + fallingPiece['x'] += 1 + lastMoveSidewaysTime = time.time() + + if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1): + fallingPiece['y'] += 1 + lastMoveDownTime = time.time() + + # let the piece fall if it is time to fall + if time.time() - lastFallTime > fallFreq: + # see if the piece has landed + if not isValidPosition(board, fallingPiece, adjY=1): + # falling piece has landed, set it on the board + addToBoard(board, fallingPiece) + score += removeCompleteLines(board) + level, fallFreq = calculateLevelAndFallFreq(score) + fallingPiece = None + else: + # piece did not land, just move the piece down + fallingPiece['y'] += 1 + lastFallTime = time.time() + + # drawing everything on the screen + DISPLAYSURF.fill(BGCOLOR) + drawBoard(board) + drawStatus(score, level) + drawNextPiece(nextPiece) # Here + if fallingPiece != None: + drawPiece(fallingPiece) + + pygame.display.update() + FPSCLOCK.tick(FPS) + + +def makeTextObjs(text, font, color): + surf = font.render(text, True, color) + return surf, surf.get_rect() + + +def terminate(): + pygame.quit() + sys.exit() + + +def checkForKeyPress(): + # Go through event queue looking for a KEYUP event. + # Grab KEYDOWN events to remove them from the event queue. + checkForQuit() + + for event in pygame.event.get([KEYDOWN, KEYUP]): + if event.type == KEYDOWN: + continue + return event.key + return None + + +def showTextScreen(text): + # This function displays large text in the + # center of the screen until a key is pressed. + # Draw the text drop shadow + titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTSHADOWCOLOR) + titleRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2)) + DISPLAYSURF.blit(titleSurf, titleRect) + + # Draw the text + titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR) + titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3) + DISPLAYSURF.blit(titleSurf, titleRect) + + # Draw the additional "Press a key to play." text. + pressKeySurf, pressKeyRect = makeTextObjs( + 'Press a key to play.', BASICFONT, TEXTCOLOR) + pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100) + DISPLAYSURF.blit(pressKeySurf, pressKeyRect) + + while checkForKeyPress() == None: + pygame.display.update() + FPSCLOCK.tick() + + +def checkForQuit(): + for event in pygame.event.get(QUIT): # get all the QUIT events + terminate() # terminate if any QUIT events are present + for event in pygame.event.get(KEYUP): # get all the KEYUP events + if event.key == K_ESCAPE: + terminate() # terminate if the KEYUP event was for the Esc key + pygame.event.post(event) # put the other KEYUP event objects back + + +def calculateLevelAndFallFreq(score): + # Based on the score, return the level the player is on and + # how many seconds pass until a falling piece falls one space. + level = int(score / 10) + 1 + fallFreq = 0.27 - (level * 0.02) + return level, fallFreq + + +def getNewPiece(): + # return a random new piece in a random rotation and color + shape = random.choice(list(PIECES.keys())) + newPiece = {'shape': shape, + 'rotation': random.randint(0, len(PIECES[shape]) - 1), + 'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2), + 'y': -2, # start it above the board (i.e. less than 0) + 'color': random.randint(0, len(COLORS) - 1)} + return newPiece + + +def addToBoard(board, piece): + # fill in the board based on piece's location, shape, and rotation + for x in range(TEMPLATEWIDTH): + for y in range(TEMPLATEHEIGHT): + if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK: + board[x + piece['x']][y + piece['y']] = piece['color'] + + +def getBlankBoard(): + # create and return a new blank board data structure + board = [] + for i in range(BOARDWIDTH): + board.append([BLANK] * BOARDHEIGHT) + return board + + +def isOnBoard(x, y): + return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT + + +def isValidPosition(board, piece, adjX=0, adjY=0): + # Return True if the piece is within the board and not colliding + for x in range(TEMPLATEWIDTH): + for y in range(TEMPLATEHEIGHT): + isAboveBoard = y + piece['y'] + adjY < 0 + if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK: + continue + if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY): + return False + if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK: + return False + return True + + +def isCompleteLine(board, y): + # Return True if the line filled with boxes with no gaps. + for x in range(BOARDWIDTH): + if board[x][y] == BLANK: + return False + return True + + +def removeCompleteLines(board): + # Remove any completed lines on the board, move everything above them down, and return the number of complete lines. + numLinesRemoved = 0 + y = BOARDHEIGHT - 1 # start y at the bottom of the board + while y >= 0: + if isCompleteLine(board, y): + # Remove the line and pull boxes down by one line. + for pullDownY in range(y, 0, -1): + for x in range(BOARDWIDTH): + board[x][pullDownY] = board[x][pullDownY - 1] + # Set very top line to blank. + for x in range(BOARDWIDTH): + board[x][0] = BLANK + numLinesRemoved += 1 + # Note on the next iteration of the loop, y is the same. + # This is so that if the line that was pulled down is also + # complete, it will be removed. + else: + y -= 1 # move on to check next row up + return numLinesRemoved + + +def convertToPixelCoords(boxx, boxy): + # Convert the given xy coordinates of the board to xy + # coordinates of the location on the screen. + return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE)) + + +def drawBox(boxx, boxy, color, pixelx=None, pixely=None): + # draw a single box (each tetromino piece has four boxes) + # at xy coordinates on the board. Or, if pixelx & pixely + # are specified, draw to the pixel coordinates stored in + # pixelx & pixely (this is used for the "Next" piece). + if color == BLANK: + return + if pixelx == None and pixely == None: + pixelx, pixely = convertToPixelCoords(boxx, boxy) + pygame.draw.rect(DISPLAYSURF, COLORS[color], + (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1)) + pygame.draw.rect( + DISPLAYSURF, LIGHTCOLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 4, BOXSIZE - 4)) + + +def drawBoard(board): + # draw the border around the board + pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7, + (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5) + + # fill the background of the board + pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN, + BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT)) + # draw the individual boxes on the board + for x in range(BOARDWIDTH): + for y in range(BOARDHEIGHT): + drawBox(x, y, board[x][y]) + + +def drawStatus(score, level): + # draw the score text + scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR) + scoreRect = scoreSurf.get_rect() + scoreRect.topleft = (WINDOWWIDTH - 150, 20) + DISPLAYSURF.blit(scoreSurf, scoreRect) + + # draw the level text + levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR) + levelRect = levelSurf.get_rect() + levelRect.topleft = (WINDOWWIDTH - 150, 50) + DISPLAYSURF.blit(levelSurf, levelRect) + + +def drawPiece(piece, pixelx=None, pixely=None): + shapeToDraw = PIECES[piece['shape']][piece['rotation']] + if pixelx == None and pixely == None: + # if pixelx & pixely hasn't been specified, use the location stored in the piece data structure + pixelx, pixely = convertToPixelCoords(piece['x'], piece['y']) + + # draw each of the boxes that make up the piece + for x in range(TEMPLATEWIDTH): + for y in range(TEMPLATEHEIGHT): + if shapeToDraw[y][x] != BLANK: + drawBox(None, None, piece['color'], pixelx + + (x * BOXSIZE), pixely + (y * BOXSIZE)) + + +def drawNextPiece(piece): + # draw the "next" text + nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR) + nextRect = nextSurf.get_rect() + nextRect.topleft = (WINDOWWIDTH - 120, 80) + DISPLAYSURF.blit(nextSurf, nextRect) + # draw the "next" piece + drawPiece(piece, pixelx=WINDOWWIDTH - 120, pixely=100) + + +if __name__ == '__main__': + main() diff --git a/tmp.py b/tmp.py new file mode 100644 index 0000000..a82b9ca --- /dev/null +++ b/tmp.py @@ -0,0 +1,41 @@ +import noise +import pygame + +# Initialize pygame +pygame.init() + +# Set up the drawing window +screen = pygame.display.set_mode([800, 600]) + +# Run until the user asks to quit +running = True +while running: + + # Did the user click the window close button? + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Fill the background with black + screen.fill((0, 0, 0)) + + # Draw stars + for i in range(2000): + x = int(noise.pnoise1(i / 10.0, octaves=4) * 800) + y = int(noise.pnoise1(i / 10.0 + 1000, octaves=4) * 600) + pygame.draw.circle(screen, (255, 255, 255), (x, y), 2) + + # Draw the moon + pygame.draw.circle(screen, (180, 180, 180), (400, 5600 - 200), 5000, 0) # moon + + # Draw craters on the surface of the moon (masked by the shape of the moon) + for i in range(20): + x = int(noise.pnoise1(i / 10.0, octaves=4) * 800) + y = int(noise.pnoise1(i / 10.0 + 1000, octaves=4) * 600) + pygame.draw.circle(screen, (100, 100, 100), (x, y), 20, 0) + + # Flip the display + pygame.display.flip() + +# Done! Time to quit. +pygame.quit() diff --git a/wall.png b/wall.png new file mode 100644 index 0000000000000000000000000000000000000000..a81bb686aa0e12414dbb72aea3e77bd6c972d4ac GIT binary patch literal 5347 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 BZpr`v literal 0 HcmV?d00001