RoboRally/gauss-turing/webserver/gauss_turing.py

360 lines
13 KiB
Python

import numpy as np
import random
import pygame
import time
import copy
import os
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)
pygame.init()
#os.environ['SDL_VIDEO_WINDOW_POS'] = '1920, 280'
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)
myfont_small = pygame.font.SysFont('Comic Sans MS', 45)
P0_text = myfont.render('P0', False, (0, 0, 0))
game_over_text = myfont.render('GAME OVER', False, RED)
won_text = myfont.render('YOU WON', False, GREEN)
run_text = myfont.render('RUN', False, tuple(BLACK))
stop_text = myfont_small.render('STOP', False, tuple(BLACK))
random.seed(42)
dimx = 7
dimy = 4
scale_fac = 180
screen = pygame.display.set_mode((dimx*scale_fac,dimy*scale_fac))
#screen = pygame.display.set_mode((dimx*scale_fac+int(0.1*scale_fac),dimy*scale_fac+300))
tiledt = np.dtype([('x', np.uint8), ('y', np.uint8), ('color', np.uint8, 3), ('star', np.bool)])
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)
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
self.initial_pos = (self.robot.x, self.robot.y, self.robot.orientation)
self.state = 'input'
self.available_inputs = [Command('forward'), Command('left'), Command('right'), Command('P0'),
Command('-', color=RED), Command('-', color=BLUE), Command('-', color=WHITE)]
self.fullscreen = False
def input_program(self):
self.state = 'input'
selected_cmd = 0
self.render(selected_cmd)
while self.state == 'input':
# get all events
ev = pygame.event.get()
# proceed events
for event in ev:
# handle MOUSEBUTTONUP
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
if pos[0] >= 50 and pos[0] <= 50 + 250 and pos[1] >= 600 and pos[1] <= 650:
print(f"clicked at pos = {pos}")
selected_cmd = (pos[0] - 50)//50
if pos[0] >= 50 and pos[0] <= 50 + 350 and pos[1] >= 700 and pos[1] <= 750:
print(f"clicked at pos = {pos}")
chosen_input = (pos[0] - 50)//50
chosen_input_cmd = self.available_inputs[chosen_input]
if selected_cmd < len(self.cmds):
edited_cmd = self.cmds[selected_cmd]
if chosen_input_cmd.action is not '-':
edited_cmd.action = chosen_input_cmd.action
else:
edited_cmd.color = chosen_input_cmd.color
else:
self.cmds.append(copy.copy(chosen_input_cmd))
if pos[0] >= 325 and pos[0] <= 400 and pos[1] >= 600 and pos[1] <= 650:
print(f"clicked at pos = {pos}")
self.state = 'running'
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
if not self.fullscreen:
os.environ['SDL_VIDEO_WINDOW_POS'] = '1920, 280'
screen = pygame.display.set_mode((dimx * scale_fac, dimy * scale_fac),
pygame.NOFRAME)
self.fullscreen = True
else:
os.environ['SDL_VIDEO_WINDOW_POS'] = '0, 0'
screen = pygame.display.set_mode((dimx * scale_fac, dimy * scale_fac))
self.fullscreen = False
self.render(selected_cmd)
pygame.time.wait(100)
self.run()
def run(self):
prg_counter = 0
self.render(prg_counter)
self.state = 'running'
while self.state == '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")
# get all events
ev = pygame.event.get()
# proceed events
for event in ev:
# handle MOUSEBUTTONUP
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
if pos[0] >= 325 and pos[0] <= 400 and pos[1] >= 600 and pos[1] <= 650:
print(f"clicked at pos = {pos}")
self.state = 'input'
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()
pygame.time.wait(1500)
self.state = 'input'
else:
tile = self.board.tiles[r.y,r.x]
if tile['star']:
tile['star'] = False
if all([not t['star'] for t in self.board.tiles.flatten()]):
print("YOU WON")
screen.blit(won_text, (50, 00))
pygame.display.update()
pygame.time.wait(1500)
self.state = 'input'
pygame.time.wait(100)
self.robot.x = self.initial_pos[0]
self.robot.y = self.initial_pos[1]
self.robot.orientation = self.initial_pos[2]
self.input_program()
def render(self, prg_counter=None):
dx = 0
dy = 0
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()))
inp_surf = self.render_inputs()
screen.blit(inp_surf, (dx, board_surf.get_height()+4*dy, inp_surf.get_width(), inp_surf.get_height()))
btn_surf = pygame.Surface((80, 50))
if self.state == 'input':
btn_surf.fill(tuple(GREEN))
btn_surf.blit(run_text, (0, 10))
elif self.state == 'running':
btn_surf.fill(tuple(RED))
btn_surf.blit(stop_text, (0, 10))
screen.blit(btn_surf, (325, board_surf.get_height()+2*scale_fac, btn_surf.get_height(), btn_surf.get_width()))
pygame.display.update()
def render_inputs(self):
inp_surf = pygame.Surface((len(self.available_inputs) * scale_fac, 1 * scale_fac))
for i, inp in enumerate(self.available_inputs):
cmd_surf = inp.render()
inp_surf.blit(cmd_surf, (i * scale_fac, 0, scale_fac, scale_fac))
return inp_surf
def render_prg(self, prg_counter=None):
prg_surf = pygame.Surface((5 * scale_fac, 1 * scale_fac))
for i in range(5):
if i < len(self.cmds):
cmd = self.cmds[i]
cmd_surf = cmd.render()
else:
cmd_surf = pygame.Surface((50,50))
cmd_surf.fill(WHITE)
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,3]['star'] = True
b.tiles[3,2]['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()