algorithms

This commit is contained in:
OusmBlueNinja 2025-04-07 11:53:24 -05:00
parent 02e42ad5d0
commit 483643a4c0
4 changed files with 519 additions and 0 deletions

View File

@ -0,0 +1,100 @@
import pygame
# Constants
WIDTH, HEIGHT = 800, 600
CELL_SIZE = 10
GRID_COLS = WIDTH // CELL_SIZE
GRID_ROWS = HEIGHT // CELL_SIZE
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
# Directions: 0=Up, 1=Right, 2=Down, 3=Left
direction_vectors = {
0: (0, -1),
1: (1, 0),
2: (0, 1),
3: (-1, 0)
}
direction_names = {
0: "Up",
1: "Right",
2: "Down",
3: "Left"
}
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Langton's Ant Visualization with Debug Info")
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 18)
# Initialize grid: False means white cell, True means black cell.
grid = [[False for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
# Initialize ant at center of grid.
ant_col = GRID_COLS // 2
ant_row = GRID_ROWS // 2
ant_direction = 0 # Starting direction: Up
steps = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Run several simulation steps per frame.
for _ in range(10):
# Get the current cell's color.
current_color = grid[ant_row][ant_col]
# Langton's Ant rules:
# If on a white cell, flip it to black, turn right.
# If on a black cell, flip it to white, turn left.
if not current_color: # White cell
grid[ant_row][ant_col] = True
ant_direction = (ant_direction + 1) % 4
else: # Black cell
grid[ant_row][ant_col] = False
ant_direction = (ant_direction - 1) % 4
# Move ant forward one cell in the current direction, with wrap-around.
dx, dy = direction_vectors[ant_direction]
ant_col = (ant_col + dx) % GRID_COLS
ant_row = (ant_row + dy) % GRID_ROWS
steps += 1
# Clear screen (fill with white).
screen.fill(WHITE)
# Draw grid: fill black cells.
for row in range(GRID_ROWS):
for col in range(GRID_COLS):
if grid[row][col]:
rect = (col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, BLACK, rect)
# Draw the ant as a red cell.
ant_rect = (ant_col * CELL_SIZE, ant_row * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, RED, ant_rect)
# Debug text (no background, just text):
debug_text1 = font.render(f"Steps: {steps}", True, BLACK)
debug_text2 = font.render(f"Ant Position: ({ant_col}, {ant_row})", True, BLACK)
debug_text3 = font.render(f"Ant Direction: {direction_names[ant_direction]}", True, BLACK)
current_cell_color = "Black" if grid[ant_row][ant_col] else "White"
debug_text4 = font.render(f"Current Cell: {current_cell_color}", True, BLACK)
# Blit debug text in the top-left corner.
screen.blit(debug_text1, (10, 10))
screen.blit(debug_text2, (10, 30))
screen.blit(debug_text3, (10, 50))
screen.blit(debug_text4, (10, 70))
pygame.display.flip()
clock.tick(60)
pygame.quit()

View File

@ -0,0 +1,51 @@
import pygame
import random
# Initialize pygame
pygame.init()
# Screen dimensions
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Chaos Game - Sierpinski Triangle")
# Define the triangle vertices
vertex1 = (WIDTH // 2, 50)
vertex2 = (50, HEIGHT - 50)
vertex3 = (WIDTH - 50, HEIGHT - 50)
vertices = [vertex1, vertex2, vertex3]
# Start with a random point
current_point = (random.randint(0, WIDTH), random.randint(0, HEIGHT))
# Set up the clock for controlling frame rate
clock = pygame.time.Clock()
# Fill background color (black)
screen.fill((0, 0, 0))
# Main loop
running = True
while running:
# Handle events (close window)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Plot several points per frame for a faster build-up
for _ in range(1):
# Choose a random vertex
chosen_vertex = random.choice(vertices)
# Compute the midpoint between current point and chosen vertex
current_point = ((current_point[0] + chosen_vertex[0]) // 2,
(current_point[1] + chosen_vertex[1]) // 2)
# Draw the point (using a small rectangle as a pixel)
screen.fill((255, 255, 255), (current_point[0], current_point[1], 1, 1))
# Update display
pygame.display.flip()
# Limit to 60 frames per second
clock.tick()
pygame.quit()

View File

@ -0,0 +1,194 @@
import pygame
import random
import math
# Screen dimensions and simulation parameters
WIDTH, HEIGHT = 800, 600
NUM_BOIDS = 30
MAX_SPEED = 4
MAX_FORCE = 0.05
NEIGHBOR_RADIUS = 50
SEPARATION_RADIUS = 20
# Debug mode flag (toggle with "D")
DEBUG_MODE = True
class Boid:
def __init__(self, x, y):
self.position = pygame.math.Vector2(x, y)
angle = random.uniform(0, 2 * math.pi)
self.velocity = pygame.math.Vector2(math.cos(angle), math.sin(angle))
self.velocity.scale_to_length(random.uniform(1, MAX_SPEED))
self.acceleration = pygame.math.Vector2(0, 0)
def edges(self):
# Wrap-around behavior for screen edges
if self.position.x > WIDTH:
self.position.x = 0
elif self.position.x < 0:
self.position.x = WIDTH
if self.position.y > HEIGHT:
self.position.y = 0
elif self.position.y < 0:
self.position.y = HEIGHT
def update(self):
# Update velocity and position
self.velocity += self.acceleration
if self.velocity.length() > MAX_SPEED:
self.velocity.scale_to_length(MAX_SPEED)
self.position += self.velocity
self.acceleration *= 0
def apply_force(self, force):
self.acceleration += force
def flock(self, boids):
# Calculate steering vectors from the three flocking rules
alignment = self.align(boids)
cohesion = self.cohere(boids)
separation = self.separate(boids)
# Weighing the forces
alignment *= 1.0
cohesion *= 0.8
separation *= 1.5
self.apply_force(alignment)
self.apply_force(cohesion)
self.apply_force(separation)
def align(self, boids):
steering = pygame.math.Vector2(0, 0)
total = 0
for other in boids:
if other != self and self.position.distance_to(other.position) < NEIGHBOR_RADIUS:
steering += other.velocity
total += 1
if total > 0:
steering /= total
if steering.length() > 0:
steering.scale_to_length(MAX_SPEED)
steering -= self.velocity
if steering.length() > MAX_FORCE:
steering.scale_to_length(MAX_FORCE)
return steering
def cohere(self, boids):
steering = pygame.math.Vector2(0, 0)
total = 0
for other in boids:
if other != self and self.position.distance_to(other.position) < NEIGHBOR_RADIUS:
steering += other.position
total += 1
if total > 0:
steering /= total
steering -= self.position
if steering.length() > 0:
steering.scale_to_length(MAX_SPEED)
steering -= self.velocity
if steering.length() > MAX_FORCE:
steering.scale_to_length(MAX_FORCE)
return steering
def separate(self, boids):
steering = pygame.math.Vector2(0, 0)
total = 0
for other in boids:
distance = self.position.distance_to(other.position)
if other != self and distance < SEPARATION_RADIUS:
diff = self.position - other.position
if distance > 0:
diff /= distance # weight by distance
steering += diff
total += 1
if total > 0:
steering /= total
if steering.length() > 0:
steering.scale_to_length(MAX_SPEED)
steering -= self.velocity
if steering.length() > MAX_FORCE:
steering.scale_to_length(MAX_FORCE)
return steering
def draw(self, screen):
# Calculate orientation for drawing the boid as a triangle
angle = self.velocity.angle_to(pygame.math.Vector2(1, 0))
# Triangle points: front and two rear corners
p1 = self.position + pygame.math.Vector2(10, 0).rotate(-angle)
p2 = self.position + pygame.math.Vector2(-5, 5).rotate(-angle)
p3 = self.position + pygame.math.Vector2(-5, -5).rotate(-angle)
pygame.draw.polygon(screen, (255, 255, 255), [p1, p2, p3])
# If debug is enabled, draw additional information (velocity vector)
if DEBUG_MODE:
end_pos = self.position + self.velocity * 10
pygame.draw.line(screen, (0, 255, 0), self.position, end_pos, 2)
def draw_debug_panel(screen, boids, clock):
# Draw a semi-transparent panel in the top-left corner
font = pygame.font.SysFont("Arial", 14)
# Collect debug information
fps_text = font.render(f"FPS: {int(clock.get_fps())}", True, (255, 255, 255))
boids_text = font.render(f"Boids: {len(boids)}", True, (255, 255, 255))
max_speed_text = font.render(f"Max Speed: {MAX_SPEED}", True, (255, 255, 255))
neighbor_text = font.render(f"Neighbor Radius: {NEIGHBOR_RADIUS}", True, (255, 255, 255))
separation_text = font.render(f"Separation Radius: {SEPARATION_RADIUS}", True, (255, 255, 255))
# Blit debug info on the panel
screen.blit(fps_text, (10, 10))
screen.blit(boids_text, (10, 30))
screen.blit(max_speed_text, (10, 50))
screen.blit(neighbor_text, (10, 70))
screen.blit(separation_text, (10, 90))
# Additionally, display details of the first boid for deeper debugging
if boids:
boid = boids[0]
pos_text = font.render(f"Boid0 Pos: ({int(boid.position.x)}, {int(boid.position.y)})", True, (255, 255, 255))
vel_text = font.render(f"Boid0 Vel: ({int(boid.velocity.x)}, {int(boid.velocity.y)})", True, (255, 255, 255))
screen.blit(pos_text, (10, 110))
screen.blit(vel_text, (10, 130))
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Boids Simulation with Debug Menu")
clock = pygame.time.Clock()
# Create boids at random positions
boids = [Boid(random.randint(0, WIDTH), random.randint(0, HEIGHT)) for _ in range(NUM_BOIDS)]
global DEBUG_MODE
debug_toggle_key = pygame.K_d # Press D to toggle the debug menu
running = True
while running:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == debug_toggle_key:
DEBUG_MODE = not DEBUG_MODE
# Update and draw each boid
for boid in boids:
boid.flock(boids)
boid.update()
boid.edges()
boid.draw(screen)
# Draw debug panel if debug mode is on
if DEBUG_MODE:
draw_debug_panel(screen, boids, clock)
pygame.display.flip()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,174 @@
import pygame
import random
import math
# Initialize pygame
pygame.init()
# Screen dimensions and panel sizes
WIDTH, HEIGHT = 800, 600
PANEL_HEIGHT = 150 # Bottom panel for sliders
SIM_HEIGHT = HEIGHT - PANEL_HEIGHT # Simulation area height
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Particle Simulation with Adjustable Variables")
clock = pygame.time.Clock()
# Define a simple Slider class
class Slider:
def __init__(self, x, y, w, h, min_val, max_val, init_val, label):
self.x = x
self.y = y
self.w = w
self.h = h
self.min_val = min_val
self.max_val = max_val
self.value = init_val
self.label = label
self.dragging = False
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if self.get_knob_rect().collidepoint(event.pos):
self.dragging = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
self.dragging = False
elif event.type == pygame.MOUSEMOTION:
if self.dragging:
mx = event.pos[0]
# Clamp mx inside slider range
relative = (mx - self.x) / self.w
relative = max(0, min(1, relative))
self.value = self.min_val + relative * (self.max_val - self.min_val)
def get_knob_rect(self):
# Determine knob position along slider track
relative = (self.value - self.min_val) / (self.max_val - self.min_val)
knob_x = self.x + relative * self.w
knob_width = 10
knob_height = self.h
return pygame.Rect(knob_x - knob_width // 2, self.y, knob_width, knob_height)
def draw(self, surface, font):
# Draw slider track (a thin rectangle)
track_rect = pygame.Rect(self.x, self.y + self.h // 2 - 2, self.w, 4)
pygame.draw.rect(surface, (200, 200, 200), track_rect)
# Draw knob
knob_rect = self.get_knob_rect()
pygame.draw.rect(surface, (100, 100, 100), knob_rect)
# Draw label and current value (as plain text)
label_surface = font.render(f"{self.label}: {self.value:.2f}", True, (0, 0, 0))
surface.blit(label_surface, (self.x, self.y - 20))
# Particle class
class Particle:
def __init__(self, pos, velocity, lifetime, size, color=(255, 255, 255)):
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(velocity)
self.lifetime = lifetime
self.size = size
self.initial_lifetime = lifetime
self.color = color
def update(self, gravity):
self.vel.y += gravity
self.pos += self.vel
self.lifetime -= 1
def is_dead(self):
return self.lifetime <= 0
def draw(self, surface, glow_intensity):
# Draw glow effect if glow_intensity is greater than zero
if glow_intensity > 0:
# Create a temporary surface for glow with per-pixel alpha
glow_surface = pygame.Surface((self.size*6, self.size*6), pygame.SRCALPHA)
glow_center = self.size * 3
# Draw several concentric circles for the glow effect
for i in range(1, 6):
# Alpha decreases as the circle radius increases
alpha = max(0, int(255 * (glow_intensity / 10) * (1 - i/6)))
radius = int(self.size + i * 2)
pygame.draw.circle(glow_surface,
(self.color[0], self.color[1], self.color[2], alpha),
(glow_center, glow_center),
radius)
# Blit the glow surface with additive blending
surface.blit(glow_surface, (self.pos.x - self.size*3, self.pos.y - self.size*3), special_flags=pygame.BLEND_RGBA_ADD)
# Draw the particle itself
pygame.draw.circle(surface, self.color, (int(self.pos.x), int(self.pos.y)), int(self.size))
# Create sliders for each variable, arranged in two rows within the bottom panel.
font = pygame.font.SysFont("Arial", 16)
sliders = []
# Row 1: Emission Rate, Speed, Lifetime
sliders.append(Slider(x=20, y=SIM_HEIGHT + 10, w=200, h=20, min_val=0, max_val=50, init_val=5, label="Emission Rate"))
sliders.append(Slider(x=240, y=SIM_HEIGHT + 10, w=200, h=20, min_val=0, max_val=10, init_val=5, label="Speed"))
sliders.append(Slider(x=460, y=SIM_HEIGHT + 10, w=200, h=20, min_val=10, max_val=300, init_val=100, label="Lifetime"))
# Row 2: Size, Gravity, Glow
sliders.append(Slider(x=20, y=SIM_HEIGHT + 50, w=200, h=20, min_val=1, max_val=20, init_val=5, label="Size"))
sliders.append(Slider(x=240, y=SIM_HEIGHT + 50, w=200, h=20, min_val=0, max_val=2, init_val=0.1, label="Gravity"))
sliders.append(Slider(x=460, y=SIM_HEIGHT + 50, w=200, h=20, min_val=0, max_val=10, init_val=3, label="Glow"))
# List to hold all particles
particles = []
# Emitter position (center of simulation area)
emitter = pygame.math.Vector2(WIDTH // 2, SIM_HEIGHT // 2)
# Main loop
running = True
while running:
# Process events (for quitting and slider events)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for slider in sliders:
slider.handle_event(event)
# Get simulation variables from slider values
emission_rate = sliders[0].value # particles per frame
speed = sliders[1].value # initial speed magnitude
lifetime = sliders[2].value # lifetime in frames
size = sliders[3].value # particle size (radius)
gravity = sliders[4].value # gravity acceleration
glow = sliders[5].value # glow intensity
# Spawn new particles (round emission_rate to an integer count)
for _ in range(int(emission_rate)):
# Spawn at the emitter with a random direction
angle = random.uniform(0, 2 * math.pi)
velocity = pygame.math.Vector2(math.cos(angle), math.sin(angle)) * speed
particles.append(Particle(emitter, velocity, lifetime, size))
# Update particles and remove dead ones
for particle in particles[:]:
particle.update(gravity)
if particle.is_dead():
particles.remove(particle)
# Clear simulation area (fill with black)
screen.fill((0, 0, 0), rect=pygame.Rect(0, 0, WIDTH, SIM_HEIGHT))
# Optionally fill the control area with a light color (or leave it as is)
screen.fill((220, 220, 220), rect=pygame.Rect(0, SIM_HEIGHT, WIDTH, PANEL_HEIGHT))
# Draw all particles
for particle in particles:
particle.draw(screen, glow_intensity=glow)
# Draw slider controls in the bottom panel
for slider in sliders:
slider.draw(screen, font)
# Draw debug text (plain text, no background) at the top-left corner of the simulation area
debug_text = (f"Particles: {len(particles)} | Emission Rate: {emission_rate:.2f} | Speed: {speed:.2f} | "
f"Lifetime: {lifetime:.2f} | Size: {size:.2f} | Gravity: {gravity:.2f} | Glow: {glow:.2f}")
debug_surface = font.render(debug_text, True, (255, 255, 255))
screen.blit(debug_surface, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()