Das ist der Jeopardy-Clone, den ich in Anlehnung an das 36C3-Event geschrieben habe. Wer immer Lust hat, darf und soll Fragen beisteuern
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

597 lines
20 KiB

#!/usr/bin/env python3
# -.-coding=UTF8 -.-
""" hacker jeopardy for hacKNology
A more or less rewrite of the original pygame version
found on then net published by james Turk in 2007
Greetings
kresse 2020
Winter /Spring 2020:
Original local Version
Keyboard Control
Added Buzzer Control
Added png img Presentation in Question
Added local sound
Winter 2020/21:
Buzzer per Webinterface
"""
import os # for getting files
import pygame # for sound, video, img presentation
from pygame.locals import * #
import pygame_input as pgi # import functions to display text with pygame
# for LAN Buzzer
import multiprocessing as mp # threading for LAN_Buzzer network handler
from bottle import * # flask-like web server tool
import my_public_ip # public ip of the system in network
import my_local_ip
class LAN_BUZZER:
"""using a website as a remote buzzer"""
def __init__(self):
self.buzzer_status = -99
def handler(self,queing):
queing.put (" Queing is started ! ",)
try:
my_local_server_address= my_local_ip.my_local_ip()
except:
my_local_server_address="127.0.0.1"
try:
my_server_address= my_public_ip.my_public_ip()
except:
my_server_address= my_local_server_address
my_port = 30000
my_server_address_w_port ="http://"+ my_server_address +":"+ str(my_port)
print(my_server_address_w_port)
LANtoken = ["100","200","300","400"]
LANplayerColor = ["red","yellow","green","lightblue"]
@route("/")
def erklaerung():
erkl = """
<head><title>hacKNerdy</title>
</head>
<body>
<center>
<h1>hacKNerdy remote Buzzer</h1>
<br>
<br>
max. 4 Leute benutzen verschiedene Webseiten,<br>
auf denen ein Buzzer angezeigt wird<br>
<br>
Beim anklicken wird das wie ein lokaler Tastendruck <br>
oder ein lokales Buzzerm verarbeitet, d.h. auch Mischungen aus<br>
lokalem Spielen plus Remote Anbindung sind denkbar.
<br>
<br>
Tools:<br>
<br>
python3<br>
bottle <br>
js<br>
<h3> IP: <br> http://"""+ my_server_address +"</h3><br><br>"
erkl += """<table border>"""
for i in range(4):
erkl += """<tr><td bgcolor ="""
erkl += LANplayerColor[i]
erkl += """ ><h1>"""
erkl += '''<a href="http://''' + my_server_address
erkl += ''':30000/player/''' +LANtoken[i]
erkl += '''">'''
erkl += "Spieler: "+str(i+1)
erkl += """</a><h1></td><tr>"""
erkl += """</table> </center></body>"""
return erkl
@route('/player/<codenumber>')
def login(codenumber):
for i in LANtoken:
#print(i)
if (codenumber == i):
ret = '''<html><body style="background-color:'''
ret += LANplayerColor[ LANtoken.index(i) ] + ''';">'''
ret += '''<center><form action="/pressed/'''
ret += codenumber
ret += '\"'
ret += ''' method="post">
<span ><input name="contestant" value="PRESS" type="submit" style='font-size:100px ' >
</span></form></center>
</body></html>'''
return ret
ret = ''' <center><h1><br><br>TRESPASSER, YOU ARE DOOMED!</center></h1>'''
return ret
@route('/pressed/<loginstr>', method='POST')
def do_login(loginstr):
print (loginstr + " Pressed")
playernumber = LANtoken.index(loginstr)
queing.put(playernumber)
print ("that is: "+ LANplayerColor[playernumber])
# When you need more occlumency use New Seed
newseed = loginstr # try this instead: (int(loginstr)+random.randint(-2,111))
LANtoken[playernumber] =str( newseed )
answer = '''
<script>
window.location.replace("'''
answer += my_server_address_w_port + "/player/" + str(newseed)
answer += '''");
</script>
'''
return answer
try:
run(host= my_local_server_address , port= my_port)#my_server_address , port= my_port )
except:
#my_server_address="127.0.0.1"
#run(host= my_server_address , port= my_port)#my_server_address , port= my_port )
print("NO HTTP available!")
# Constants
# COLORS
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
YELLOW= (255,255,0)
GREEN = (0,255,0)
BLUE = (0,0,200)
LITE_BLUE =(100,100,220)
# Dimension of the display Window
# SOLLTE BESSER ABGEFRAGT WERDEN
x_dim =1200
y_dim =768
# Folder for sound effects
sfx = "assets//sfx//"
graphics = "assets//graphics//"
# global Var
spieler = []
JOYSTICKS = []
spieler_anzahl = 4
_image_library = {}
modus = "aussuchen"
def get_image(path):
'''Graphics import'''
# https://www.raspberry-pi-geek.de/ausgaben/rpg/2014/04/grafikausgaben-mit-python-und-pygame/2/
global _image_library
image = _image_library.get(path)
if image == None:
canonicalized_path = path.replace('/', os.sep).replace('\\', os.sep)
image = pygame.image.load(canonicalized_path)
_image_library[path] = image
return image
class Frage:
def __init__(self,kategorie,punkte,frage,antwort):
self.frage = frage
self.antwort = antwort
self.kategorie = kategorie
self.punkte = punkte
self.farbe = BLUE
class Fragen:
def __init__(self,paketordner):
#print ("Fragen wird aufgerufen")
self.fragenfeld = []
self.anzahl_fragen = 0
self.paketordner = paketordner
f = open(paketordner+"//"+"fragen.jeopardy")
lines = f.readlines()
for line in lines:
data = line.strip().split(':')
self.frageHinzufuegen(*data)
def frageHinzufuegen(self, kategorie, punkte, q, a):
self.fragenfeld.append (Frage(kategorie,punkte ,q,a))
self.anzahl_fragen += 1
#print(self.anzahl_fragen)
# ist 30 nach (0-29)
class Spieler():
"Die teilnehmenden Spieler mit Punkten und Farbe"
def __init__(self,name="SPIELER",nummer=0):
Farbliste = (RED, YELLOW, GREEN, LITE_BLUE)
#
self.name = name
self.punkte = 0
self.nr = nummer % spieler_anzahl
self.color = Farbliste[self.nr]
def buzz():
pygame.mixer.Sound(sfx+'game-fx-1.wav').play()
def percent_tile(x1, y1, x2, y2):
return pygame.Rect( int(x_dim*x1/100), int(y_dim*y1/100), int(x_dim*x2/100), int(y_dim*y2/100) )
def main():
# music intro
music_enabled = True
fullscreen_enabled = False
def textCentered( str, skiperoo=-75):
#
# Rekursive Funktion zur Beschriftung
#
choperoo = len(str)
font = smallFont
# fill background
#screen.blit(background, (0, 0))
# draw string and see if it is too long
text = font.render(str, 1, WHITE)
# if string is too long, choperoo!
while text.get_rect().width > screen.get_rect().width:
for c in range(int(choperoo/2) , int(choperoo)):
if str[c] == ' ':
choperoo = c
break
# redraw string
text = font.render(str[:choperoo], 1, WHITE)
# find the centered rect for the drawing
cr = text.get_rect()
cr.center = screen.get_rect().center
cr.y += skiperoo
# draw
screen.blit(text, cr)
if choperoo != len(str):
textCentered(str[choperoo:], skiperoo+cr.height)
def wandAnzeigen(screen):
# global fragen
BG_COLOR = BLUE
FG_COLOR = WHITE
GRID_COLOR = LITE_BLUE
wall_font_name = fontFile
big_font_size = 90
small_font_size = 48
screen.blit(background, (0, 0))
if modus =="aussuchen":
xStart, xEnd = 0, int(x_dim )
yStart, yEnd = 0, int(y_dim*0.9)
xStep = int(xEnd/6)
yStep = int(yEnd/6)
# Beschriftung
for x in range(0, 6):
ausgabe = " "+(fragen.fragenfeld[x*5].kategorie)
ausgabe = ausgabe.replace('\\n' , '\n ')
pgi.text_output (screen,percent_tile(x*16.6,0,18,20),ausgabe,BG_COLOR,FG_COLOR,wall_font_name,small_font_size)
for y in range(0, 5):
jetztfrage = fragen.fragenfeld[x*5+y]
if jetztfrage.farbe == BLUE:
pgi.text_output (screen,percent_tile(x*16.7, (y+1)*90*(yStep/yEnd), 18, 20), " "+ str(fragen.fragenfeld[x*5+y].punkte), BG_COLOR, FG_COLOR,wall_font_name, big_font_size)
else:
pgi.text_output (screen,percent_tile(x*16.7, (y+1)*90*(yStep/yEnd), 18, 20), "", jetztfrage.farbe, BLACK, wall_font_name, big_font_size)
# Gitter zeichnen
for x in range(xStart, xEnd+1, xStep):
pygame.draw.line(screen, GRID_COLOR, (x, yStart), (x, yEnd), 5)
for y in range(yStart, yEnd+1, yStep):
pygame.draw.line(screen, GRID_COLOR, (xStart, y), (xEnd, y), 5)
# Spieler
for _ in spieler:
pgi.text_output (screen,percent_tile(_.nr * 100 / spieler_anzahl, 100 - 10 , 100 / spieler_anzahl , 10), (_.name + ': ' + str(_.punkte)) , _.color, BLACK, wall_font_name, small_font_size)
def frageAnzeigen(aktuelleFrageNr,fragen,druecker):
question=fragen.fragenfeld[aktuelleFrageNr]
Anzeigetext=question.frage
action =""
if question.farbe == BLUE:
#frage anzeigen
if question.frage[-4:]== ".png":
#Besorge Bild und Zeige es formatfüllend
x=get_image(fragen.paketordner+ "//" + question.frage)
x= pygame.transform.scale(x, (x_dim, y_dim))
screen.blit(x,(0,0))
elif question.frage[-4:]== ".wav":
action = fragen.paketordner + "//"+ question.frage
pgi.text_output (screen,percent_tile(40, 40, 30, 30), "(S)OUND" , BLUE, WHITE, font_name="arial", font_size=40)
else:
#print (Anzeigetext)
textCentered(Anzeigetext)
#
if druecker == -99:
drueckertext="WIR WARTEN...."
pgi.text_output (screen,percent_tile(40, 85, 24, 15), drueckertext, BLUE, BLACK, font_name="arial", font_size=40)
else:
pgi.text_output (screen,percent_tile(40, 85, 24, 15)," "+ druecker.name, (druecker.color), BLACK , font_name="arial", font_size=40)
else:
Anzeigetext = " Die FRAGE HATTEN WIR SCHON !!!"
textCentered(Anzeigetext)
return action
def antwortAnzeigen(aktuelleFrageNr, fragen):
question = fragen.fragenfeld[aktuelleFrageNr]
Anzeigetext = question.antwort
textCentered(Anzeigetext)
x = get_image(graphics+"antwort.png")
x = pygame.transform.scale(x, (int(x_dim), int(y_dim*.2)))
screen.blit(x, (0,int(y_dim*0.79)))
def queueleeren(queue):
while not queing.empty():
foo=queing.get()
print("hacKNerdy .... Neuer! Lauter! Bunter! ")
# initialiseren
pygame.init()
# Buzzer initialisieren
for i in range(0, pygame.joystick.get_count()):
JOYSTICKS.append(pygame.joystick.Joystick(i))
JOYSTICKS[-1].init()
print("Detected joystick '%s'" % JOYSTICKS[-1].get_name())
# LAN_BUZZER initialisieren in Multiprocessing Thread
lan_buzzer= LAN_BUZZER()
queing = mp.Queue()
mp_handler = mp.Process(target=lan_buzzer.handler, args=(queing,))
mp_handler.start()
# intro music
pygame.mixer.music.load(sfx+'theme.mp3')
if music_enabled == True:
pygame.mixer.music.play(0)
# fonts einladen
fontFile = "assets//fonts//Roboto-Medium.ttf"
smallFont = pygame.font.Font(fontFile , 38)
bigFont = pygame.font.Font(fontFile, 70)
# Anzeigetafel initialisieren und mit Titelbild zeigen
screen = pygame.display.set_mode((x_dim, y_dim))
pygame.display.set_caption('hacKNerdy')
pygame.mouse.set_visible(1)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill(BLUE)
pygame.display.flip()
if fullscreen_enabled:
pygame.display.toggle_fullscreen()
x = get_image("assets//graphics//bootsplash.png")
#
x = pygame.transform.scale(x, (x_dim, y_dim))
screen.blit(x, (0,0))
pygame.display.flip()
# Fragenpaket auswählen
# Use Surface at Rect to print Text, with BGColor, TextColor in Font With Size
pgi.text_output(screen, percent_tile(5, 85, 40, 15), "Welches Fragenpaket?", BLACK, GREEN, font_name = "ocra", font_size = 40)
#pgi.text_output(screen, percent_tile(5, 85, 40, 15), "Welches Fragenpaket?", BLACK, GREEN, font_name = "ocra", font_size = 40)
# Use Surface at Rect to input Text, with BGColor, TextColor in Font With Size
fragenpaket = pgi.text_input(screen, percent_tile(45, 85, 40, 15), BLACK, GREEN, font_name = "ocra", font_size = 40)
if fragenpaket == "":
fragenpaket = "1"
print ("Fragenpaket " + fragenpaket + " Gewählt ")
#
#
# Hier muss noch eine Kontrolle rein, ob der Ordner und alle Dateien existieren!
#
#
fragen = Fragen(fragenpaket)
try:
screen.blit(background, (0, 0))
x = get_image(fragenpaket + "//splash.png")
x = pygame.transform.scale(x, (x_dim, y_dim))
screen.blit(x, (0,0))
pygame.display.flip()
#print ("Bild gefunden")
except:
#print("nicht gefunden")
#
screen.blit(background, (0, 0))
x = get_image("assets//graphics//bootsplash.png")
#background = pygame.Surface(screen.get_size())
pygame.display.flip()
x = pygame.transform.scale(x, (x_dim, y_dim))
screen.blit(x, (0,0))
pygame.display.flip()
print ("Warning:\nNo specific splash.png in " + fragenpaket + " found, using default instead")
pass
print (queing.get())
# initialize Spieler
for spieler_nr in range(spieler_anzahl):
# Use Surface at Rect to print Text, with BGColor, TextColor in Font With Size
question = "Name des " + str(spieler_nr+1) + ". Spielers? "
pgi.text_output(screen, percent_tile(5, 85, 40, 15), question, BLUE, WHITE, font_name = fontFile, font_size=40)
# Use Surface at Rect to input Text, with BGColor, TextColor in Font With Size
player_name = pgi.text_input(screen, percent_tile(45, 85, 40, 15), BLUE, WHITE, font_name = fontFile, font_size=40)
spieler.append(Spieler(player_name[0:11], spieler_nr))
# Startstatus
quit = False
druecker = -99
modus = "aussuchen"
mauspos = None
action = ""
aktuelleFrage= -1
# Das Teil für die Framerate
clock = pygame.time.Clock()
# MAIN LOOP
while not quit:
clock.tick(30)
for event in pygame.event.get():
if event.type == QUIT:
quit = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
quit = True
elif event.key == ord('m'):
if music_enabled == False:
pygame.mixer.music.play(0)
music_enabled = True
else:
pygame.mixer.music.stop()
music_enabled = False
elif event.key == ord('y'):
pygame.mixer.Sound(sfx + 'game-fx-1.wav').play()
elif event.key == ord('a'):
pygame.mixer.Sound(sfx + 'game-fx-2.wav').play()
elif event.key == ord('s'):
if (action[-4:]== ".wav"):
pygame.mixer.Sound(action).play()
actionWav = ""
if druecker == -99 and modus == "frage":
for _ in range(spieler_anzahl):
if event.key == (49 + _ ):
druecker = spieler[ _ ]
buzz()
#
elif druecker == -99 and event.type == JOYBUTTONDOWN:
#
if event.button//5 < len(spieler):
druecker = spieler[event.button // 5]
if modus == "frage":
#
buzz()
elif event.type == MOUSEBUTTONDOWN:
mauspos = (pygame.mouse.get_pos())
if modus == "aussuchen":
wandAnzeigen(screen)
if mauspos != None:
# Berechnug der angeklickten Frage
if (int(mauspos[1]/int(0.9*y_dim/6)) > 0) and (int(mauspos[1]/int(0.9*y_dim/6)) < 6):
aktuelleFrage = (int(mauspos[0]/int(x_dim/6))*5 + int(mauspos[1]/int(0.9*y_dim/6))-1 )
modus = "frage"
mauspos = None
druecker= -99
queueleeren(queing)
elif modus == "frage":
screen.blit(background, (0, 0))
action = frageAnzeigen(aktuelleFrage,fragen , druecker)
questionWav= action
if not queing.empty():
lan_buzzer_result = queing.get()
if druecker == -99:
druecker = spieler[lan_buzzer_result]
buzz()
if mauspos != None:
modus = "antwort"
mauspos = None
elif modus == "antwort":
screen.blit(background, (0, 0))
antwortAnzeigen(aktuelleFrage,fragen)
if mauspos != None:
if fragen.fragenfeld[aktuelleFrage].farbe == BLUE:
fragen.anzahl_fragen -= 1
fragen.fragenfeld[aktuelleFrage].farbe = WHITE
# Berechnung Ja/Nicht gewertet/Nein
if ((mauspos[1]/int(0.8*y_dim)) > 0.8) and druecker != -99:
if (0 == int(mauspos[0]/int(x_dim/3))):
# Gültig !
druecker.punkte += int(fragen.fragenfeld[aktuelleFrage].punkte)
fragen.fragenfeld[aktuelleFrage].farbe = druecker.color
elif (2 == int(mauspos[0]/int(x_dim/3))):
# Falsch !
druecker.punkte -= int(fragen.fragenfeld[aktuelleFrage].punkte)
fragen.fragenfeld[aktuelleFrage].punkte= ""
if fragen.anzahl_fragen < 1:
wandAnzeigen(screen)
if music_enabled == True:
while not(pygame.mixer_music.get_busy()):
pygame.mixer.music.play(0)
i = 0
for x in (" "," E"," N"," D"," E"," "):
fragen.fragenfeld[i*5].kategorie = x
i += 1
modus = "aussuchen"
druecker = -99
mauspos = None
pygame.display.flip()
# end of the main loop
mp_handler.kill()
mp_handler.join()
pygame.display.quit()
pygame.quit()
if __name__ == '__main__':
main()
1