diff --git a/circlewars-game/README.md b/circlewars-game/README.md
new file mode 100644
index 0000000..71687f7
--- /dev/null
+++ b/circlewars-game/README.md
@@ -0,0 +1,56 @@
+#
๐งฑโ๏ธ CIRCLE WARS โ๏ธ๐งฑ
+
+
+
+**Circle Wars** is a fast-paced, neon-glowing arcade shooter where you control a glowing circle battling waves of relentless enemies. Dodge and weave the full-screen arena, blast enemies with shots and survive as long as possible while leveling up!
+
+---
+
+## ๐ฎ Gameplay Overview
+
+You control a glowing blue circle navigating the arena. Your goal is to:
+
+* ๐ซ Shoot enemies using keyboard, mouse, or controller
+* ๐ง Defeat increasingly faster enemies that chase you relentlessly
+* ๐ก๏ธ Dodge collisions โ one hit and itโs game over
+* โซ Survive waves to level up and increase enemy speed and challenge
+
+---
+
+## ๐น๏ธ Controls
+
+### Keyboard
+
+* **Move:** `W`, `A`, `S`, `D`
+* **Shoot:** `Spacebar` or Left Mouse Button
+* **Pause / Unpause:** `Esc`
+* **Quit Game:** `Q` (only when paused)
+
+### Controller (Optional)
+
+* **Move:** Left Stick (supports 8 directions)
+* **Shoot:** `A` button (fires in joystick direction)
+* **Pause / Unpause:** `Start` button
+* **Quit Game:** `X` button (only when paused)
+
+---
+
+## ๐ Features
+
+* Fullscreen neon-glow aesthetic with smooth glowing edges
+* Keyboard, mouse, and controller support with adaptive input handling
+* Directional shooting including diagonal firing with joystick
+* Controller rumble feedback on key events (player death, enemy hit)
+* Increasing difficulty with waves and speed scaling
+* Immersive looping background music and satisfying sound effects
+* Pause screen with tailored instructions based on input method
+
+---
+
+## ๐ Getting Started
+
+Run the game using Python 3 (and Pygame installed):
+
+```bash
+python3 circlewars-game.py
+
diff --git a/circlewars-game/audio_enemydeath.wav b/circlewars-game/audio_enemydeath.wav
new file mode 100644
index 0000000..eb5c459
Binary files /dev/null and b/circlewars-game/audio_enemydeath.wav differ
diff --git a/circlewars-game/audio_go.wav b/circlewars-game/audio_go.wav
new file mode 100644
index 0000000..5dc294e
Binary files /dev/null and b/circlewars-game/audio_go.wav differ
diff --git a/circlewars-game/audio_levelup.wav b/circlewars-game/audio_levelup.wav
new file mode 100644
index 0000000..52854e2
Binary files /dev/null and b/circlewars-game/audio_levelup.wav differ
diff --git a/circlewars-game/audio_music.mp3 b/circlewars-game/audio_music.mp3
new file mode 100644
index 0000000..b3fa656
Binary files /dev/null and b/circlewars-game/audio_music.mp3 differ
diff --git a/circlewars-game/audio_playerdeath.wav b/circlewars-game/audio_playerdeath.wav
new file mode 100644
index 0000000..fc09ec5
Binary files /dev/null and b/circlewars-game/audio_playerdeath.wav differ
diff --git a/circlewars-game/audio_playerfire.wav b/circlewars-game/audio_playerfire.wav
new file mode 100644
index 0000000..02cdc22
Binary files /dev/null and b/circlewars-game/audio_playerfire.wav differ
diff --git a/circlewars-game/circlewars-game.py b/circlewars-game/circlewars-game.py
new file mode 100644
index 0000000..db6034d
--- /dev/null
+++ b/circlewars-game/circlewars-game.py
@@ -0,0 +1,486 @@
+#!/usr/bin/python3
+# Description: blockwars - A simple game written in Python.
+# Usage: python3 blockwars-game.py
+# Author: Justin Oros (improved by ChatGPT)
+# Source: https://github.com/JustinOros
+
+import pygame
+import random
+import time
+import os
+
+# Initialize pygame
+pygame.init()
+
+# Set up full-screen mode
+SCREEN_WIDTH = pygame.display.Info().current_w
+SCREEN_HEIGHT = pygame.display.Info().current_h
+screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.FULLSCREEN)
+pygame.display.set_caption("Block Wars")
+
+# Hide mouse cursor
+pygame.mouse.set_visible(False)
+
+# Define colors
+NEON_BLUE = (0, 200, 255) # Used for UI text
+BLUE = (0, 200, 255) # Player glow blue (same as NEON_BLUE)
+RED = (255, 0, 0)
+DARK_GRAY = (50, 50, 50)
+
+# Define constants
+PLAYER_SIZE = 20
+ENEMY_SIZE = 20
+PROJECTILE_SIZE = 5
+
+# Function to load sound safely
+def load_sound(filename):
+ if os.path.exists(filename):
+ return pygame.mixer.Sound(filename)
+ else:
+ print(f"Warning: {filename} not found. Sound will be disabled.")
+ return None
+
+# Load sounds
+GO_SOUND = load_sound("audio_go.wav")
+FIRE_SOUND = load_sound("audio_playerfire.wav")
+DIE_SOUND = load_sound("audio_playerdeath.wav")
+LEVEL_UP_SOUND = load_sound("audio_levelup.wav")
+ENEMY_DEATH_SOUND = load_sound("audio_enemydeath.wav")
+EXPLOSION_SOUND = load_sound("audio_playerdeath.wav")
+
+# Load background music (pygame.mixer.music)
+if os.path.exists("audio_music.mp3"):
+ try:
+ pygame.mixer.music.load("audio_music.mp3")
+ except Exception as e:
+ print(f"Error loading background music: {e}")
+else:
+ print("Warning: audio_music.mp3 not found. Background music disabled.")
+
+# Set font for score
+font = pygame.font.SysFont(None, 30)
+
+# Initialize the joystick (controller)
+pygame.joystick.init()
+controller = None
+if pygame.joystick.get_count() > 0:
+ controller = pygame.joystick.Joystick(0)
+ controller.init()
+
+# Draw glowing rectangle ONLY (no solid fill) -- KEEP in case you want to revert
+def draw_glow_rect(surf, rect, glow_color, glow_radius):
+ glow_surface = pygame.Surface((rect.width + glow_radius*2, rect.height + glow_radius*2), pygame.SRCALPHA)
+ max_glow = int(glow_radius * 0.6)
+ for i in range(max_glow, 0, -1):
+ alpha = int(255 * (i / max_glow) ** 2)
+ glow_rect = pygame.Rect(
+ glow_radius - i,
+ glow_radius - i,
+ rect.width + i * 2,
+ rect.height + i * 2
+ )
+ pygame.draw.rect(
+ glow_surface,
+ glow_color + (alpha,),
+ glow_rect,
+ border_radius=3,
+ width=2
+ )
+ surf.blit(glow_surface, (rect.x - glow_radius, rect.y - glow_radius))
+
+# NEW: Draw glowing circle ONLY (no solid fill)
+def draw_glow_circle(surf, center, radius, glow_color, glow_radius):
+ glow_surface = pygame.Surface((radius*2 + glow_radius*2, radius*2 + glow_radius*2), pygame.SRCALPHA)
+ max_glow = int(glow_radius * 0.6)
+ for i in range(max_glow, 0, -1):
+ alpha = int(255 * (i / max_glow) ** 2)
+ pygame.draw.circle(
+ glow_surface,
+ glow_color + (alpha,),
+ (radius + glow_radius, radius + glow_radius),
+ radius + i,
+ width=2
+ )
+ surf.blit(glow_surface, (center[0] - radius - glow_radius, center[1] - radius - glow_radius))
+
+# Helper function for circle collision
+def circles_collide(x1, y1, r1, x2, y2, r2):
+ dist_sq = (x1 - x2)**2 + (y1 - y2)**2
+ radius_sum_sq = (r1 + r2)**2
+ return dist_sq <= radius_sum_sq
+
+# Player class
+class Player:
+ def __init__(self):
+ self.x = SCREEN_WIDTH // 2
+ self.y = SCREEN_HEIGHT // 2
+ self.size = PLAYER_SIZE
+ self.speed = 3 # Will be set in game()
+ self.direction = None # Current movement direction vector (dx, dy)
+ self.last_direction = (0, -1) # Default facing up
+
+ def move(self, keys, joystick_axes):
+ move_x, move_y = 0, 0
+
+ # Joystick input with deadzone
+ axis_x = joystick_axes[0]
+ axis_y = joystick_axes[1]
+ deadzone = 0.3
+
+ if abs(axis_x) > deadzone:
+ move_x = self.speed * (1 if axis_x > 0 else -1)
+ if abs(axis_y) > deadzone:
+ move_y = self.speed * (1 if axis_y > 0 else -1)
+
+ # Keyboard input only if joystick inactive this frame
+ if move_x == 0 and move_y == 0:
+ if keys[pygame.K_a]:
+ move_x = -self.speed
+ elif keys[pygame.K_d]:
+ move_x = self.speed
+
+ if keys[pygame.K_w]:
+ move_y = -self.speed
+ elif keys[pygame.K_s]:
+ move_y = self.speed
+
+ self.x += move_x
+ self.y += move_y
+
+ # Screen wrapping
+ if self.x < 0:
+ self.x = SCREEN_WIDTH - self.size
+ elif self.x > SCREEN_WIDTH - self.size:
+ self.x = 0
+ if self.y < 0:
+ self.y = SCREEN_HEIGHT - self.size
+ elif self.y > SCREEN_HEIGHT - self.size:
+ self.y = 0
+
+ # Set current direction vector normalized to -1, 0, or 1 on each axis
+ norm_dx = 0
+ norm_dy = 0
+ if move_x > 0:
+ norm_dx = 1
+ elif move_x < 0:
+ norm_dx = -1
+ if move_y > 0:
+ norm_dy = 1
+ elif move_y < 0:
+ norm_dy = -1
+
+ if norm_dx == 0 and norm_dy == 0:
+ self.direction = None
+ else:
+ self.direction = (norm_dx, norm_dy)
+ self.last_direction = self.direction # Update last known direction
+
+ def draw(self):
+ center = (int(self.x + self.size // 2), int(self.y + self.size // 2))
+ radius = self.size // 2
+ draw_glow_circle(screen, center, radius, BLUE, glow_radius=10)
+
+# Projectile class
+class Projectile:
+ def __init__(self, x, y, direction_vector):
+ self.x = x
+ self.y = y
+ self.size = PROJECTILE_SIZE
+ self.color = BLUE
+ self.speed = 15
+ self.dir_x, self.dir_y = direction_vector
+
+ def move(self):
+ # Normalize direction so diagonal speed isn't faster
+ length = (self.dir_x ** 2 + self.dir_y ** 2) ** 0.5
+ if length == 0:
+ return
+ self.x += self.speed * self.dir_x / length
+ self.y += self.speed * self.dir_y / length
+
+ def draw(self):
+ center = (int(self.x + self.size // 2), int(self.y + self.size // 2))
+ radius = self.size // 2
+ pygame.draw.circle(screen, self.color, center, radius)
+
+# Explosion class
+class Explosion:
+ def __init__(self, x, y, color=RED):
+ self.x = x
+ self.y = y
+ self.size = 50
+ self.lifetime = 10
+ self.color = color
+
+ def draw(self):
+ pygame.draw.circle(screen, self.color, (self.x, self.y), self.size)
+ self.lifetime -= 1
+
+ def is_alive(self):
+ return self.lifetime > 0
+
+# Enemy class
+class Enemy:
+ def __init__(self, speed):
+ edge = random.choice(["top", "bottom", "left", "right"])
+ if edge == "top":
+ self.x = random.randint(0, SCREEN_WIDTH - ENEMY_SIZE)
+ self.y = 0
+ elif edge == "bottom":
+ self.x = random.randint(0, SCREEN_WIDTH - ENEMY_SIZE)
+ self.y = SCREEN_HEIGHT - ENEMY_SIZE
+ elif edge == "left":
+ self.x = 0
+ self.y = random.randint(0, SCREEN_HEIGHT - ENEMY_SIZE)
+ else:
+ self.x = SCREEN_WIDTH - ENEMY_SIZE
+ self.y = random.randint(0, SCREEN_HEIGHT - ENEMY_SIZE)
+ self.size = ENEMY_SIZE
+ self.speed = speed
+
+ def move(self, player_x, player_y):
+ dx = player_x - self.x
+ dy = player_y - self.y
+ dist = max((dx ** 2 + dy ** 2) ** 0.5, 0.001) # Avoid zero division
+ self.x += self.speed * dx / dist
+ self.y += self.speed * dy / dist
+
+ def draw(self):
+ center = (int(self.x + self.size // 2), int(self.y + self.size // 2))
+ radius = self.size // 2
+ draw_glow_circle(screen, center, radius, RED, glow_radius=10)
+
+# Pause screen
+def draw_pause_screen(paused_by_controller):
+ overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
+ overlay.set_alpha(180)
+ overlay.fill((0, 0, 0))
+ screen.blit(overlay, (0, 0))
+
+ pause_font = pygame.font.SysFont(None, 100)
+ instruction_font = pygame.font.SysFont(None, 40)
+ pause_text = pause_font.render("PAUSED", True, NEON_BLUE)
+
+ if paused_by_controller:
+ instruction = "Press START to resume or X to Exit."
+ else:
+ instruction = "Press ESC to resume or Q to Quit."
+
+ instruction_text = instruction_font.render(instruction, True, NEON_BLUE)
+
+ screen.blit(pause_text, (
+ SCREEN_WIDTH // 2 - pause_text.get_width() // 2,
+ SCREEN_HEIGHT // 2 - pause_text.get_height() // 2 - 30
+ ))
+ screen.blit(instruction_text, (
+ SCREEN_WIDTH // 2 - instruction_text.get_width() // 2,
+ SCREEN_HEIGHT // 2 + 30
+ ))
+
+# Helper function to fire projectile
+def fire_projectile(player, projectiles):
+ # Use last_direction if no current direction
+ direction = player.direction if player.direction is not None else player.last_direction
+ if direction != (0, 0):
+ px = player.x + player.size // 2 - PROJECTILE_SIZE // 2
+ py = player.y + player.size // 2 - PROJECTILE_SIZE // 2
+ projectiles.append(Projectile(px, py, direction))
+ if FIRE_SOUND:
+ FIRE_SOUND.play()
+
+# Main game loop
+def game():
+ clock = pygame.time.Clock()
+ player = Player()
+ projectiles = []
+ explosions = []
+ level = 1
+ score = 0
+ enemy_speed = 2
+ enemies = [Enemy(enemy_speed)]
+ player.speed = enemy_speed + 1 # Player slightly faster than enemies
+ running = True
+ paused = False
+ paused_by_controller = False
+ player_dead = False
+ controller_active = False # Start assuming keyboard usage
+
+ def rumble(duration=300, strength=1.0):
+ if controller_active and controller and hasattr(controller, "rumble"):
+ try:
+ controller.rumble(strength, strength, duration)
+ except Exception as e:
+ print(f"Rumble error: {e}")
+
+ # Start background music looping infinitely
+ if pygame.mixer.music.get_busy() == False:
+ try:
+ pygame.mixer.music.play(-1) # -1 means loop indefinitely
+ except Exception as e:
+ print(f"Failed to play background music: {e}")
+
+ if GO_SOUND:
+ GO_SOUND.play()
+
+ while running:
+ screen.fill(DARK_GRAY)
+ keys = pygame.key.get_pressed()
+ joystick_axes = [controller.get_axis(0), controller.get_axis(1)] if controller else [0, 0]
+
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ # Track input method for rumble control
+ if event.type in [pygame.JOYAXISMOTION, pygame.JOYBUTTONDOWN]:
+ controller_active = True
+ elif event.type in [pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN]:
+ controller_active = False
+
+ if event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_ESCAPE:
+ paused = not paused
+ paused_by_controller = False
+ elif paused and event.key == pygame.K_q:
+ running = False
+ elif not paused and event.key == pygame.K_SPACE:
+ fire_projectile(player, projectiles)
+
+ if event.type == pygame.JOYBUTTONDOWN:
+ # Button mapping notes:
+ # 7 or 6 = START buttons (pause/resume)
+ # 2 = X button (quit when paused)
+ # 0 = A button (fire projectile)
+ if event.button == 7 or event.button == 6:
+ paused = not paused
+ paused_by_controller = True
+ elif paused and event.button == 2:
+ running = False
+ elif not paused and event.button == 0:
+ fire_projectile(player, projectiles)
+
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if not paused and event.button == 1: # Left mouse button
+ fire_projectile(player, projectiles)
+
+ # Also check if keyboard movement keys are pressed, set controller_active to False
+ if keys[pygame.K_w] or keys[pygame.K_a] or keys[pygame.K_s] or keys[pygame.K_d]:
+ controller_active = False
+
+ if paused:
+ draw_pause_screen(paused_by_controller)
+ pygame.display.flip()
+ clock.tick(60)
+ continue
+
+ if player_dead:
+ for explosion in explosions[:]:
+ explosion.draw()
+ if not explosion.is_alive():
+ explosions.remove(explosion)
+ pygame.display.flip()
+ clock.tick(60)
+ if not explosions:
+ show_final_score(score)
+ running = False
+ continue
+
+ player.move(keys, joystick_axes)
+
+ for projectile in projectiles[:]:
+ projectile.move()
+ projectile.draw()
+ # Remove projectile if out of screen
+ if (projectile.x < 0 or projectile.x > SCREEN_WIDTH or
+ projectile.y < 0 or projectile.y > SCREEN_HEIGHT):
+ projectiles.remove(projectile)
+
+ for enemy in enemies[:]:
+ enemy.move(player.x, player.y)
+ enemy.draw()
+
+ # Circle collision for player and enemy
+ player_center = (player.x + player.size // 2, player.y + player.size // 2)
+ enemy_center = (enemy.x + enemy.size // 2, enemy.y + enemy.size // 2)
+
+ if circles_collide(player_center[0], player_center[1], player.size // 2,
+ enemy_center[0], enemy_center[1], enemy.size // 2):
+ if not player_dead:
+ if DIE_SOUND:
+ DIE_SOUND.play()
+ rumble(duration=1000, strength=1.0)
+ player_dead = True
+ explosions.append(Explosion(player_center[0], player_center[1], color=BLUE))
+
+ for projectile in projectiles[:]:
+ projectile_center = (projectile.x + projectile.size // 2, projectile.y + projectile.size // 2)
+ if circles_collide(enemy_center[0], enemy_center[1], enemy.size // 2,
+ projectile_center[0], projectile_center[1], projectile.size // 2):
+ explosions.append(Explosion(enemy_center[0], enemy_center[1]))
+ if ENEMY_DEATH_SOUND:
+ ENEMY_DEATH_SOUND.play()
+ rumble(duration=150, strength=0.5)
+ enemies.remove(enemy)
+ projectiles.remove(projectile)
+ score += 100
+ break
+
+ for explosion in explosions[:]:
+ explosion.draw()
+ if not explosion.is_alive():
+ explosions.remove(explosion)
+
+ if not enemies:
+ level += 1
+ if LEVEL_UP_SOUND:
+ LEVEL_UP_SOUND.play()
+
+ # Increase enemy speed every 2 levels, cap at 8
+ if level % 2 == 0 and enemy_speed < 8:
+ enemy_speed += 1
+
+ player.speed = enemy_speed + 1 # Player stays slightly faster
+
+ enemies = [Enemy(enemy_speed) for _ in range(level)]
+
+ player.draw()
+
+ score_text = font.render(f"Score: {score}", True, NEON_BLUE)
+ level_text = font.render(f"Level: {level}", True, NEON_BLUE)
+ screen.blit(score_text, (10, 10))
+ screen.blit(level_text, (SCREEN_WIDTH - level_text.get_width() - 10, 10))
+
+ pygame.display.flip()
+ clock.tick(60)
+
+# Show final score on death
+def show_final_score(score):
+ font_large = pygame.font.SysFont(None, 80)
+ if score == 0:
+ text = "GAME OVER"
+ else:
+ text = f"Final Score: {score}"
+ final_score_text = font_large.render(text, True, RED)
+ screen.fill(DARK_GRAY)
+ screen.blit(final_score_text, (SCREEN_WIDTH // 2 - final_score_text.get_width() // 2,
+ SCREEN_HEIGHT // 2 - final_score_text.get_height() // 2))
+ pygame.display.flip()
+
+ # Instead of time.sleep (which blocks), wait for 2 seconds with event handling
+ wait_start = pygame.time.get_ticks()
+ waiting = True
+ while waiting:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ waiting = False
+ elif event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
+ waiting = False
+ if pygame.time.get_ticks() - wait_start > 2000:
+ waiting = False
+ pygame.time.Clock().tick(30)
+
+if __name__ == "__main__":
+ game()
+
+pygame.quit()
+
diff --git a/circlewars-game/screenshot_circlewars.png b/circlewars-game/screenshot_circlewars.png
new file mode 100644
index 0000000..dc3773a
Binary files /dev/null and b/circlewars-game/screenshot_circlewars.png differ