import math import os import pygame from PIL import Image from advanced_view.gauge import GaugeDraw from pygame import gfxdraw from random import randint from car import MAX_SPEED, VISION_F, VISION_W, VISION_B import config from config import ROAD_VIEW_OFFSET, INPUT_VIEW_OFFSET_X, INPUT_VIEW_OFFSET_Y pygame.font.init() font_28 = pygame.font.Font(os.path.join('./advanced_view/fonts/digitize.ttf'), 28) font_60 = pygame.font.Font(os.path.join('./advanced_view/fonts/digitize.ttf'), 60) class Point: # constructed using a normal tupple def __init__(self, point_t = (0,0)): self.x = float(point_t[0]) self.y = float(point_t[1]) # define all useful operators def __add__(self, other): return Point((self.x + other.x, self.y + other.y)) def __sub__(self, other): return Point((self.x - other.x, self.y - other.y)) def __mul__(self, scalar): return Point((self.x*scalar, self.y*scalar)) def __div__(self, scalar): return Point((self.x/scalar, self.y/scalar)) def __len__(self): return int(math.sqrt(self.x**2 + self.y**2)) # get back values in original tuple format def get(self): return self.x, self.y black = pygame.Color(0, 0, 0) white = pygame.Color(255, 255, 255) grey = pygame.Color(128, 128, 128) yellow = pygame.Color(255, 255, 0, 128) orange = pygame.Color(255, 140, 0, 128) IMAGE_PATH = './images' if config.VISUALENABLED: accelerate_on = pygame.image.load(os.path.join(IMAGE_PATH, 'accelerate_on.png')) accelerate_off = pygame.image.load(os.path.join(IMAGE_PATH, 'accelerate_off.png')) brake_on = pygame.image.load(os.path.join(IMAGE_PATH, 'brake_on.png')) brake_off = pygame.image.load(os.path.join(IMAGE_PATH, 'brake_off.png')) left_on = pygame.image.load(os.path.join(IMAGE_PATH, 'left_on.png')) left_off = pygame.image.load(os.path.join(IMAGE_PATH, 'left_off.png')) right_on = pygame.image.load(os.path.join(IMAGE_PATH, 'right_on.png')) right_off = pygame.image.load(os.path.join(IMAGE_PATH, 'right_off.png')) def draw_dashed_line(surf, color, start_pos, end_pos, width=1, dash_length=10): if not config.VISUALENABLED: return origin = Point(start_pos) target = Point(end_pos) displacement = target - origin length = len(displacement) slope = displacement/length loop = length / dash_length for index in range(0, loop, 2): start = origin + (slope * index * dash_length) end = origin + (slope * (index + 1) * dash_length) gfxdraw.filled_polygon(surf, ( (int(start.x), int(start.y)), (int(start.x) + width, int(start.y)), (int(end.x) + width, int(end.y)), (int(end.x), int(end.y)) ), color) def draw_dashed_line_delay(surf, color, start_pos, end_pos, width=1, dash_length=10, delay=0): if not config.VISUALENABLED: return origin = Point(start_pos) target = Point(end_pos) displacement = target - origin length = len(displacement) slope = displacement/length loop = length / dash_length origin = origin + (slope * delay * 10) for index in range(0, loop + 1, 2): start = origin + (slope * index * dash_length) end = origin + (slope * (index + 1) * dash_length) gfxdraw.filled_polygon(surf, ( (int(start.x), int(start.y)), (int(start.x) + width, int(start.y)), (int(end.x) + width, int(end.y)), (int(end.x), int(end.y)) ), color) def draw_basic_road(surface, speed): if not config.VISUALENABLED: return surface.fill(white) # Left most lane marking pygame.draw.line(surface, black, (ROAD_VIEW_OFFSET + 13, -10), (ROAD_VIEW_OFFSET + 13, 1000), 5) # Right most lane marking pygame.draw.line(surface, black, (ROAD_VIEW_OFFSET + 367, -10), (ROAD_VIEW_OFFSET + 367, 1000), 5) line_marking_offset = randint(0, 10) for l in range(1, 7): draw_dashed_line( surface, grey, (ROAD_VIEW_OFFSET + l * 50 + 15, int((speed/(MAX_SPEED * 1.0)) * -1 * line_marking_offset)), (ROAD_VIEW_OFFSET + l * 50 + 15, 1000), width=1, dash_length=5 ) def draw_road_overlay_safety(surface, lane_map): if not config.VISUALENABLED: return # Draw on surface for y in range(100): for x in range(7): if lane_map[y][x] != 0: pygame.draw.rect(surface, yellow, (ROAD_VIEW_OFFSET + x * 50 + 15 + 1, y * 10, 49, 10)) pygame.draw.rect(surface, grey, (ROAD_VIEW_OFFSET + x * 50 + 15 + 1, y * 10, 49, 10), 1) def draw_road_overlay_vision(surface, subject_car): if not config.VISUALENABLED: return # Draw on surface min_x = min(max(0, subject_car.lane - VISION_W - 1), 6) max_x = min(max(0, subject_car.lane + VISION_W - 1), 6) min_y = min(max(0, int(math.ceil(subject_car.y / 10.0)) - VISION_F - 1), 100) max_y = min(max(0, int(math.ceil(subject_car.y / 10.0)) + VISION_B - 1), 100) for y in range(min_y, max_y + 1): for x in range(min_x, max_x + 1): pygame.draw.rect(surface, orange, (ROAD_VIEW_OFFSET + x * 50 + 15 + 1, y * 10, 49, 10)) pygame.draw.rect(surface, grey, (ROAD_VIEW_OFFSET + x * 50 + 15 + 1, y * 10, 49, 10), 1) def control_car(target_car, keydown): if keydown == pygame.K_UP: target_car.move('A') elif keydown == pygame.K_DOWN: target_car.move('D') else: target_car.move('M') if keydown == pygame.K_LEFT: target_car.switch_lane('L') elif keydown == pygame.K_RIGHT: target_car.switch_lane('R') def identify_free_lane(cars): lanes = [[n for n in range(1, 8)] for _ in range(2)] for car in cars: if -170 <= car.y <= 0: if car.lane in lanes[0]: lanes[0].remove(car.lane) if car.switching_lane in lanes[0]: lanes[0].remove(car.switching_lane) elif 930 <= car.y <= 1070: if car.lane in lanes[1]: lanes[1].remove(car.lane) if car.switching_lane in lanes[1]: lanes[1].remove(car.switching_lane) return lanes def draw_inputs(surface, vision): vision_title = font_28.render("Vision:", False, (0, 0, 0)) surface.blit(vision_title, (INPUT_VIEW_OFFSET_X - 10, INPUT_VIEW_OFFSET_Y)) for y_i in range(len(vision)): for x_i, x in enumerate(vision[y_i]): pygame.draw.rect(surface, orange if x != 0 else white, (INPUT_VIEW_OFFSET_X + x_i * 10 + 80, INPUT_VIEW_OFFSET_Y + y_i * 10, 10, 10)) pygame.draw.rect(surface, grey, (INPUT_VIEW_OFFSET_X + x_i * 10 + 1 + 80, INPUT_VIEW_OFFSET_Y + y_i * 10, 10, 10), 1) def draw_actions(surface, action): action_title = font_28.render("Action:", False, (0, 0, 0)) surface.blit(action_title, (INPUT_VIEW_OFFSET_X - 10, INPUT_VIEW_OFFSET_Y + 370)) surface.blit(left_on if action == 'L' else left_off, (INPUT_VIEW_OFFSET_X + 80, INPUT_VIEW_OFFSET_Y + 370, 34, 70)) surface.blit(right_on if action == 'R' else right_off, (INPUT_VIEW_OFFSET_X + 80 + 40, INPUT_VIEW_OFFSET_Y + 370, 34, 70)) surface.blit(brake_on if action == 'D' else brake_off, (INPUT_VIEW_OFFSET_X + 80, INPUT_VIEW_OFFSET_Y + 410, 34, 70)) surface.blit(accelerate_on if action == 'A' else accelerate_off, (INPUT_VIEW_OFFSET_X + 80 + 40, INPUT_VIEW_OFFSET_Y + 410, 34, 70)) class Score: def __init__(self, score=0): self.score = score def add(self): self.score += 1 # self.score -= 0.1 def subtract(self): self.score -= 1 # self.score -= 0.1 def penalty(self): # Penalty over time self.score += config.CONSTANT_PENALTY def brake_penalty(self): self.score += config.EMERGENCY_BRAKE_PENALTY def action_mismatch_penalty(self): self.score += config.MISMATCH_ACTION_PENALTY def switching_lane_penalty(self): self.score += config.SWITCHING_LANE_PENALTY def draw_gauge(surface, speed): im = Image.new("RGB", (200, 200), (255, 255, 255, 0)) g = GaugeDraw(im, 0, 110) g.render_simple_gauge(value=speed, major_ticks=10, minor_ticks=5, label="{}kmh".format(speed)) gauge = pygame.image.fromstring(im.tobytes(), im.size, im.mode) speed_title = font_28.render("Speed:", False, (0, 0, 0)) surface.blit(speed_title, (INPUT_VIEW_OFFSET_X - 10, 10)) surface.blit(gauge, ((INPUT_VIEW_OFFSET_X - 10, 35), (200, 200))) def draw_score(surface, score): score_title = font_28.render("Score:", False, (0, 0, 0)) score = font_60.render(str(int(score)), False, (0, 0, 0)) surface.blit(score_title, (INPUT_VIEW_OFFSET_X - 10, 245)) surface.blit(score, (INPUT_VIEW_OFFSET_X - 10 + 80, 240))