581 lines
17 KiB
Python
581 lines
17 KiB
Python
#!/usr/bin/python
|
|
import os
|
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
|
os.environ['SDL_VIDEO_CENTERED'] = '1'
|
|
|
|
import serial
|
|
import webapp
|
|
import threading
|
|
import logging
|
|
import socket
|
|
import json
|
|
import string
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
import random, pygame
|
|
from pygame.locals import *
|
|
|
|
import threading
|
|
import time
|
|
import RPi.GPIO as GPIO
|
|
|
|
class Slide(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
def render(self, screen):
|
|
pass
|
|
|
|
def handle(self, ev):
|
|
pass
|
|
|
|
def enter(self, prev_slide):
|
|
return True
|
|
|
|
def exit(self, next_slide):
|
|
return True
|
|
|
|
|
|
class ImageSlide(Slide):
|
|
def __init__(self, image):
|
|
self.image = image
|
|
|
|
def render(self, screen):
|
|
screen.fill(WHITE)
|
|
screen.blit(self.image, (0, 0))
|
|
|
|
class FinalSlide(ImageSlide):
|
|
def exit(self, next_slide):
|
|
# Allow change to exit slide
|
|
return isinstance(next_slide, FinalSlide)
|
|
|
|
class FinalEverythingSlide(FinalSlide):
|
|
def enter(self, prev_slide):
|
|
global wall_a
|
|
if(wall_a):
|
|
win()
|
|
pygame.mixer.music.load("media/win.mp3")
|
|
pygame.mixer.music.play(MP3_LOOPS)
|
|
pygame.mixer.music.set_volume(1.0)
|
|
|
|
return True
|
|
|
|
class SnakeSlide(Slide):
|
|
def enter(self, prev_slide=None):
|
|
self.xs = [290, 290, 290, 290, 290];
|
|
self.ys = [290, 270, 250, 230, 210];
|
|
self.dirs = 0
|
|
self.score = 0
|
|
self.applepos = (random.randint(0, WINSIZEX), random.randint(0, WINSIZEY))
|
|
#s=pygame.display.set_mode((600, 600))
|
|
self.appleimage = IMG_APPLE
|
|
self.img = IMG_SNAKEBODY#pygame.Surface((20, 20))
|
|
#img.fill((255, 0, 0))
|
|
self.f = FONT_SNAKE
|
|
self.clock = pygame.time.Clock()
|
|
self.finished = False
|
|
|
|
return True
|
|
|
|
def finish(self):
|
|
self.finished = True
|
|
self.finish_time = time.time()
|
|
|
|
def handle(self, e):
|
|
print(e)
|
|
if e.type == KEYDOWN:
|
|
if e.key == K_UP and self.dirs != 0: self.dirs = 2
|
|
elif e.key == K_DOWN and self.dirs != 2: self.dirs = 0
|
|
elif e.key == K_LEFT and self.dirs != 1: self.dirs = 3
|
|
elif e.key == K_RIGHT and self.dirs != 3: self.dirs = 1
|
|
|
|
if self.finished and time.time() - self.finish_time > 3:
|
|
self.enter()
|
|
|
|
print(self.dirs)
|
|
|
|
def render(self, screen):
|
|
self.clock.tick(TICK_SNAKE)
|
|
|
|
if not self.finished:
|
|
self.interact()
|
|
|
|
#rysuj tlo
|
|
screen.fill(WHITE)
|
|
screen.blit(IMG_SNAKE, (0, 0))
|
|
|
|
for i in range(0, len(self.xs)):
|
|
screen.blit(self.img, (self.xs[i], self.ys[i]))
|
|
|
|
screen.blit(self.appleimage, self.applepos);
|
|
t=self.f.render(str(self.score), True, (0, 0, 0));
|
|
screen.blit(t, (10, 10));
|
|
|
|
if self.finished:
|
|
f=FONT_SNAKE
|
|
t=f.render(MSG_SNAKE+str(self.score), True, (0, 0, 0))
|
|
screen.blit(t, (10, 270))
|
|
return
|
|
|
|
def interact(self):
|
|
# snek collision
|
|
i = len(self.xs)-1
|
|
while i >= 2:
|
|
if collide(self.xs[0], self.xs[i], self.ys[0], self.ys[i], 20, 20, 20, 20):
|
|
self.finish()
|
|
return
|
|
|
|
i-= 1
|
|
|
|
# Apple collision
|
|
if collide(self.xs[0], self.applepos[0], self.ys[0], self.applepos[1], 20, 10, 20, 10):
|
|
self.score+=1
|
|
for i in range(0, SNAKE_ADDSIZE):
|
|
self.xs.append(700)
|
|
self.ys.append(700)
|
|
self.applepos=(random.randint(0,WINSIZEX),random.randint(0,WINSIZEY))
|
|
|
|
if self.xs[0] < 0 or self.xs[0] > WINSIZEX or self.ys[0] < 0 or self.ys[0] > WINSIZEY:
|
|
self.finish()
|
|
return
|
|
|
|
self.xs[1:] = self.xs[:-1]
|
|
self.ys[1:] = self.ys[:-1]
|
|
|
|
if self.dirs==0:self.ys[0] += 20
|
|
elif self.dirs==1:self.xs[0] += 20
|
|
elif self.dirs==2:self.ys[0] -= 20
|
|
elif self.dirs==3:self.xs[0] -= 20
|
|
|
|
print("drawing snake...")
|
|
if(self.score > SNAKE_SCORE):
|
|
signalWin(4)
|
|
self.finish()
|
|
return
|
|
|
|
pygame.init()
|
|
pygame.mixer.init()
|
|
|
|
from gameplayconfig import *
|
|
|
|
#ilosc powtorzen mp3
|
|
MP3_LOOPS = 4
|
|
|
|
#layout
|
|
#font i komunikat do snake
|
|
FONT_SNAKE = pygame.font.SysFont("Courier", 28)
|
|
MSG_SNAKE = 'Koniec gry! Twoj wynik: '
|
|
|
|
#font klawiatury
|
|
FONT_KEYBOARD = pygame.font.SysFont("Purisa", 28)
|
|
#kolor klawiatury w RGB
|
|
COLOR_KEYBOARD = [0, 0, 127]
|
|
#kolor tekstu na klawiaturze (na klawiszach, guzikach i wpisywanego hasla) w RGB
|
|
COLOR_KEYBOARD_TEXT = (0, 255, 0)
|
|
#tekst na przyciskach klawiatury
|
|
MSG_BTN1 = "Wpisz ja"
|
|
MSG_BTN2 = "Kasuj"
|
|
|
|
#grafika - jablko do snake, segmenty snake
|
|
IMG_APPLE = pygame.image.load("media/apple.gif")
|
|
IMG_SNAKEBODY = pygame.image.load("media/snake.gif")
|
|
|
|
#obrazki poszczegolnych slajdow
|
|
IMG_HELLO = pygame.image.load('media/hello_background.png')
|
|
IMG_KEY = pygame.image.load('media/key_background.gif')
|
|
IMG_WIN = pygame.image.load('media/congratulation_background.gif')
|
|
IMG_POT = pygame.image.load('media/pot_background.png')
|
|
IMG_FOAMBTN = pygame.image.load('media/foambtn_background.png')
|
|
IMG_KEYPAD = pygame.image.load('media/keypad_background.png')
|
|
IMG_SNAKE = pygame.image.load('media/snake_background.png')
|
|
IMG_MAZE = pygame.image.load('media/maze_background.png')
|
|
IMG_MP3 = pygame.image.load('media/sound_background.jpg')
|
|
IMG_KEYBOARD = pygame.image.load('media/keyboard_background.png')
|
|
IMG_DYNAMO = pygame.image.load('media/dynamo_background.jpg')
|
|
|
|
wall_a = 0 #czy to RPi jest na scianie A?
|
|
|
|
#pinologia
|
|
#przypisania pinow - fizyczne przyciski
|
|
PIN_BTNY = 17
|
|
PIN_BTNG = 27
|
|
|
|
#przypisania pinow - piny z RPi do Ardu zapalajace diode
|
|
PIN_SUC4 = 2
|
|
PIN_SUC9 = 3
|
|
PIN_SUC10 = 4
|
|
|
|
#przypisania pinow - piny z Ardu do Rpi wywolujace slajd
|
|
PIN_SET1 = 25
|
|
PIN_SET2 = 8
|
|
PIN_SET3 = 7
|
|
PIN_SET5 = 12
|
|
PIN_SET6 = 16
|
|
PIN_SET_WIN = 20
|
|
|
|
#przypisania pinow - zworka do GND na tym pinie ustawia, ze ta Pi jest na scianie A (uruchamia dzwiek i obrotowa lampe)
|
|
PIN_WALL_A = 26
|
|
#przypisania pinow - na tym pinie jest przekaznik wlaczajacy lampe obrotowa i otwierajacy szuflade #5
|
|
PIN_RELAY = 21
|
|
|
|
#ustawianie stanow i trybow pracy pinow
|
|
GPIO.setmode(GPIO.BCM) # set up BCM GPIO numbering
|
|
GPIO.setup(PIN_BTNY, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_BTNG, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_SET1, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_SET2, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_SET3, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_SET5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_SET6, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
GPIO.setup(PIN_WALL_A, GPIO.IN, pull_up_down=GPIO.PUD_UP) # set GPIO2 as input (button)
|
|
|
|
if GPIO.input(PIN_WALL_A) == 0:
|
|
print "this RPi is on wall A."
|
|
wall_a = 1
|
|
|
|
if(wall_a):
|
|
GPIO.setup(PIN_SET_WIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # ten pin jest dzielony miedzy wszystkie rpi i arduina - pullup robi tylko RPi A
|
|
else:
|
|
GPIO.setup(PIN_SET_WIN, GPIO.IN)
|
|
print "this RPi is."
|
|
|
|
GPIO.setup(PIN_SUC4, GPIO.OUT)
|
|
GPIO.output(PIN_SUC4, 1)
|
|
GPIO.setup(PIN_SUC9, GPIO.OUT)
|
|
GPIO.setup(PIN_SUC10, GPIO.OUT)
|
|
GPIO.setup(PIN_RELAY, GPIO.OUT)
|
|
GPIO.output(PIN_SUC9, 1)
|
|
GPIO.output(PIN_SUC10, 1)
|
|
|
|
# Well, we flipped it...
|
|
GPIO.output(PIN_RELAY, 0)
|
|
|
|
WINSIZEX, WINSIZEY = 900, 680
|
|
timeSec = 0
|
|
UP = 1
|
|
DOWN = 3
|
|
RIGHT = 2
|
|
LEFT = 4
|
|
|
|
#numery slajdow
|
|
SLIDE_START = 0
|
|
SLIDE_KEY = 1
|
|
SLIDE_POT = 2
|
|
SLIDE_FOAMBTN = 3
|
|
SLIDE_SNAKE = 4
|
|
SLIDE_DYNAMO = 5
|
|
SLIDE_KEYPAD = 6
|
|
SLIDE_MAZE = 7
|
|
SLIDE_MP3 = 8
|
|
SLIDE_KEYBOARD = 11
|
|
SLIDE_CONGRATS = 9
|
|
SLIDE_CONGRATS_EVERYTHING = 12
|
|
|
|
SLIDE_SNAKE_DEAD = 10 #to nie slajd, tylko wartosc potrzebna do resetowania snake po przegranej
|
|
|
|
global slide
|
|
#slajd poczatkowy
|
|
slide = SLIDE_START
|
|
slides = {
|
|
SLIDE_START: ImageSlide(IMG_HELLO),
|
|
SLIDE_KEY: ImageSlide(IMG_KEY),
|
|
SLIDE_POT: ImageSlide(IMG_POT),
|
|
SLIDE_FOAMBTN: ImageSlide(IMG_FOAMBTN),
|
|
SLIDE_DYNAMO: ImageSlide(IMG_DYNAMO),
|
|
SLIDE_KEYPAD: ImageSlide(IMG_KEYPAD),
|
|
SLIDE_MAZE: ImageSlide(IMG_MAZE),
|
|
SLIDE_MP3: ImageSlide(IMG_MP3),
|
|
SLIDE_CONGRATS: FinalSlide(IMG_WIN),
|
|
SLIDE_CONGRATS_EVERYTHING: FinalEverythingSlide(IMG_WIN),
|
|
SLIDE_SNAKE: SnakeSlide(),
|
|
}
|
|
|
|
def go_to_slide(new_slide):
|
|
global slide
|
|
|
|
if new_slide == slide:
|
|
logging.warning('Same slide...')
|
|
return False
|
|
|
|
if slides.get(slide) and not slides[slide].exit(slides.get(new_slide)):
|
|
logging.warning('exit rejected')
|
|
return False
|
|
|
|
if slides.get(new_slide) and not slides[new_slide].enter(slides.get(slide)):
|
|
logging.warning('enter rejected')
|
|
return False
|
|
|
|
slide = new_slide
|
|
return True
|
|
|
|
class SerialThread(threading.Thread):
|
|
def run(self):
|
|
self.logger = logging.getLogger(self.__class__.__name__)
|
|
|
|
for line in self.serial:
|
|
try:
|
|
self.handle(line)
|
|
except:
|
|
self.logger.exception('Oops...')
|
|
|
|
def handle(self, line):
|
|
print('Arduino said:', line)
|
|
|
|
command, _, args = line.partition(':')
|
|
|
|
if command == 'SLIDE':
|
|
global slide
|
|
go_to_slide(int(args))
|
|
print('Setting slide to: ', slide)
|
|
|
|
class BroadcastThread(threading.Thread):
|
|
def run(self):
|
|
cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
while True:
|
|
global slide
|
|
|
|
print('sending')
|
|
cs.sendto(json.dumps({
|
|
'slide': slide,
|
|
'name': socket.gethostname(),
|
|
}), ('255.255.255.255', 54545))
|
|
time.sleep(1)
|
|
|
|
class ReceiveThread(threading.Thread):
|
|
states = None
|
|
|
|
def run(self):
|
|
self.states = {}
|
|
|
|
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.bind(('',54545))
|
|
while True:
|
|
try:
|
|
msg, (host, port) = s.recvfrom(1024)
|
|
self.states[host] = json.loads(msg)
|
|
|
|
if all(s['slide'] in [SLIDE_CONGRATS, SLIDE_CONGRATS_EVERYTHING]
|
|
for s in self.states.values()):
|
|
go_to_slide(SLIDE_CONGRATS_EVERYTHING)
|
|
except:
|
|
logging.exception('Receiver failed')
|
|
time.sleep(0.5)
|
|
|
|
ser = None
|
|
|
|
for c in ['/dev/ttyUSB0', '/dev/ttyACM0']:
|
|
try:
|
|
ser = serial.Serial(c, 115200)
|
|
except:
|
|
continue
|
|
|
|
print('Using ', c)
|
|
webapp.init(ser)
|
|
th = SerialThread()
|
|
th.serial = ser
|
|
th.daemon = True
|
|
th.start()
|
|
|
|
bth = BroadcastThread()
|
|
bth.daemon = True
|
|
bth.start()
|
|
|
|
rth = ReceiveThread()
|
|
rth.daemon = True
|
|
rth.start()
|
|
|
|
webapp.app.rth = rth
|
|
|
|
|
|
def win():
|
|
print "GAME OVER - YOU WIN"
|
|
GPIO.output(PIN_RELAY, 1)
|
|
|
|
def signalWin(success):
|
|
pin = 0
|
|
if success == 4:
|
|
pin = PIN_SUC4
|
|
elif success == 9:
|
|
pin = PIN_SUC9
|
|
elif success == 10:
|
|
pin = PIN_SUC10
|
|
print("signalling win for task: ")
|
|
print(success)
|
|
print("using GPIO pin: ")
|
|
print(pin)
|
|
GPIO.output(pin, 0)
|
|
pygame.time.wait(200)
|
|
GPIO.output(pin, 1)
|
|
print("done signalling.")
|
|
|
|
def waitForEvents():
|
|
global slide
|
|
print("waiting for events")
|
|
while checkEvents():
|
|
clock.tick(30)
|
|
|
|
def checkEvents(handler=None):
|
|
global slide
|
|
|
|
if GPIO.input(PIN_BTNY) == 0 and slide != SLIDE_MAZE:
|
|
print "yellow button pressed - go to maze slide"
|
|
go_to_slide(SLIDE_MAZE)
|
|
return
|
|
|
|
if GPIO.input(PIN_BTNG) == 0 and slide != SLIDE_MP3:
|
|
print "green button pressed - go to mp3 slide"
|
|
go_to_slide(SLIDE_MP3)
|
|
return
|
|
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
go_to_slide(SLIDE_KEYBOARD)
|
|
print("mouse event")
|
|
return
|
|
|
|
elif event.type == pygame.KEYDOWN:
|
|
if slide != SLIDE_SNAKE:
|
|
go_to_slide(SLIDE_SNAKE)
|
|
print("keydown event")
|
|
return
|
|
|
|
if handler:
|
|
handler(event)
|
|
|
|
return True
|
|
|
|
def collide(x1, x2, y1, y2, w1, w2, h1, h2):
|
|
if x1+w1>x2 and x1<x2+w2 and y1+h1>y2 and y1<y2+h2:return True
|
|
else:return False
|
|
|
|
def die(screen, score):
|
|
f=FONT_SNAKE
|
|
t=f.render(MSG_SNAKE+str(score), True, (0, 0, 0))
|
|
if(score >= SNAKE_SCORE):
|
|
signalWin(4)
|
|
screen.blit(t, (10, 270))
|
|
pygame.display.update()
|
|
pygame.time.wait(2000)
|
|
|
|
############# old main
|
|
######## CONSTANTS
|
|
WINSIZE = [WINSIZEX,WINSIZEY]
|
|
BLOCKSIZE = [18,18]
|
|
UP = 1
|
|
DOWN = 3
|
|
RIGHT = 2
|
|
LEFT = 4
|
|
WHITE = [255, 255, 255]
|
|
|
|
MINX = 100
|
|
MAXX = WINSIZEX - MINX
|
|
MINY = 100
|
|
MAXY = WINSIZEY - MINY
|
|
|
|
|
|
######## VARIABLES
|
|
clock = pygame.time.Clock()
|
|
screen = pygame.display.set_mode(WINSIZE, pygame.NOFRAME)
|
|
pygame.display.set_caption('PozySejf')
|
|
|
|
############# old main
|
|
|
|
|
|
def main():
|
|
global slide
|
|
|
|
passwordLetter = ''
|
|
|
|
startonce = 0
|
|
|
|
while startonce == 0:
|
|
startonce = 0
|
|
#### show initial start screen
|
|
### every while loop for each slide
|
|
|
|
if slide in slides:
|
|
slides[slide].render(screen)
|
|
checkEvents(slides[slide].handle)
|
|
pygame.display.flip()
|
|
clock.tick(15)
|
|
|
|
while slide == SLIDE_SNAKE_DEAD:
|
|
print("resetting snake.")
|
|
slide = SLIDE_SNAKE
|
|
|
|
while slide == SLIDE_KEYBOARD: ## keyboard
|
|
#print("checking events")
|
|
checkEvents()
|
|
|
|
#render backdrop
|
|
screen.fill(WHITE)
|
|
screen.blit(IMG_KEYBOARD, (0, 0))
|
|
|
|
font = FONT_KEYBOARD
|
|
#render keys
|
|
height1 = 480-40
|
|
height2 = 560-40
|
|
height3 = 640-40
|
|
letterMeaning = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ' ']
|
|
letterQM = []
|
|
for i in range(10):
|
|
letterQM.append(pygame.draw.rect(screen,COLOR_KEYBOARD,Rect([i*80 +120,height1],[70 , 70])))
|
|
tt = font.render(letterMeaning[i], True, COLOR_KEYBOARD_TEXT)
|
|
#tt = font.render(str(i), True, (0, 0, 0))
|
|
screen.blit(tt, (i*80 +120+20,height1+15))
|
|
for j in range(9):
|
|
letterQM.append(pygame.draw.rect(screen,COLOR_KEYBOARD,Rect([j*80 +160,height2],[70 , 70])))
|
|
tt = font.render(letterMeaning[j+10], True, COLOR_KEYBOARD_TEXT)
|
|
#tt = font.render(str(j+10), True, (0, 0, 0))
|
|
screen.blit(tt, (j*80 +160+20,height2+15))
|
|
for k in range(8):
|
|
letterQM.append(pygame.draw.rect(screen,COLOR_KEYBOARD,Rect([k*80 +200,height3],[70 , 70])))
|
|
tt = font.render(letterMeaning[k+19], True, COLOR_KEYBOARD_TEXT)
|
|
#tt = font.render(str(k+19), True, (0, 0, 0))
|
|
screen.blit(tt, (k*80 +200+20,height3+15))
|
|
|
|
#render buttons
|
|
enterButton = pygame.draw.rect(screen,COLOR_KEYBOARD,Rect([WINSIZEX/2 - 100 - 70,300],[140 , 70]))
|
|
tt = font.render(MSG_BTN1, True, COLOR_KEYBOARD_TEXT)
|
|
screen.blit(tt, [WINSIZEX/2 - 100 - 70,300])
|
|
clearButton = pygame.draw.rect(screen,COLOR_KEYBOARD,Rect([WINSIZEX/2 + 100 - 70,300],[140 , 70]))
|
|
tt = font.render(MSG_BTN2, True, COLOR_KEYBOARD_TEXT)
|
|
screen.blit(tt, [WINSIZEX/2 + 100 - 70,300])
|
|
#render password as it is typed
|
|
tt = font.render(passwordLetter, True, COLOR_KEYBOARD_TEXT)
|
|
screen.blit(tt, [WINSIZEX/2 - 70,380])
|
|
|
|
#interactive features
|
|
for event in pygame.event.get():
|
|
#if event.type == pygame.QUIT:
|
|
# exit()
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
# Set the x, y postions of the mouse click
|
|
x, y = event.pos
|
|
if enterButton.collidepoint(x, y):
|
|
if passwordLetter == PASSWORD1:
|
|
print 'password correct for task 9.'
|
|
signalWin(9)
|
|
|
|
if passwordLetter == PASSWORD2:
|
|
print 'password correct for task 10.'
|
|
signalWin(10)
|
|
|
|
passwordLetter = ''
|
|
if clearButton.collidepoint(x, y):
|
|
passwordLetter = ''
|
|
|
|
for i in range(10+9+8):
|
|
if letterQM[i].collidepoint(x, y):
|
|
passwordLetter = passwordLetter + letterMeaning[i]
|
|
print passwordLetter
|
|
|
|
pygame.display.flip()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
|