heisser_draht/gametest_rev1.py
Patrick Tschuchnig 5e2ecbc98f more fixes
2019-09-12 09:02:51 +02:00

746 lines
26 KiB
Python

#!/usr/bin/env python
#coding=utf-8
############################################
########### GENERAL INFORMATION ############
############################################
# Pinout for assembly:
#
# 3V3 (1) (2) 5V
# - (3) (4) 5V
# - (5) (6) GND
# - (7) (8) -
# GND (9) (10) -
# - (11) (12) -
# - (13) (14) GND
# - (15) (16) pin_blue
# 3V3 (17) (18) pin_green
# - (19) (20) GND
# - (21) (22) pin_red
# - (23) (24) -
# GND (25) (26) -
# - (27) (28) -
# - (29) (30) GND
# - (31) (32) pin_start
# pin_error (33) (34) GND
# - (35) (36) pin_shutdown
# - (37) (38) -
# GND (39) (40) -
# default state of pins:
#
# 32: pull up
# 33: pull up
# 36: pull up
#
# make sure leds are on pull-up default pins
# so when their state is undefined, the led
# strips are switched off!
#
# 22: pull up
# 18: pull up
# 16: pull up
############################################
############# START OF IMPORTS #############
############################################
import pygame, signal, sys, time, os, RPi.GPIO as GPIO
from PIL import Image
from pygame.locals import *
############################################
############## END OF IMPORTS ##############
############################################
############################################
########### START OF DEFINITIONS ###########
############################################
# base pygame settings
pygame_fps = 30 #frames per second setting
pygame_clock = pygame.time.Clock()
# GPIO for Buttons
pin_start = 32
pin_error = 33
pin_shutdown = 36
# GPIO for LED
pin_red = 22
pin_green = 18
pin_blue = 16
# other constants
# every time the wire is touched, some time is added as penalty.
time_per_error = 5
# screen settings
screen_size_x = 1920
screen_size_y = 1080
# name length for highscores
max_name_length = 10
# preset highscores
hs1_name = "Fritz"
hs2_name = "Bernd"
hs3_name = "Max"
hs1_time = 100
hs2_time = 200
hs3_time = 300
############################################
############ END OF DEFINITIONS ############
############################################
############################################
####### START OF PRE-INITIALISATION ########
############################################
# here goes stuff that is requires by functions
# so they can be properly defined
# initialise the game
pygame.init()
# fonts
pygame_font_1 = pygame.font.Font('freesansbold.ttf', 90)
pygame_font_2 = pygame.font.Font('freesansbold.ttf', 65)
pygame_font_3 = pygame.font.Font('freesansbold.ttf', 45)
# colors
pygame_color_green = pygame.Color(42, 217, 13)
pygame_color_black = pygame.Color(0, 0, 0)
pygame_color_white = pygame.Color(255, 255, 255)
pygame_color_yellow = pygame.Color(255, 215, 0)
pygame_color_grey = pygame.Color(196, 202, 206)
pygame_color_brown = pygame.Color(177, 86, 15)
# font colors
pygame_font_main_color = pygame_color_black
# initialise gpio
GPIO.setmode(GPIO.BOARD)
GPIO.setup(pin_start, GPIO.IN)
GPIO.setup(pin_error, GPIO.IN)
GPIO.setup(pin_shutdown, GPIO.IN)
# predefinition of led variables, for use in functions
led_red = 0
led_green = 0
led_blue = 0
# RPi.GPIO does not provide getter methods for PWM, therefor
# defining my own here
led_red_brightness = 0
led_green_brightness = 0
led_blue_brightness = 0
# leds are turned on and can be switched off by default
leds_off_toggle = False
# toggle for the led update to happen every *other* evaluation
# of the timer equation
timer_led_updated = 0
# disabled constant led updating by default
leds_constant_update = False
############################################
######## END OF PRE-INITIALISATION #########
############################################
############################################
###### START OF FUNCTION DEFINITIONS #######
############################################
def get_image_width(filepath):
return ((Image.open(filepath).size)[0])
def get_image_height(filepath):
return ((Image.open(filepath).size)[1])
def signal_handler(sig, frame):
# signal handler, later called to interpret
# ctrl+c as "quit" rather than "abort"
print('Programm exiting')
exit_application()
def led_init():
# initialises the LED pins
# mind that the pins for the LEDs are pullup by default,
# which is on purpose, since this causes the LED strip to
# be turned off when the program isnt running or their state
# is undefined
# set the led pins to be outputs
# careful: these pins dont drive the
# led strip themselves, but rather 3
# n-channel mosfets, type IRLZ34N
global led_red,led_green,led_blue
GPIO.setup(pin_red, GPIO.OUT)
GPIO.setup(pin_green, GPIO.OUT)
GPIO.setup(pin_blue, GPIO.OUT)
# set the pwm frequency to 100
# if you experience strobing, try changing
# the number
led_red = GPIO.PWM(pin_red, 100)
led_blue = GPIO.PWM(pin_blue, 100)
led_green = GPIO.PWM(pin_green, 100)
# start pwm generation with a duty cycle of 0
led_green.start(0)
led_blue.start(0)
led_red.start(0)
def change_red(amount):
# takes an input from 0 to 255 and converts it to Duty Cycle
# valid duty cycle values are between 0 and 100
# stores the new value in led_brightness_COLOUR
# this value is later read by the led_handler to set the
# corresponding duty cycles to allow fast interactive updating
#
# to be used by change_led_colour
global led_red_brightness
factor = 100/255
led_red_brightness = amount*factor
def change_green(amount):
# takes an input from 0 to 255 and converts it to Duty Cycle
# valid duty cycle values are between 0 and 100
# stores the new value in led_brightness_COLOUR
# this value is later read by the led_handler to set the
# corresponding duty cycles to allow fast interactive updating
#
# to be used by change_led_colour
global led_green_brightness
factor = 100/255
led_green_brightness = amount*factor
def change_blue(amount):
# takes an input from 0 to 255 and converts it to Duty Cycle
# valid duty cycle values are between 0 and 100
# stores the new value in led_brightness_COLOUR
# this value is later read by the led_handler to set the
# corresponding duty cycles to allow fast interactive updating
#
# to be used by change_led_colour
global led_blue_brightness
factor = 100/255
led_blue_brightness = amount*factor
def change_led_colour(red_amount,green_amount,blue_amount):
global leds_constant_update, led_red, led_green, led_blue, led_red_brightness, led_green_brightness, led_blue_brightness
# sets an RGB value for the led strip.
if red_amount < 0 or red_amount > 255 or green_amount < 0 or green_amount > 255 or blue_amount < 0 or blue_amount > 255:
# valid rgb values should be between 0 and 255
print('invalid rgb value received: ' + str(red_amount) + ' ,' + str(green_amount) + ' ,' + str(blue_amount))
return false
change_red(red_amount)
change_blue(blue_amount)
change_green(green_amount)
if leds_constant_update == False:
led_red.ChangeDutyCycle(led_red_brightness)
led_green.ChangeDutyCycle(led_green_brightness)
led_blue.ChangeDutyCycle(led_blue_brightness)
def change_led_speed(colour, speed):
# changes the speed of the LED pwm
#
# !!!!OBSOLETE!!!!
#
global led_red,led_green,led_blue
if colour == "red":
led_red.ChangeDutyCycle(speed)
elif colour == "green":
led_green.ChangeDutyCycle(speed)
elif colour == "blue":
led_blue.ChangeDutyCycle(speed)
else:
return false
def led_handler():
global timer_led_blink_toggle, timer_led_switched, timer_led_updated, leds_constant_update
# led blinking effect. can be toggled on or off by setting led_blink to true or false.
# blinks every second-
if leds_constant_update == True:
if led_blink == True:
# this equals to 0 every tick for one second, and 1 every tick for another second
# it requires being handled in a one-shot situation since this is called
# multiple times per second, and will equal to 0 or 1 multiple times per second
# aswell. the real value of get_ticks()/1000 cannot be predetermined.
if int((pygame.time.get_ticks()/1000))%2 == 0:
if timer_led_switched == 0:
timer_led_switched = 1
if timer_led_blink_toggle == 1:
print('BLINK: switching leds off')
leds_toggle()
timer_led_blink_toggle = 0
else:
print('BLINK: reverting colours')
leds_toggle()
timer_led_blink_toggle = 1
else:
timer_led_switched = 0
# update the led duty cycle
# may cause flickering, not sure :)
# TODO: test this
global led_red,led_green,led_blue
# dont update the pwm for the LEDs when they're supposed to be off
# instead, just copy the needed values to the 3 brightness variables!
# the values will be correctly put in once the toggle is back
if leds_constant_update == True:
if leds_off_toggle == False:
if int((pygame.time.get_ticks()/100))%20 == 0:
if timer_led_updated == 0:
timer_led_updated = 1
led_red.ChangeDutyCycle(led_red_brightness)
led_green.ChangeDutyCycle(led_green_brightness)
led_blue.ChangeDutyCycle(led_blue_brightness)
else:
timer_led_updated = 0
def leds_toggle():
global led_red,led_green,led_blue,leds_off_toggle
if leds_off_toggle == False:
led_red.ChangeDutyCycle(0)
led_green.ChangeDutyCycle(0)
led_blue.ChangeDutyCycle(0)
leds_off_toggle = True
else:
leds_off_toggle = False
led_red.ChangeDutyCycle(led_red_brightness)
led_green.ChangeDutyCycle(led_green_brightness)
led_blue.ChangeDutyCycle(led_blue_brightness)
def toggle_fullscreen():
screen = pygame.display.get_surface()
tmp = screen.convert()
caption = pygame.display.get_caption()
cursor = pygame.mouse.get_cursor() # Duoas 16-04-2007
w,h = screen.get_width(),screen.get_height()
flags = screen.get_flags()
bits = screen.get_bitsize()
pygame.display.quit()
pygame.display.init()
screen = pygame.display.set_mode((w,h),flags^FULLSCREEN,bits)
screen.blit(tmp,(0,0))
pygame.display.set_caption(*caption)
pygame.key.set_mods(0) # HACK: work-a-round for a SDL bug??
pygame.mouse.set_cursor( *cursor ) # Duoas 16-04-2007
return screen
def clear_screen():
#print("filled screen")
screen.fill(pygame_color_white)
return
def handle_events():
for event in pygame.event.get():
if event.type == KEYDOWN and event.key == K_ESCAPE:
exit_application()
# disabled in favour of the shutdown pin
#elif event.type == KEYDOWN and event.key == K_q:
# shutdown_raspberry()
return
# shutdown raspberry if shutdown pin is detected
if (GPIO.INPUT(pin_shutdown)):
shutdown_raspberry()
def exit_application():
pygame.quit()
GPIO.cleanup()
sys.exit()
return
def shutdown_raspberry():
pygame.quit()
GPIO.cleanup()
os.system("sudo shutdown -h now")
return
def enter_name():
clear_screen()
print('text entry started')
name = ''
while True:
clear_screen()
highscore_surface = pygame_font_1.render('High Score!', True, pygame_font_main_color)
highscore_rectangle = highscore_surface.get_rect()
highscore_rectangle.topleft = (700, 250)
screen.blit(highscore_surface, highscore_rectangle)
textbox_text_surface = pygame_font_2.render('Enter Name:',True, pygame_font_main_color)
textbox_text_rectangle = textbox_text_surface.get_rect()
textbox_text_rectangle.topleft = (700, 400)
textbox_surface = pygame_font_2.render(str(name), True, pygame_font_main_color)
textbox_rectangle = textbox_surface.get_rect()
textbox_rectangle.topleft = (800, 480)
screen.blit(textbox_text_surface, textbox_text_rectangle)
screen.blit(textbox_surface, textbox_rectangle)
draw_border()
draw_logos()
pygame.display.flip()
for event in pygame.event.get():
if len(name) < max_name_length:
if event.type == KEYDOWN:
if event.key == pygame.K_BACKSPACE:
name = name[:-1]
elif event.key == pygame.K_RETURN and len(name) > 0:
clear_screen()
return name
elif event.key == pygame.K_RETURN:
# do nothing
pass
elif event.key == pygame.K_ESCAPE:
exit_application()
else:
name += event.unicode
elif event.key == pygame.K_BACKSPACE:
name = name[:-1]
elif event.type == KEYDOWN and event.key == pygame.K_RETURN:
clear_screen()
return name
clear_screen()
name = 'Error'
return name
def check_highscores(time):
global hs1_time, hs2_time, hs3_time, hs1_name, hs2_name, hs3_name
if time <= hs1_time:
print ('new high score:'+str(time))
# make the second the third
hs3_time = hs2_time
hs3_name = hs2_name
# make the first the second
hs2_time = hs1_time
hs2_name = hs1_name
# new high score
hs1_time = time
hs1_name = enter_name()
return
elif time <= hs2_time:
print ('new second:'+str(time))
# make the second the third
hs3_time = hs2_time
hs3_name = hs2_name
# new second time
hs2_time = time
hs2_name = enter_name()
return
elif time <= hs3_time:
print ('new third:'+str(time))
hs3_time = time
hs3_name = enter_name()
return
else:
return
def draw_border():
# draw logos
screen.blit(img_itlablogo_image, (screen_size_x-img_itlablogo_imagex-20, screen_size_y-img_itlablogo_imagey-20))
# TODO: Metallic Logo
def draw_logos():
# draw rectangle border around everything
pygame.draw.rect(screen,pygame_color_black, (500, 200, (screen_size_x-500*2), (screen_size_y-200*2)), 5)
def show_debug():
print('#############')
print('Game running: ' + str(game_running))
print('Game ending: ' + str(game_ending))
print('Pin status: ')
print('Start pin: ' + str(GPIO.input(pin_start)))
print('Error pin: ' + str(GPIO.input(pin_error)))
print('Shutdown pin: ' + str(GPIO.input(pin_shutdown)))
print('#############')
############################################
####### END OF FUNCTION DEFINITIONS ########
############################################
############################################
######### START OF INITIALISATION ##########
############################################
# signal handing setup for controlled exit via ctrl+c
signal.signal(signal.SIGINT, signal_handler)
# hide mouse from screen
pygame.mouse.set_visible(False)
# start fullscreen mode with defined screen geometrics
screen = pygame.display.set_mode((screen_size_x, screen_size_y), pygame.FULLSCREEN)
#pygame.display.set_caption('Heisser Draht') # not required for fullscreen application
# currently unused because its not needed
#screen = toggle_fullscreen()
# define image variables
img_itlablogo = 'img/itlablogo.png'
img_itlablogo_image = pygame.image.load(img_itlablogo)
img_itlablogo_imagex = get_image_width(img_itlablogo)
img_itlablogo_imagey = get_image_height(img_itlablogo)
# TODO: Metallic Logo einfügen
#img_metalliclogo = 'img/metalliclogo.png'
#img_metalliclogo_image = pygame.image.load(img_metalliclogo)
#img_metalliclogo_imagex = get_image_width(img_metalliclogo)
#img_metalliclogo_imagey = get_image_height(img_metalliclogo)
# initialise led strip
led_init()
game_running = False
game_just_started = True
game_ending = False
highscore_checked = False
pin_start_inhibit = False
timer_game_ending_started = False
# test: enable led blinking permanently
led_blink = False
timer_led_blink_toggle = 1
timer_led_switched = 0
############################################
########## END OF INITIALISATION ###########
############################################
############################################
######### START OF MAIN GAME LOOP ##########
############################################
while True:
# default actions to be done every cycle
clear_screen()
handle_events()
show_debug()
led_handler()
print(str(pygame.time.get_ticks()))
if game_running == False and game_ending == False:
# one shot for changing the led colour
if game_just_started == True:
print('game just started, changed colour of leds to green')
change_led_colour(10,140,10)
game_just_started = False
# reset all the surfaces to be empty
time_surface = 0
errors_surface = 0
time_rectangle = 0
errors_rectangle = 0
# fill all surfaces again to contain the proper text
header_surface = pygame_font_1.render('Dr' + u'ü' + 'cken Sie Start!', True, pygame_font_main_color)
header_rectangle = header_surface.get_rect()
header_rectangle.topleft = (560, 250)
highscore_header_surface = pygame_font_2.render('Highscores:', True, pygame_font_main_color)
highscore_header_rectangle = highscore_header_surface.get_rect()
highscore_header_rectangle = (770, 410)
score1_surface = pygame_font_3.render(hs1_name + ": " + str(hs1_time) + 's', True, pygame_color_yellow)
score1_rectangle = score1_surface.get_rect()
score1_rectangle.topleft = (820, 540)
score2_surface = pygame_font_3.render(hs2_name + ": " + str(hs2_time) + 's', True, pygame_color_grey)
score2_rectangle = score2_surface.get_rect()
score2_rectangle.topleft = (820, 630)
score3_surface = pygame_font_3.render(hs3_name + ": " + str(hs3_time) + 's', True, pygame_color_brown)
score3_rectangle = score3_surface.get_rect()
score3_rectangle.topleft = (820, 720)
# draw headline, highscore headline, the 3 scores, as well as the logos to the screen
screen.blit(header_surface, header_rectangle)
screen.blit(highscore_header_surface, highscore_header_rectangle)
screen.blit(score1_surface, score1_rectangle)
screen.blit(score2_surface, score2_rectangle)
screen.blit(score3_surface, score3_rectangle)
# display everything
#pygame.display.flip()
# event handling for if the start button is pushed
if GPIO.input(pin_start) == True and pin_start_inhibit == False: #wait for the user to press start button
# change game state to running and not ending
game_running = True
game_ending = False
# set an inhibitor bit to stop the contact from possibly vibrating and
# causing a stop of the game immediately after start
pin_start_inhibit = True
timer_pin_start_inhibit_starttime = pygame.time.get_ticks()
# reset errors
errors = 0
error_added = False # for the 2 tick error counter system, see blow
# change the headline
header_surface = pygame_font_1.render('Spiel l' + u'ä' + 'uft!', True, pygame_font_main_color)
# start the timer
start_time = int(time.time())
# change led colour to blue
change_led_colour(10,10,100)
if game_running == True and game_ending == False:
# if the start pin is inhibited and at least 3 seconds have passed,
# one shot: stop blocking the pin
if pin_start_inhibit == True:
if ((pygame.time.get_ticks()-timer_pin_start_inhibit_starttime)/1000) > 3:
pin_start_inhibit = False
# add errors if error pin is detected, only once per 2 ticks
# TODO: change system to be time based (only 1 error per 500ms or something)
if GPIO.input(pin_error) == True:
if error_added == False:
errors += 1
int_time = int(time.time()) - start_time + errors * time_per_error
error_added = True # for the 2 tick system
else:
error_added = False # for the 2 tick system
# calculate the current time
int_time = int(time.time()) - start_time
# fill the surface with the current time and errors
time_surface = pygame_font_2.render('Zeit: ' + str(int_time) + 's', True, pygame_font_main_color)
time_rectangle = time_surface.get_rect()
time_rectangle.topleft = (640, 480)
errors_surface = pygame_font_2.render('Fehler: ' + str(errors), True, pygame_font_main_color)
errors_rectangle = errors_surface.get_rect()
errors_rectangle.topleft = (640, 560)
clear_screen()
# draw errors, time and headline to the screen
screen.blit(errors_surface, errors_rectangle) #errors
screen.blit(time_surface, time_rectangle) #time
screen.blit(header_surface, header_rectangle) #header
# display everything
#pygame.display.flip()
# if another push of start is detected (i.e. the game is ending!)
if GPIO.input(pin_start) == True and pin_start_inhibit == False:
# change led colour to red
change_led_colour(100,10,10)
# reset the one shot latch for the end of game timer
timer_game_ending_started = False
# change the state to not running and ending
game_running = False
game_ending = True
# set the start inhibitor to prevent an instant restart
# TODO: maybe unneeded due to the strict game state
pin_start_inhibit = True
if game_running == False and game_ending == True:
# check highscores only once when game ended
if highscore_checked == False:
check_highscores(int_time)
highscore_checked = True
# timer for ending the game after 5 seconds
if timer_game_ending_started == False:
timer_game_ending_timout = pygame.time.get_ticks()
timer_game_ending_started = True
clear_screen()
# change the headline to game over
header_surface = pygame_font_1.render('Game over!', True, pygame_font_main_color)
# draw errors, time, and new headline to the screen
screen.blit(errors_surface, errors_rectangle) #errors
screen.blit(time_surface, time_rectangle) #time
screen.blit(header_surface, header_rectangle) #header
# display everything
#pygame.display.flip()
# unneeded timer, replaced by tick based system
#time.sleep(5)
# if 5 seconds have passed since the game state changed to ending (see: timer_game_ending_timeout)
if ((pygame.time.get_ticks()-timer_game_ending_timout)/1000) > 5:
# reset game_just_started flag to enable the one shot led change in the first state
game_just_started = True
# change state
game_running = False
game_ending = False
# reset highscore one shot
highscore_checked = False
# reset timer started one shot
timer_game_ending_started = False
# unblock the start pin
pin_start_inhibit = False
if game_running == True and game_ending == True:
# something went horribly wrong...
# this state is never supposed to be reached
pass
# overlaying structure - this is to be drawn no matter what's below
draw_border()
draw_logos()
pygame.display.flip()
pygame.display.update()
pygame_clock.tick(pygame_fps)
############################################
########## END OF MAIN GAME LOOP ###########
############################################