Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # A Primitive Example of a tower defense game similar to clash royale written in pygame
- # click on the grid to place objects that will find the nearest enemy tower
- import pygame
- import sys
- import os
- import math
- import random
- import heapq # For A* pathfinding
- # Initialize pygame
- pygame.init()
- # Constants
- GRID_SIZE = 18 # Increased from 15 to 18 for more play area
- CELL_SIZE = 35 # Slightly smaller cells to fit on screen
- MARGIN = 4
- WINDOW_SIZE = (GRID_SIZE * (CELL_SIZE + MARGIN) + MARGIN,
- GRID_SIZE * (CELL_SIZE + MARGIN) + MARGIN)
- # Colors
- BLACK = (0, 0, 0)
- WHITE = (255, 255, 255)
- GRAY = (200, 200, 200)
- GREEN = (0, 255, 0)
- BLUE = (100, 100, 255)
- RED = (255, 100, 100)
- YELLOW = (255, 255, 100)
- PURPLE = (200, 100, 255)
- RIVER_BLUE = (80, 170, 255)
- LIGHT_GRASS = (140, 200, 100)
- DARK_GRASS = (120, 180, 80)
- BRIDGE_BROWN = (150, 100, 50)
- # Create the grid
- grid = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
- # Create river position (horizontal line in middle of the grid)
- river_row_start = 8 # Adjusted for larger grid
- river_row_end = 10 # Making river slightly wider (2 rows)
- # Define bridge positions (aligned with princess towers)
- left_bridge_col = 3 # Left bridge aligned with left princess towers
- right_bridge_col = 13 # Right bridge adjusted for wider grid
- bridge_width = 2 # Width of each bridge
- # Define tower placement areas - TOP (Player 1 side)
- # Main tower (Top): Centered in the grid, 3x3
- tower1_top_x, tower1_top_y = 8, 1 # King tower position
- tower1_top_size = 3
- # Princess towers (Top): On each side of the main tower, one row down from king tower
- tower2_top_x, tower2_top_y = 3, 2 # Left princess tower (one row down from king tower)
- tower2_top_size = 2
- tower3_top_x, tower3_top_y = 13, 2 # Right princess tower (one row down from king tower)
- tower3_top_size = 2
- # Define tower placement areas - BOTTOM (Player 2 side)
- # Main tower (Bottom): Centered in the grid, 3x3
- tower1_bottom_x, tower1_bottom_y = 8, 14 # King tower position
- tower1_bottom_size = 3
- # Princess towers (Bottom): On each side of the main tower, aligned with king tower top
- tower2_bottom_x, tower2_bottom_y = 3, 14 # Left princess tower (aligned with king tower top)
- tower2_bottom_size = 2
- tower3_bottom_x, tower3_bottom_y = 13, 14 # Right princess tower (aligned with king tower top)
- tower3_bottom_size = 2
- # Create separate lists to store different tower positions
- king_tower_positions = []
- princess_tower_positions = []
- blue_tower_positions = [] # To track our own towers for pathfinding
- # Add king tower positions (red)
- for row in range(tower1_top_y, tower1_top_y + tower1_top_size):
- for col in range(tower1_top_x, tower1_top_x + tower1_top_size):
- king_tower_positions.append((row, col))
- # Add princess tower positions (red)
- for row in range(tower2_top_y, tower2_top_y + tower2_top_size):
- for col in range(tower2_top_x, tower2_top_x + tower2_top_size):
- princess_tower_positions.append((row, col))
- for row in range(tower3_top_y, tower3_top_y + tower3_top_size):
- for col in range(tower3_top_x, tower3_top_x + tower3_top_size):
- princess_tower_positions.append((row, col))
- # Add blue tower positions (used to prevent placement and for pathfinding)
- # King tower (blue)
- for row in range(tower1_bottom_y, tower1_bottom_y + tower1_bottom_size):
- for col in range(tower1_bottom_x, tower1_bottom_x + tower1_bottom_size):
- blue_tower_positions.append((row, col))
- # Princess towers (blue)
- for row in range(tower2_bottom_y, tower2_bottom_y + tower2_bottom_size):
- for col in range(tower2_bottom_x, tower2_bottom_x + tower2_bottom_size):
- blue_tower_positions.append((row, col))
- for row in range(tower3_bottom_y, tower3_bottom_y + tower3_bottom_size):
- for col in range(tower3_bottom_x, tower3_bottom_x + tower3_bottom_size):
- blue_tower_positions.append((row, col))
- # Combine all red tower positions (will be used for display purposes)
- red_tower_positions = king_tower_positions + princess_tower_positions
- # Troop class to handle movement and rendering
- class Troop:
- def __init__(self, row, col, color=(0, 0, 255)):
- self.row = row
- self.col = col
- # Store position as floats for smooth movement
- self.exact_x = col
- self.exact_y = row
- self.color = color
- self.path = []
- self.speed = 1.0 # Cells per second
- self.size = CELL_SIZE - 10
- self.active = True
- # Find path to nearest red tower
- self.find_path()
- def find_path(self):
- """Find path to nearest red tower using A* pathfinding"""
- # First check if princess towers are still available
- if princess_tower_positions:
- # Find closest princess tower
- closest_princess = min(princess_tower_positions,
- key=lambda t: math.sqrt((self.row - t[0])**2 + (self.col - t[1])**2))
- princess_dist = math.sqrt((self.row - closest_princess[0])**2 +
- (self.col - closest_princess[1])**2)
- # Find closest king tower
- closest_king = min(king_tower_positions,
- key=lambda t: math.sqrt((self.row - t[0])**2 + (self.col - t[1])**2))
- king_dist = math.sqrt((self.row - closest_king[0])**2 +
- (self.col - closest_king[1])**2)
- # Choose the closer of the two
- if princess_dist <= king_dist + 2: # Slight preference for princess towers
- target = closest_princess
- print("Targeting princess tower at", target)
- else:
- target = closest_king
- print("Targeting king tower at", target)
- else:
- # All princess towers destroyed, target king tower
- target = min(king_tower_positions,
- key=lambda t: math.sqrt((self.row - t[0])**2 + (self.col - t[1])**2))
- print("No princess towers left, targeting king tower at", target)
- # A* pathfinding - include blue towers as obstacles to path around
- self.path = astar_pathfinding((self.row, self.col), target, include_blue_towers=True)
- if not self.path:
- print("No path found!")
- self.active = False
- else:
- # Remove the starting position from the path
- if self.path and len(self.path) > 0:
- self.path.pop(0)
- def update(self, dt):
- """Update troop position"""
- if not self.active or not self.path:
- return
- # Get the next position to move toward
- next_pos = self.path[0]
- # Calculate direction
- dx = next_pos[1] - self.exact_x
- dy = next_pos[0] - self.exact_y
- # Normalize direction if not 0
- distance = math.sqrt(dx**2 + dy**2)
- if distance > 0:
- dx /= distance
- dy /= distance
- # Move towards the next position
- move_distance = self.speed * dt
- if distance <= move_distance:
- # We've reached the next position
- self.exact_x = next_pos[1]
- self.exact_y = next_pos[0]
- self.path.pop(0)
- # Check if we've reached the target
- if not self.path:
- print("Reached target!")
- self.active = False
- else:
- # Move towards the next position
- self.exact_x += dx * move_distance
- self.exact_y += dy * move_distance
- # Update integer position
- self.row = int(self.exact_y)
- self.col = int(self.exact_x)
- def draw(self, screen):
- """Draw the troop on the screen"""
- if not self.active:
- return
- # Calculate screen position
- x = self.exact_x * (CELL_SIZE + MARGIN) + MARGIN + CELL_SIZE // 2
- y = self.exact_y * (CELL_SIZE + MARGIN) + MARGIN + CELL_SIZE // 2
- # Draw the troop
- pygame.draw.circle(screen, self.color, (int(x), int(y)), self.size // 2)
- # Add a border
- pygame.draw.circle(screen, (0, 0, 200), (int(x), int(y)), self.size // 2, 2)
- # Pathfinding functions
- def is_valid_cell(row, col, include_blue_towers=False):
- """Check if a cell is valid for pathfinding"""
- # Check if within grid bounds
- if not (0 <= row < GRID_SIZE and 0 <= col < GRID_SIZE):
- return False
- # Check if it's a river (not a bridge)
- if river_row_start <= row < river_row_end:
- is_bridge = ((left_bridge_col <= col < left_bridge_col + bridge_width) or
- (right_bridge_col <= col < right_bridge_col + bridge_width))
- if not is_bridge:
- return False
- # Check if it's a blue tower position (only for pathfinding, not for placement)
- if include_blue_towers and (row, col) in blue_tower_positions:
- return False
- return True
- def get_neighbors(node, include_blue_towers=False):
- """Get valid neighboring cells for pathfinding"""
- row, col = node
- neighbors = []
- # Check all 4 adjacent cells
- directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
- for dr, dc in directions:
- new_row, new_col = row + dr, col + dc
- if is_valid_cell(new_row, new_col, include_blue_towers):
- neighbors.append((new_row, new_col))
- return neighbors
- def heuristic(a, b):
- """Calculate the manhattan distance heuristic"""
- return abs(a[0] - b[0]) + abs(a[1] - b[1])
- def astar_pathfinding(start, goal, include_blue_towers=False):
- """A* pathfinding algorithm to find a path from start to goal"""
- frontier = []
- heapq.heappush(frontier, (0, start))
- came_from = {start: None}
- cost_so_far = {start: 0}
- while frontier:
- _, current = heapq.heappop(frontier)
- if current == goal:
- break
- for next_node in get_neighbors(current, include_blue_towers):
- new_cost = cost_so_far[current] + 1
- if next_node not in cost_so_far or new_cost < cost_so_far[next_node]:
- cost_so_far[next_node] = new_cost
- priority = new_cost + heuristic(next_node, goal)
- heapq.heappush(frontier, (priority, next_node))
- came_from[next_node] = current
- # Reconstruct the path
- if goal not in came_from:
- return []
- path = [goal]
- while path[-1] != start:
- path.append(came_from[path[-1]])
- path.reverse()
- return path
- # List to store troops
- troops = []
- # Set up the display
- screen = pygame.display.set_mode(WINDOW_SIZE)
- pygame.display.set_caption("Clash Royale Style Grid")
- clock = pygame.time.Clock()
- # Animation variables
- time_passed = 0
- # Main game loop
- running = True
- while running:
- # Get delta time for smooth animations
- dt = clock.get_time() / 1000.0 # Time in seconds
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- elif event.type == pygame.MOUSEBUTTONDOWN:
- # Get position of the mouse click
- pos = pygame.mouse.get_pos()
- # Calculate the row and column clicked
- column = pos[0] // (CELL_SIZE + MARGIN)
- row = pos[1] // (CELL_SIZE + MARGIN)
- # Make sure it's within the grid
- if 0 <= row < GRID_SIZE and 0 <= column < GRID_SIZE:
- # Check if it's not a river (except bridges)
- valid_position = True
- if river_row_start <= row < river_row_end:
- is_bridge = ((left_bridge_col <= column < left_bridge_col + bridge_width) or
- (right_bridge_col <= column < right_bridge_col + bridge_width))
- if not is_bridge:
- valid_position = False
- # Don't place troops on the top (enemy) side
- if row < river_row_start:
- valid_position = False
- # Don't place troops on blue towers
- if (row, column) in blue_tower_positions:
- valid_position = False
- if valid_position:
- # Create a new troop
- new_troop = Troop(row, column)
- troops.append(new_troop)
- print(f"Placed a troop at ({row}, {column})")
- else:
- print("Invalid placement position")
- # Update troops
- for troop in troops:
- troop.update(dt)
- # Remove inactive troops
- troops = [troop for troop in troops if troop.active]
- # Update animation time
- time_passed += dt
- # Clear screen
- screen.fill(BLACK)
- # Draw background terrain (alternating grass pattern)
- for row in range(GRID_SIZE):
- for column in range(GRID_SIZE):
- # Calculate the position for each cell
- x = column * (CELL_SIZE + MARGIN) + MARGIN
- y = row * (CELL_SIZE + MARGIN) + MARGIN
- # Alternate between light and dark grass for a checkerboard effect
- if (row + column) % 2 == 0:
- grass_color = LIGHT_GRASS
- else:
- grass_color = DARK_GRASS
- # Draw the terrain cell
- pygame.draw.rect(screen, grass_color, [x, y, CELL_SIZE, CELL_SIZE])
- # Draw the river crossing the middle
- for row in range(river_row_start, river_row_end):
- for column in range(GRID_SIZE):
- x = column * (CELL_SIZE + MARGIN) + MARGIN
- y = row * (CELL_SIZE + MARGIN) + MARGIN
- # Check if this is a bridge position
- is_bridge = ((left_bridge_col <= column < left_bridge_col + bridge_width) or
- (right_bridge_col <= column < right_bridge_col + bridge_width))
- if is_bridge:
- # Draw bridge
- pygame.draw.rect(screen, BRIDGE_BROWN, [x, y, CELL_SIZE, CELL_SIZE])
- # Add bridge planks
- for i in range(3):
- plank_y = y + (i+1) * CELL_SIZE // 4
- pygame.draw.line(screen, (100, 70, 30),
- (x, plank_y), (x + CELL_SIZE, plank_y), 2)
- else:
- # Draw river
- pygame.draw.rect(screen, RIVER_BLUE, [x, y, CELL_SIZE, CELL_SIZE])
- # Add wave animation to the river
- wave_height = 3 * math.sin(column / 2 + time_passed * 2)
- pygame.draw.line(screen, (150, 220, 255),
- (x, y + CELL_SIZE // 2 + wave_height),
- (x + CELL_SIZE, y + CELL_SIZE // 2 - wave_height), 2)
- # Add some bubbles/foam to the river (avoid bridges)
- for i in range(5):
- bubble_x = (int(time_passed * 100 + i * 100) % WINDOW_SIZE[0])
- bubble_y = river_row_start * (CELL_SIZE + MARGIN) + MARGIN + CELL_SIZE // 2
- # Check if bubble is on a bridge
- bubble_col = bubble_x // (CELL_SIZE + MARGIN)
- is_on_bridge = ((left_bridge_col <= bubble_col < left_bridge_col + bridge_width) or
- (right_bridge_col <= bubble_col < right_bridge_col + bridge_width))
- if not is_on_bridge:
- bubble_size = random.randint(2, 4)
- pygame.draw.circle(screen, (200, 230, 255), (bubble_x, bubble_y), bubble_size)
- # Draw the tower areas and grid cells
- for row in range(GRID_SIZE):
- for column in range(GRID_SIZE):
- # Skip if this is a river cell (not a bridge)
- is_bridge = False
- if river_row_start <= row < river_row_end:
- is_bridge = ((left_bridge_col <= column < left_bridge_col + bridge_width) or
- (right_bridge_col <= column < right_bridge_col + bridge_width))
- if not is_bridge:
- continue
- # Calculate the position for each cell
- x = column * (CELL_SIZE + MARGIN) + MARGIN
- y = row * (CELL_SIZE + MARGIN) + MARGIN
- # Determine if this cell is in a tower area
- is_tower_cell = False
- # Check top towers (red team)
- if (tower1_top_x <= column < tower1_top_x + tower1_top_size and
- tower1_top_y <= row < tower1_top_y + tower1_top_size):
- # Main tower (top) - King tower
- color = RED
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (255, 50, 50), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, RED, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (255, 0, 0), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- elif (tower2_top_x <= column < tower2_top_x + tower2_top_size and
- tower2_top_y <= row < tower2_top_y + tower2_top_size):
- # Princess tower 1 (top)
- color = RED
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (255, 50, 50), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, RED, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (255, 0, 0), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- elif (tower3_top_x <= column < tower3_top_x + tower3_top_size and
- tower3_top_y <= row < tower3_top_y + tower3_top_size):
- # Princess tower 2 (top)
- color = RED
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (255, 50, 50), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, RED, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (255, 0, 0), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- # Check bottom towers (blue team)
- elif (tower1_bottom_x <= column < tower1_bottom_x + tower1_bottom_size and
- tower1_bottom_y <= row < tower1_bottom_y + tower1_bottom_size):
- # Main tower (bottom) - King tower
- color = BLUE
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (50, 50, 255), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, BLUE, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (0, 0, 255), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- elif (tower2_bottom_x <= column < tower2_bottom_x + tower2_bottom_size and
- tower2_bottom_y <= row < tower2_bottom_y + tower2_bottom_size):
- # Princess tower 1 (bottom)
- color = BLUE
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (50, 50, 255), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, BLUE, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (0, 0, 255), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- elif (tower3_bottom_x <= column < tower3_bottom_x + tower3_bottom_size and
- tower3_bottom_y <= row < tower3_bottom_y + tower3_bottom_size):
- # Princess tower 2 (bottom)
- color = BLUE
- is_tower_cell = True
- if grid[row][column] == 1:
- # Draw activated tower
- pygame.draw.rect(screen, (50, 50, 255), [x, y, CELL_SIZE, CELL_SIZE])
- else:
- # Draw tower with border
- pygame.draw.rect(screen, BLUE, [x, y, CELL_SIZE, CELL_SIZE])
- pygame.draw.rect(screen, (0, 0, 255), [x+2, y+2, CELL_SIZE-4, CELL_SIZE-4], 2)
- # Draw normal grid cells (if not a tower)
- elif grid[row][column] == 1:
- # Draw an activated regular cell (troop placement indicator)
- pygame.draw.rect(screen, GREEN, [x, y, CELL_SIZE, CELL_SIZE], 2)
- # Draw all troops
- for troop in troops:
- troop.draw(screen)
- # Update the display
- pygame.display.flip()
- # Limit to 60 frames per second
- clock.tick(60)
- # Clean up
- pygame.quit()
- sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement