import numpy as np import random import pygame import time pygame.init() pygame.font.init() # you have to call this at the start, # if you want to use this module. myfont = pygame.font.SysFont('Comic Sans MS', 55) P0_text = myfont.render('P0', False, (0, 0, 0)) game_over_text = myfont.render('GAME OVER', False, (255, 0, 0)) random.seed(0) dimx = 5 dimy = 10 scale_fac = 50 screen = pygame.display.set_mode((dimx*scale_fac+100,dimy*scale_fac+200)) tiledt = np.dtype([('x', np.uint8), ('y', np.uint8), ('color', np.uint8, 3), ('star', np.bool)]) BLACK = np.array([0, 0, 0], dtype=np.uint8) WHITE = np.array([255, 255, 255], dtype=np.uint8) RED = np.array([255, 0, 0], dtype=np.uint8) BLUE = np.array([0, 0, 255], dtype=np.uint8) YELLOW = np.array([255, 255, 0], dtype=np.uint8) GREEN = np.array([0, 255, 0], dtype=np.uint8) class Board: valid_colors = [WHITE, RED, BLUE] def __init__(self, dim_x, dim_y, robot): self.tiles = np.zeros((dim_y, dim_x), dtype=tiledt) for x in range(dim_x): for y in range(dim_y): self.tiles[y, x]['x'] = x self.tiles[y, x]['y'] = y self.tiles[y, x]['color'] = random.choice(Board.valid_colors) self.robot = robot def render(self): board_surf = pygame.Surface((dimx * scale_fac, dimy * scale_fac)) robot_surf = pygame.Surface((scale_fac, scale_fac), pygame.SRCALPHA) # Use the local coordinate system of the surface to draw the lines. pygame.draw.lines(robot_surf, (0, 0, 0), True, [(0.75 * scale_fac, 0.5 * scale_fac), (0.25 * scale_fac, 0.25 * scale_fac), (0.25 * scale_fac, 0.75 * scale_fac)], 3) # I rotate it so that the arrow is pointing to the right (0° is right). robot_surf = pygame.transform.rotate(robot_surf, self.robot.get_angle()) star_surf = pygame.Surface((scale_fac, scale_fac), pygame.SRCALPHA) pygame.draw.circle(star_surf, YELLOW, (int(0.5 * scale_fac), int(0.5 * scale_fac)), int(0.25 * scale_fac)) for y in range(self.tiles.shape[0]): for x in range(self.tiles.shape[1]): pygame.draw.rect(board_surf, tuple(self.tiles[y, x]['color']), (x * scale_fac, y * scale_fac, scale_fac, scale_fac), 0) pygame.draw.rect(board_surf, (0, 0, 0), (x * scale_fac, y * scale_fac, scale_fac, scale_fac), 1) if self.tiles[y, x]['star']: board_surf.blit(star_surf, (x * scale_fac, y * scale_fac, scale_fac, scale_fac)) if (x, y) == (self.robot.x, self.robot.y): board_surf.blit(robot_surf, (x * scale_fac, y * scale_fac, scale_fac, scale_fac)) return board_surf def __repr__(self): s = '' for y in range(self.tiles.shape[0]): for x in range(self.tiles.shape[1]): if (x,y) == (self.robot.x, self.robot.y): s += self.robot.orientation else: s += '.' s += '\n' return s class Robot: orientations = ['^', 'left', 'down', 'right'] resulting_orientation = { '^': {'left': '<', 'right': '>'}, '>': {'left': '^', 'right': 'v'}, 'v': {'left': '>', 'right': '<'}, '<': {'left': 'v', 'right': '^'}, } def __init__(self, x, y, orientation): self.x = x self.y = y self.orientation = orientation def get_forward_coordinates(self): # get the coordinates of the neighboring tile in the given direction if self.orientation == '^': return self.y - 1, self.x elif self.orientation == '>': return self.y, self.x + 1 elif self.orientation == 'v': return self.y + 1, self.x elif self.orientation == '<': return self.y, self.x - 1 else: raise Exception("error: undefined direction") def get_angle(self): angle = {'>': 0, '^': np.pi/2, '<': np.pi, 'v': 3*np.pi/2}[self.orientation] return np.rad2deg(angle) def __repr__(self): return f"({self.y}, {self.x}) - {self.orientation}" class Command: valid_actions = {'forward', 'left', 'right', 'P0', '-'} def __init__(self, action=None, color=WHITE): if not (action in Command.valid_actions and any([np.all(color == c) for c in Board.valid_colors])): raise ValueError("invalid values for command") self.action = action self.color = color def __repr__(self): return f"{self.action}: {self.color}" def render(self): cmd_surf = pygame.Surface((scale_fac, scale_fac)) cmd_surf.fill(tuple(self.color)) arrow_surf = pygame.Surface((300, 300), pygame.SRCALPHA) #arrow_surf.fill(tuple(WHITE)) pygame.draw.polygon(arrow_surf, (0, 0, 0), ((0, 100), (0, 200), (200, 200), (200, 300), (300, 150), (200, 0), (200, 100))) arrow_surf = pygame.transform.scale(arrow_surf, (int(0.9*scale_fac), int(0.9*scale_fac))) if self.action == 'forward': arrow_surf = pygame.transform.rotate(arrow_surf, 90) elif self.action == 'left': arrow_surf = pygame.transform.rotate(arrow_surf, 180) if self.action in {'left', 'forward', 'right'}: cmd_surf.blit(arrow_surf, (0.05*scale_fac,0.05*scale_fac,0.95*scale_fac,0.95*scale_fac)) elif self.action == 'P0': cmd_surf.blit(P0_text, (0.05*scale_fac,0.05*scale_fac,0.95*scale_fac,0.95*scale_fac)) return cmd_surf class Program: def __init__(self, robot, board, cmds): self.cmds = cmds self.robot = robot self.board = board def input_program(self): self.render() pass def run(self): running = True prg_counter = 0 self.render(prg_counter) while running: cmd = self.cmds[prg_counter] prg_counter += 1 # current position x = self.robot.x y = self.robot.y # current tile the robot is on tile = self.board.tiles[y, x] if np.all(cmd.color == WHITE) or np.all(cmd.color == tile['color']): # matching color -> execute command if cmd.action == 'forward': ynew, xnew = r.get_forward_coordinates() r.x = xnew r.y = ynew elif cmd.action in {'left', 'right'}: r.orientation = Robot.resulting_orientation[self.robot.orientation][cmd.action] elif cmd.action == 'P0': prg_counter = 0 else: print("color not matching -> skipping command") self.render(prg_counter) if (not (0 <= r.x < self.board.tiles.shape[1])) or not (0 <= r.y < self.board.tiles.shape[0]): print("GAME OVER") screen.blit(game_over_text, (50, 00)) pygame.display.update() running = False time.sleep(0.1) def render(self, prg_counter=None): dx = 50 dy = 50 screen.fill(tuple(BLACK)) board_surf = self.board.render() screen.blit(board_surf, (dx, dy, dx + dimx * scale_fac, dy + dimy * scale_fac)) prg_surf = self.render_prg(prg_counter) screen.blit(prg_surf, (dx, board_surf.get_height()+2*dy, prg_surf.get_width(), prg_surf.get_height())) pygame.display.update() def render_prg(self, prg_counter=None): prg_surf = pygame.Surface((5 * scale_fac, 1 * scale_fac)) prg_surf.fill(WHITE) for i in range(5): if i < len(self.cmds): cmd = self.cmds[i] cmd_surf = cmd.render() if prg_counter is not None and i == prg_counter: pygame.draw.rect(cmd_surf, tuple(GREEN), (0,0,scale_fac,scale_fac), 5) prg_surf.blit(cmd_surf, (i * scale_fac, 0, scale_fac, scale_fac)) return prg_surf r = Robot(x=1, y=1, orientation='v') b = Board(dimx,dimy, r) b.tiles[3,4]['star'] = True print(b) cmds = [Command('forward'), Command('left', color=RED), Command('left', color=BLUE), Command('P0')] prg = Program(r, b, cmds) prg.input_program() prg.run()