Skip to content
Snippets Groups Projects
Commit e0d1b702 authored by Mohannad Alaya's avatar Mohannad Alaya
Browse files

CTF_Done

parent baa2bbc1
Branches main
No related tags found
No related merge requests found
Showing
with 605 additions and 0 deletions
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
ai.py 0 → 100644
import math
import pymunk
from pymunk import Vec2d
import gameobjects
from collections import defaultdict, deque
import images
import pygame
# NOTE: use only 'map0' during development!
MIN_ANGLE_DIF = math.radians(6) # input degrees
def angle_between_vectors(vec1, vec2):
""" Since Vec2d operates in a cartesian coordinate space we have to
convert the resulting vector to get the correct angle for our space.
"""
vec = vec1 - vec2
vec = vec.perpendicular()
return vec.angle
def periodic_difference_of_angles(angle1, angle2):
""" Return the periodic difference between two angles. """
return (angle1 - angle2)%(2*math.pi)
class Ai:
""" A simple ai that finds the shortest path to the target using
a breadth first search. Also capable of shooting other tanks and or wooden
boxes. """
def __init__(self, tank,game_objects_list, tanks_list,space,currentmap):
""" Ai constructor """
self.tank = tank
self.game_objects_list = game_objects_list
self.tanks_list = tanks_list
self.space = space
self.currentmap = currentmap
self.flag = None
self.MAX_X = currentmap.width - 1
self.MAX_Y = currentmap.height - 1
self.metal_path = False
self.path = deque()
self.move_cycle = self.move_cycle_gen()
self.update_grid_pos()
self.last_shot = pygame.time.get_ticks()
def ray_cast(self):
"""Function for detecting ray cast for ai shoot function"""
angle = self.tank.body.angle + math.pi/2 # adapt to cartesian angles
x_bullet = self.tank.body.position[0]+0.5*math.cos(angle)
y_bullet = self.tank.body.position[1]+0.5*math.sin(angle)
ray_beg = (x_bullet, y_bullet) # same as bullet start
ray_max1 = x_bullet + 100 * math.cos(angle)
ray_max2 = y_bullet + 100 * math.sin(angle)
ray_max = (ray_max1, ray_max2) # projectile line for bullet
return self.space.segment_query_first(ray_beg, ray_max, 0, \
pymunk.ShapeFilter())
def update_grid_pos(self):
""" This should only be called in the beginning,
or at the end of a move_cycle. """
self.grid_pos = self.get_tile_of_position(self.tank.body.position)
def maybe_shoot(self):
""" Makes a raycast query in front of the tank. If another tank
or a wooden box is found, then we shoot. """
res = self.ray_cast()
if hasattr(res, "shape"):
# check for the collision types
if isinstance(res.shape.collision_type, int):
col_type = res.shape.collision_type
if col_type == 1 or (col_type == 3 and \
res.shape.parent.destructable) or col_type == 2:
ai_bullet = self.tank.shoot()
if ai_bullet != None:
##unfair Ai (times 1.3)
ai_bullet.body.velocity = ai_bullet.body.velocity*1.3
self.game_objects_list.append(ai_bullet)
def decide(self):
""" Main decision function that gets called on every tick of the game.
"""
self.maybe_shoot()
next(self.move_cycle)
def find_shortest_path(self):
""" A simple Breadth First Search using integer coordinates as our
nodes. Edges are calculated as we go, using an external
function. """
source_node = self.grid_pos.int_tuple
queue = deque() # neighbors (new nodes)
visited = set()
shortest_path = []
queue.append((source_node, shortest_path))
while queue: # all tiles in worst case...
queue = self.astar_heu(queue) # rearrange according to heu!
curr_tile, curr_shortest = queue.popleft()
if self.get_target_tile() == curr_tile:
curr_shortest.append(curr_tile)
shortest_path = curr_shortest
return deque(shortest_path[1:])
neighbours = self.get_tile_neighbors(curr_tile)
for neighbor in neighbours:
neighbor = neighbor.int_tuple
if neighbor not in visited:
queue.append((neighbor,curr_shortest + [curr_tile]))
visited.add(neighbor)
def astar_heu(self, input_queue):
""" Heurstic function for calculating a path the
flag using the shortest distance. """
queue = deque(input_queue)
astar_queue = deque()
curr_min_dist = 1+Vec2d(self.tank.body.position\
).get_distance(self.get_target_tile())
while queue:
curr_path =queue.popleft()
temp_dist =Vec2d(curr_path[0]).get_distance(self.get_target_tile())
if temp_dist < curr_min_dist:
astar_queue.appendleft(curr_path)
curr_min_dist = temp_dist
else:
astar_queue.append(curr_path)
return astar_queue
def move_cycle_gen(self):
""" A generator that iteratively goes through all the required steps
to move to our goal. """
while True:
path = self.find_shortest_path()
if path == None:
self.metal_path = True
yield
continue
if len(path) == 0:
yield
continue
next_coord = path.popleft()+Vec2d(0.5, 0.5)
yield
# turn()
angle_diff = angle_between_vectors(self.tank.body.position,\
next_coord)
angle_next = periodic_difference_of_angles(\
self.tank.body.angle,angle_diff)
self.tank.stop_moving()
self.tank.body.angle = self.tank.body.angle % (2*math.pi)
# Must check when tank is tilted downwords!
if (angle_next < -math.pi) or (math.pi > angle_next > 0):
self.tank.turn_left()
else:
self.tank.turn_right()
# correct_angle()
while abs(angle_next) > MIN_ANGLE_DIF:
angle_next = periodic_difference_of_angles(\
self.tank.body.angle,angle_diff)
yield
self.tank.stop_turning()
self.tank.accelerate()
yield
# correct_pos()
current_dist = self.tank.body.position.get_distance(next_coord)
temp_next_dist = current_dist
while (current_dist >= temp_next_dist):
self.update_grid_pos()
# set new next tile when we reach the target!
temp_next_dist = current_dist + 1
yield
move_cycle = self.move_cycle_gen()
def get_target_tile(self):
""" Returns position of the flag if we don't have it. If we do
have the flag, return the position of our home base. """
if self.tank.flag is not None:
x, y = self.tank.start_position
else:
self.get_flag() # Ensure that we have initialized it.
x, y = self.flag.x, self.flag.y
return Vec2d(int(x), int(y))
def get_flag(self):
""" This has to be called to get the flag, since we don't know
where it is when the Ai object is initialized. """
if self.flag is None:
# Find the flag in the game objects list
for obj in self.game_objects_list:
if isinstance(obj, gameobjects.Flag):
self.flag = obj
break
return self.flag
def get_tile_of_position(self, position_vector):
""" Converts and returns the float position of our tank to
an integer position."""
x, y = position_vector
return Vec2d(int(x), int(y))
def get_tile_neighbors(self, coord_vec):
""" Returns all bordering grid squares of the input coordinate.
A bordering square is only considered accessible if it is grass
or a wooden box. """
curr_tile = self.get_tile_of_position(coord_vec)
tile1 = Vec2d(curr_tile[0]+1, curr_tile[1])
tile2 = Vec2d(curr_tile[0]-1, curr_tile[1])
tile3 = Vec2d(curr_tile[0], curr_tile[1]+1)
tile4 = Vec2d(curr_tile[0], curr_tile[1]-1)
neighbors = [tile1, tile2, tile3, tile4]
return filter(self.filter_tile_neighbors, neighbors)
def filter_tile_neighbors (self, coord):
""" Checks wether a tank can move to the specific tile type"""
x = coord[0]
y = coord[1]
if (x >= 0 and x <= self.MAX_X) and (y >= 0 and y <= self.MAX_Y):
box = self.currentmap.boxAt(x,y)
if box == 0 or box == 2 or self.metal_path == True and box == 3:
return True
return False
class VideoGame:
def __init__(self, title, price, developer, platform):
self.title = title
self.price = price
self.developer = developer
self.platform = platform
def print_description(self):
print()
print(f'{self.title} - Now only ${self.price}!!')
print(f'Developed by {self.developer} for {self.platform}')
class PC_Game(VideoGame):
def __init__(self, title, price, developer, platform, requirements):
super().__init__(title, price, developer, platform)
self.requirements = requirements
def print_description(self):
super().print_description()
print(f'Written by the renowned author {self.requirements}')
videogames = [
PC_Game('Crysis 3', 500, 'Crytek', 'PC','Intel HD Graphics 4000 is not the right option')
]
#for videogame in videogames:
# videogame.print_description()
ctf.py 0 → 100644
import pygame
import sys
import time
from pygame.locals import *
from pygame.color import *
import pymunk
pygame.init()
pygame.display.set_mode()
#-- Initialise the clock
clock = pygame.time.Clock()
#-- Initialise the physics engine
space = pymunk.Space()
space.gravity = (0.0, 0.0)
space.damping = 0.1# Adds friction to the ground for all objects
#-- Import from the ctf framework
import ai
import images
import gameobjects
import maps
import menu
import print_functions
import init_objects
####### Collision functions #################
def collision_bullet_wall(arb, space, data):
"""Handle the collision between bullet and wall."""
bullet = arb.shapes[0].parent
game_objects_list.remove(bullet)
space.remove(arb.shapes[0], arb.shapes[0].body)
return False
def collision_bullet_box(arb, space, data):
"""Handle the collision between bullet and box."""
bullet = arb.shapes[0].parent
space.remove(arb.shapes[0], arb.shapes[0].body)
if bullet in game_objects_list:
game_objects_list.remove(bullet)
box = arb.shapes[1].parent
if box.destructable:
if box.hitpoints > 1:
box.hitpoints = box.hitpoints-1
else:
if box in game_objects_list:
game_objects_list.remove(box)
space.remove(arb.shapes[1], arb.shapes[1])
return False
def collision_bullet_tank(arb, space, data):
"""Handle the collision between bullet and tank."""
bullet = arb.shapes[0].parent
tank = arb.shapes[1].parent
if bullet in game_objects_list:
game_objects_list.remove(bullet)
space.remove(arb.shapes[0], arb.shapes[0].body)
flag.is_on_tank = False
tank.on_death()
return False
def collision_bullet_bullet(arb, space, data):
"""Handle the collision between bullet and bullet."""
bullet1= arb.shapes[1].parent
bullet2 = arb.shapes[0].parent
if bullet1 in game_objects_list:
game_objects_list.remove(bullet1)
space.remove(arb.shapes[0], arb.shapes[0].body)
if bullet2 in game_objects_list:
game_objects_list.remove(bullet2)
space.remove(arb.shapes[1], arb.shapes[1].body)
return False
def create_collision_handlers():
"""Creates the handling of collisions
between different objects."""
handler = space.add_collision_handler(1, 3)
handler.pre_solve = collision_bullet_box
handler = space.add_collision_handler(1, 10)
handler.pre_solve = collision_bullet_wall
handler = space.add_collision_handler(1, 2)
handler.pre_solve = collision_bullet_tank
handler = space.add_collision_handler(1, 1)
handler.pre_solve = collision_bullet_bullet
def create_objects():
"""Creates objects in the game."""
init_objects.create_borders(space,current_map)
init_objects.create_background(current_map,background)
init_objects.create_boxes(game_objects_list, current_map, space)
init_objects.create_tanks(tanks_list,game_objects_list , \
current_map, space, multi, ai_list)
init_objects.create_bases(game_objects_list , current_map)
############## Main loop ######################
def shoot_bullet(i):
"""Shoots a bullet from the tank
and adds it to the game objects list."""
bullet = tanks_list[i].shoot()
if bullet != None:
game_objects_list.append(bullet)
def keyhandler():
"""Handles key events for the game."""
dic_keydown = {
K_UP: tanks_list[0].accelerate,
K_DOWN: tanks_list[0].decelerate,
K_RIGHT: tanks_list[0].turn_right,
K_LEFT: tanks_list[0].turn_left,
K_ESCAPE: pygame.quit}
dic_keyup = {
K_UP: tanks_list[0].stop_moving,
K_DOWN: tanks_list[0].stop_moving,
K_LEFT: tanks_list[0].stop_turning,
K_RIGHT: tanks_list[0].stop_turning }
if multi:
dic_keydown_multi = {
K_w: tanks_list[1].accelerate,
K_s: tanks_list[1].decelerate,
K_d: tanks_list[1].turn_right,
K_a: tanks_list[1].turn_left}
dic_keyup_multi = {
K_w: tanks_list[1].stop_moving,
K_s: tanks_list[1].stop_moving,
K_d: tanks_list[1].stop_turning,
K_a: tanks_list[1].stop_turning}
dic_keydown.update(dic_keydown_multi)
dic_keyup.update(dic_keyup_multi)
return dic_keydown, dic_keyup
def tank_win(tank, max_round, max_time):
"""Handles the scenario when a tank wins the game."""
player = 1
flag.x, flag.y = flag_start
flag.is_on_tank = False
tank.flag = None
tank.points = tank.points +1
for i in tanks_list:
i.stop_moving() # make sure no speed when respawn
i.body.position = i.start_position
print("Player",player,":",i.points)
player = player+1
print_functions.print_score_result(screen,tanks_list)
print("============")
if tank.points == 2 or max_round == 10 or max_time>1800*5: # 1800 == 1 min
return False
return True
def fog_of_war():
"""Implements the fog of war effect for the game."""
tank_pixle_pos = 40
tile_pixle_size = 100
fog_pixle_radius = 90
color_black = (255,255,255)
color_white = (0,0,0)
map_pixle_size = ((current_map.width+1)*tile_pixle_size,\
(current_map.height+1)*tile_pixle_size)
x = tanks_list[0].body.position[0]*tank_pixle_pos
y = tanks_list[0].body.position[1]*tank_pixle_pos
surface1 = pygame.Surface(map_pixle_size)
pygame.draw.circle(surface1, color_black, (x,y), \
fog_pixle_radius,pygame.SRCALPHA)
# add the extra fog vision in case of multiplayer
if multi:
x_multi = tanks_list[1].body.position[0]*tank_pixle_pos
y_multi = tanks_list[1].body.position[1]*tank_pixle_pos
pygame.draw.circle(surface1, color_black,(x_multi,y_multi),
fog_pixle_radius,pygame.SRCALPHA)
pygame.draw.rect(surface1, color_white,(map_pixle_size[0], \
map_pixle_size[1],0,0))
surface1.set_colorkey(color_black)
screen.blit(surface1, (0,0))
def main_loop():
"""The main loop of the game where events are handled,
game objects are updated and displayed on the screen."""
# init key inputs!
dic_keydown, dic_keyup = keyhandler()
running = True
skip_update = 0
# Create the tank, images.tanks contains the image representing the tank
count_rounds = 1
count_time = 0
while running:
count_time = count_time +1
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key in dic_keydown:
dic_keydown[event.key]()
elif event.key == K_RETURN:
shoot_bullet(0)
elif event.key == K_SPACE:
shoot_bullet(1)
elif event.type == KEYUP:
if event.key in dic_keyup:
dic_keyup[event.key]()
#-- Update physics
if skip_update == 0:
for obj in game_objects_list:
obj.update()
skip_update = 2
else:
skip_update -= 1
space.step(1 / FRAMERATE)
#<INSERT DISPLAY BACKGROUND>
# Display the background on the screen
screen.blit(background, (0, 0))
#<INSERT DISPLAY OBJECTS>
# Update the display of the game objects on the screen
for i in tanks_list:
if i.has_won():
count_rounds = count_rounds +1
running = tank_win(i,count_rounds, count_time)
i.post_update(flag)
for i in ai_list:
i.decide()
for obj in game_objects_list:
obj.update_screen(screen)
obj.update()
if FOG:
fog_of_war()
if game_mode == 1:
print_functions.print_stats_score(screen,\
tanks_list, current_map)
elif game_mode == 2:
print_functions.print_stats_time(screen, \
count_time, current_map)
elif game_mode == 3:
print_functions.print_stats_round(screen,\
count_rounds , current_map)
# Redisplay the entire screen (see double buffer technique)
pygame.display.flip()
# Control the game framerate
clock.tick(FRAMERATE)
# Redisplay the entire screen (see double buffer technique)
pygame.display.flip()
# Control the game framerate
clock.tick(FRAMERATE)
# main
try:
if str(sys.argv[1]) == "--singleplayer":
multi = False
if str(sys.argv[1]) == "--hot-multiplayer":
multi = True
FOG = False
current_map = maps.map0
game_mode = 1
except IndexError:
main_tuple = menu.main_menu()
multi = main_tuple[0]
current_map = main_tuple[1]
FOG = main_tuple[2]
game_mode = main_tuple[3]
#----- Initialisation -----#
#-- Initialise the display
#-- Constants
FRAMERATE = 100
#-- Variables
# Define the current level
flag_start = current_map.flag_position[0], current_map.flag_position[1]
# List of all game objects
game_objects_list = []
tanks_list = []
ai_list = []
#-- Resize the screen to the size of the current level
screen = pygame.display.set_mode((current_map.rect().size[0]+100,\
current_map.rect().size[1]))
#<INSERT GENERATE BACKGROUND>
#-- Generate the background
background = pygame.Surface(screen.get_size())
flag = init_objects.create_flag(game_objects_list,flag_start)
create_objects()
create_collision_handlers()
main_loop()
File added
data/base_blue.png

380 B

data/base_gray.png

376 B

data/base_orange.png

348 B

data/base_red.png

328 B

data/base_white.png

364 B

data/base_yellow.png

355 B

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment