Skip to content

Instantly share code, notes, and snippets.

@Aakash67

Aakash67/run.py Secret

Created March 27, 2025 15:44
Minecraft with Pygame by Gemini 2.5
import pygame
import sys
import random
import math
import array # For generating sound buffers
# --- Constants ---
SCREEN_WIDTH = 960
SCREEN_HEIGHT = 720
BLOCK_SIZE = 32
CAMERA_SPEED = 6 # Pixels per frame
WORLD_WIDTH_BLOCKS = 150 # World width in number of blocks
WORLD_HEIGHT_BLOCKS = 80 # World height in number of blocks
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
SKY_BLUE = (135, 206, 250)
# Block Base Colors (Used for generating textures)
GRASS_BASE = (34, 177, 76)
DIRT_BASE = (139, 69, 19)
STONE_BASE = (128, 128, 128)
HIGHLIGHT_COLOR = (255, 255, 0, 100) # Yellow, semi-transparent alpha
# Block Types (ID -> Name)
BLOCK_TYPES = {
1: "Grass",
2: "Dirt",
3: "Stone",
}
# --- Pygame Setup ---
pygame.init()
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512) # Standard init
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PyCraft 2D - Place[LMB]/Break[RMB] - Select[1,2,3] - Move[WASD]")
clock = pygame.time.Clock()
try:
font = pygame.font.Font(None, 30) # Default Pygame font
except Exception as e:
print(f"Warning: Could not load default font. Text UI might not work. Error: {e}")
font = None # Handle missing font gracefully
# --- Asset Generation ---
block_textures = {}
highlight_surface = None
def generate_block_texture(base_color, detail_variation=25, detail_density=0.2):
"""Generates a simple noisy texture for a block."""
texture = pygame.Surface((BLOCK_SIZE, BLOCK_SIZE))
texture.fill(base_color)
num_details = int((BLOCK_SIZE * BLOCK_SIZE) * detail_density)
for _ in range(num_details):
# Add slightly darker/lighter pixels
mod_r = random.randint(-detail_variation, detail_variation)
mod_g = random.randint(-detail_variation, detail_variation)
mod_b = random.randint(-detail_variation, detail_variation)
detail_color = (
max(0, min(255, base_color[0] + mod_r)),
max(0, min(255, base_color[1] + mod_g)),
max(0, min(255, base_color[2] + mod_b))
)
px = random.randint(0, BLOCK_SIZE - 1)
py = random.randint(0, BLOCK_SIZE - 1)
texture.set_at((px, py), detail_color)
# Add a simple border
pygame.draw.rect(texture, BLACK, (0, 0, BLOCK_SIZE, BLOCK_SIZE), 1)
return texture
print("Generating textures...")
try:
block_textures[1] = generate_block_texture(GRASS_BASE, detail_variation=20, detail_density=0.3)
# Add a greener top to grass
pygame.draw.rect(block_textures[1], (50, 200, 80), (0, 0, BLOCK_SIZE, BLOCK_SIZE // 5))
pygame.draw.rect(block_textures[1], BLACK, (0, 0, BLOCK_SIZE, BLOCK_SIZE), 1) # Re-apply border
block_textures[2] = generate_block_texture(DIRT_BASE, detail_variation=15, detail_density=0.25)
block_textures[3] = generate_block_texture(STONE_BASE, detail_variation=30, detail_density=0.4)
# Highlight Surface
highlight_surface = pygame.Surface((BLOCK_SIZE, BLOCK_SIZE), pygame.SRCALPHA) # SRCALPHA allows transparency
highlight_surface.fill(HIGHLIGHT_COLOR)
print("Textures generated successfully.")
except Exception as e:
print(f"Error generating textures: {e}. Block rendering might fail.")
# Basic fallback - without textures, drawing needs adjustment, but let's try anyway
block_textures = {} # Clear partially generated textures on error
# --- Sound Generation ---
sound_place = None
sound_break = None
def generate_beep(freq, duration_ms, vol=0.1):
"""Generates a pygame Sound object with a sine wave."""
try:
sample_rate = pygame.mixer.get_init()[0]
num_channels = pygame.mixer.get_init()[1]
bits = abs(pygame.mixer.get_init()[2])
max_amp = (2**(bits - 1)) - 1
num_samples = int(sample_rate * duration_ms / 1000.0)
if bits == 16:
pack_format = "<h" # Little-endian signed short
elif bits == 8:
pack_format = "<b" # Little-endian signed char
else:
print(f"Unsupported bit depth for sound generation: {bits}")
return None # Cannot generate sound
buf = array.array(pack_format, (0 for _ in range(num_samples * num_channels)))
for i in range(num_samples):
val = int(vol * max_amp * math.sin(2 * math.pi * freq * i / sample_rate))
# Write to all channels (e.g., stereo)
for c in range(num_channels):
buf[i * num_channels + c] = val
sound = pygame.mixer.Sound(buffer=buf)
return sound
except Exception as e:
print(f"Error generating sound: {e}")
return None
print("Generating sounds...")
try:
# Generate simple beep sounds
sound_place = generate_beep(880, 80, vol=0.08) # Higher pitch, short duration
sound_break = generate_beep(440, 120, vol=0.1) # Lower pitch, slightly longer
if sound_place and sound_break:
print("Sounds generated successfully.")
else:
print("Failed to generate some or all sounds.")
except Exception as e:
print(f"Could not initialize or generate sounds: {e}. Sound disabled.")
sound_place = None
sound_break = None
# --- Game State ---
world_data = {} # Dictionary: (col, row) -> block_type_id
camera_x = (WORLD_WIDTH_BLOCKS * BLOCK_SIZE) // 2 - SCREEN_WIDTH // 2 # Start camera in the middle horizontally
camera_y = (WORLD_HEIGHT_BLOCKS * BLOCK_SIZE) // 3 - SCREEN_HEIGHT // 2 # Start camera slightly above the middle vertically
current_block_type = 1 # Start with Grass
# --- Initial World Generation ---
print("Generating initial world terrain...")
for col in range(WORLD_WIDTH_BLOCKS):
# Simple terrain: Grass top layer, 3 Dirt, rest Stone down to world bottom
surface_level = WORLD_HEIGHT_BLOCKS // 2 + int(math.sin(col * 0.1) * 3) # Add slight waviness
# Ensure surface level is within reasonable bounds
surface_level = max(5, min(WORLD_HEIGHT_BLOCKS - 10, surface_level))
world_data[(col, surface_level)] = 1 # Grass
for depth in range(1, 4):
row = surface_level + depth
if row < WORLD_HEIGHT_BLOCKS:
world_data[(col, row)] = 2 # Dirt
for row in range(surface_level + 4, WORLD_HEIGHT_BLOCKS):
world_data[(col, row)] = 3 # Stone
print("World generation complete.")
# --- Helper Functions ---
def world_to_screen(world_x, world_y, cam_x, cam_y):
"""Converts world coordinates to screen coordinates."""
screen_x = world_x - cam_x
screen_y = world_y - cam_y
return int(screen_x), int(screen_y)
def screen_to_grid(screen_x, screen_y, cam_x, cam_y):
"""Converts screen coordinates to world grid coordinates."""
world_x = screen_x + cam_x
world_y = screen_y + cam_y
grid_col = world_x // BLOCK_SIZE
grid_row = world_y // BLOCK_SIZE
return grid_col, grid_row
# --- Game Loop ---
running = True
while running:
dt = clock.tick(60) / 1000.0 # Delta time in seconds, helps smooth movement if needed
# --- Event Handling ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# Block selection
if event.key == pygame.K_1:
current_block_type = 1
elif event.key == pygame.K_2:
current_block_type = 2
elif event.key == pygame.K_3:
current_block_type = 3
# Add more elif blocks here for keys 4, 5, etc. if you add more block types
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_screen_x, mouse_screen_y = event.pos
grid_col, grid_row = screen_to_grid(mouse_screen_x, mouse_screen_y, camera_x, camera_y)
# Optional: Check if click is within world boundaries if you want defined edges
# if 0 <= grid_col < WORLD_WIDTH_BLOCKS and 0 <= grid_row < WORLD_HEIGHT_BLOCKS:
if event.button == 1: # Left-click to place
# Place block only if the space is empty
if (grid_col, grid_row) not in world_data:
world_data[(grid_col, grid_row)] = current_block_type
if sound_place:
sound_place.play()
elif event.button == 3: # Right-click to break
# Break block if it exists
if (grid_col, grid_row) in world_data:
del world_data[(grid_col, grid_row)]
if sound_break:
sound_break.play()
# --- Continuous Key Presses (Camera Movement) ---
keys = pygame.key.get_pressed()
if keys[pygame.K_w] or keys[pygame.K_UP]:
camera_y -= CAMERA_SPEED
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
camera_y += CAMERA_SPEED
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
camera_x -= CAMERA_SPEED
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
camera_x += CAMERA_SPEED
# Optional: Clamp camera to world boundaries if desired
# camera_x = max(0, min(camera_x, WORLD_WIDTH_BLOCKS * BLOCK_SIZE - SCREEN_WIDTH))
# camera_y = max(0, min(camera_y, WORLD_HEIGHT_BLOCKS * BLOCK_SIZE - SCREEN_HEIGHT))
# --- Drawing ---
# Background
screen.fill(SKY_BLUE)
# Calculate visible grid range
start_col = camera_x // BLOCK_SIZE
end_col = start_col + (SCREEN_WIDTH // BLOCK_SIZE) + 2 # Add a buffer of 1-2 blocks
start_row = camera_y // BLOCK_SIZE
end_row = start_row + (SCREEN_HEIGHT // BLOCK_SIZE) + 2 # Add a buffer
# Draw Blocks (Visible ones only)
if block_textures: # Only draw if textures were loaded/generated
for row in range(start_row, end_row):
for col in range(start_col, end_col):
block_id = world_data.get((col, row))
if block_id:
texture = block_textures.get(block_id)
if texture: # Check if texture exists for this ID
world_px = col * BLOCK_SIZE
world_py = row * BLOCK_SIZE
screen_px, screen_py = world_to_screen(world_px, world_py, camera_x, camera_y)
# Basic Culling: Only blit if block is potentially on screen
if -BLOCK_SIZE < screen_px < SCREEN_WIDTH and -BLOCK_SIZE < screen_py < SCREEN_HEIGHT:
screen.blit(texture, (screen_px, screen_py))
else:
if font: # Display error message if textures failed
error_text = font.render("Error: Textures failed to load/generate.", True, BLACK)
screen.blit(error_text, (SCREEN_WIDTH // 2 - error_text.get_width() // 2, SCREEN_HEIGHT // 2))
# Draw Mouse Hover Highlight
if highlight_surface:
mouse_screen_x, mouse_screen_y = pygame.mouse.get_pos()
hover_col, hover_row = screen_to_grid(mouse_screen_x, mouse_screen_y, camera_x, camera_y)
highlight_world_x = hover_col * BLOCK_SIZE
highlight_world_y = hover_row * BLOCK_SIZE
highlight_screen_x, highlight_screen_y = world_to_screen(highlight_world_x, highlight_world_y, camera_x, camera_y)
# Ensure highlight is drawn within screen bounds if needed (optional)
# if 0 <= highlight_screen_x < SCREEN_WIDTH - BLOCK_SIZE and 0 <= highlight_screen_y < SCREEN_HEIGHT - BLOCK_SIZE:
screen.blit(highlight_surface, (highlight_screen_x, highlight_screen_y))
# --- Draw UI ---
if font:
try:
# Display current block type
block_name = BLOCK_TYPES.get(current_block_type, "Unknown")
ui_text_line1 = f"Selected: {block_name} [{current_block_type}]"
ui_surf1 = font.render(ui_text_line1, True, BLACK, WHITE) # Black text on white background box
screen.blit(ui_surf1, (10, 10))
# Display Coordinates (optional)
mouse_sx, mouse_sy = pygame.mouse.get_pos()
h_col, h_row = screen_to_grid(mouse_sx, mouse_sy, camera_x, camera_y)
coord_text = f"Coords: ({h_col}, {h_row})"
coord_surf = font.render(coord_text, True, BLACK, WHITE)
screen.blit(coord_surf, (10, 40))
# Draw small preview of current block
preview_texture = block_textures.get(current_block_type)
if preview_texture:
preview_size = BLOCK_SIZE * 1.5 # Slightly larger preview
scaled_preview = pygame.transform.scale(preview_texture, (int(preview_size), int(preview_size)))
preview_x = 15
preview_y = 70
# Simple border for preview
pygame.draw.rect(screen, WHITE, (preview_x - 2, preview_y - 2, scaled_preview.get_width() + 4, scaled_preview.get_height() + 4))
pygame.draw.rect(screen, BLACK, (preview_x - 2, preview_y - 2, scaled_preview.get_width() + 4, scaled_preview.get_height() + 4), 1)
screen.blit(scaled_preview, (preview_x, preview_y))
except Exception as e:
print(f"Error drawing UI: {e}") # Avoid crashing if UI drawing fails
# --- Update Display ---
pygame.display.flip()
# --- Cleanup ---
print("Exiting game.")
pygame.quit()
sys.exit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment