Python-3-Spieletester gesucht :-) (Software)

Martin Vogel ⌂ @, Dortmund / Bochum, Tue, 13.12.2016, 14:50 (vor 2662 Tagen)

Für einen kleinen Softwaretest, bei dem ich die Kommunikation eines lokalen Pythonprogramms mit einem serverbasierten Programm testen möchte, brauche ich mal eben ein paar Freiwillige.

Damit der Test nicht allzu langweilig abläuft, ist er mit einem kleinen Spiel verbunden, das mit einer Online-Highscoreliste gekoppelt ist. Wer es schafft, eine ansehnliche Punktzahl zu erreichen, ist herzlich eingeladen, seinen Spielernamen in die Highscoreliste einzutragen.

Das Programm benötigt eine existierende Python-3-Installation unter Windows, Linux oder Mac OS.

[image]

Zum Spielen einfach die ZIP-Datei herunterladen, auspacken und das Pythonprogramm ausführen.
Gestartet wird es mit der Eingabetaste, gesteuert mit den Pfeiltasten.

[Update: Neue Version]

Jetzt mit noch besserer Grafik, viel mehr erreichbaren Punkten und einer Belohnung für schnelle Auffassungsgabe!

Jeder Hamster lässt die Schlange um 5 Segmente wachsen und 5% schneller werden. Für den ersten Hamster gibt es 100 Punkte, für den zweiten 200 und so weiter. Für jeden Bewegungsschritt wird jedoch wieder ein Punkt abgezogen. Umwege lohnen sich hier also nicht.

Das Spiel endet, wenn die Schlange gegen die Wand stößt oder mit sich selbst kollidiert.

Download: [image]ZIP-Archiv_48FZMD6Q1.zip

  1. #! /usr/bin/python3
  2.  
  3. from tkinter import Tk, Canvas, PhotoImage
  4. from tkinter.messagebox import askyesno as frage
  5. from tkinter.simpledialog import askstring as eingabe
  6. from random import randint, choice
  7. from getpass import getuser
  8. from webbrowser import open as showpage
  9. from urllib.parse import urlencode
  10. from time import sleep
  11.  
  12. # Python vs. Hamster
  13. #
  14. # Ein Beispielprogramm zur Auswertung der Daten von Eventhandlern für
  15. # Tastendrücke und zur simplen Datenübermittlung an eine Webseite
  16. #
  17. # Version vom 16. Januar 2017
  18. # Autor: Dipl.-Ing. Martin Vogel, Hochschule Bochum
  19. # Lizenz: cc-by-3.0 https://creativecommons.org/licenses/by/3.0/de/
  20. #
  21. # Diese Abwandlung des klassischen Snake-Spiels lässt eine Schlange
  22. # kleine blaue Java-Hamster fressen.
  23. #
  24. # Das Spielfeld ist 29×29 Felder groß und wird auf einer Canvas in 24-facher
  25. # Größe wiedergegeben.
  26. #
  27. # Die Schlange wird mit den Cursortasten gesteuert.
  28. #
  29. # Jeder Hamster lässt die Schlange um 5 Segmente wachsen und 5% schneller
  30. # werden. Für den ersten Hamster gibt es 100 Punkte, für den zweiten 200
  31. # und so weiter. Für jeden Bewegungsschritt wird wieder ein Punkt abgezogen.
  32. #
  33. # Das Spiel endet, wenn die Schlange gegen die Wand stößt oder mit sich
  34. # selbst kollidiert.
  35. #
  36. # Auf einer Webseite lässt sich durch das Programm ein Highscorewert eintragen.
  37. url = "http://ruhrhochschule.de/hamsterscore.py"
  38. #
  39. # Vorlage für den Hamsterkopf war das bekannte Motiv von Daniel Jasper und
  40. # Dietrich Boles aus ihrem freien Programmierlernprogramm Hamster-Simulator.
  41. #
  42.  
  43.  
  44. class countdown:
  45. # Zählt von 3 bis 1 rückwärts.
  46. # Jede anfangs riesige bildfüllende Ziffer wird innerhalb einer Sekunde
  47. # immer kleiner.
  48. def __init__(self):
  49. self.counter = 3
  50. # Ziffer rot und mittig anzeigen
  51. self.text = C.create_text(360, 360, fill="red")
  52. self.count()
  53. C.config(cursor="none")
  54.  
  55. def count(self):
  56. # Der counter zählt Sekundenbruchteile, angezeigt werden nur
  57. # die ganzen Sekunden.
  58. n = int(self.counter) + 1
  59. # Zifferngröße in Pixeln entspricht dem Nachkommawert mal 1000
  60. s = (self.counter*1000) % 1000
  61. # Neue Größe setzen
  62. C.itemconfig(self.text, text=n, font="helvetica -%i bold" % s)
  63. # Alle 17 ms soll ein Animationsschritt erfolgen.
  64. self.counter -= 0.017
  65. if self.counter > 0:
  66. C.after(17, self.count)
  67. else:
  68. C.delete(self.text)
  69. Schlange.Start()
  70.  
  71.  
  72. def newcountdown(event=None):
  73. # Erzeugt ein neues countdown-Objekt der Canvas
  74. C.delete("Schlange")
  75. Hamster.verstecken()
  76. C.countdown = countdown()
  77.  
  78.  
  79. def username():
  80. "Gibt den Spielernamen zurück"
  81. # Wurde der schon eingegeben?
  82. if hasattr(Hamster, "username"):
  83. un = Hamster.username
  84. else:
  85. # Fragen wir mal das Betriebssystem nach dem
  86. # Anmeldenamen.
  87. un = getuser()
  88. # Der kann übernommen oder geändert werden.
  89. un = eingabe("Hallo, äh … "+un+"?",
  90. "Gib Deinen Namen ein:",
  91. initialvalue=un)
  92. # Eingabe mit "OK" bestätigt?
  93. if un:
  94. Hamster.username = un
  95. # Oder Eingabedialog abgebrochen?
  96. else:
  97. un = ""
  98. return un
  99.  
  100.  
  101. class C_Hamster:
  102. "Der Hamster weiß, wo er ist, wie er aussieht und alles über den Spieler."
  103. def __init__(self):
  104. # Das Hamsterbild muss im Verzeichnis des Pythonprogramms liegen
  105. try:
  106. self.bilddatei = PhotoImage(file="Hamster24.gif")
  107. self.bild_ID = C.create_image(-24, -24, image=self.bilddatei)
  108. except:
  109. # Falls nicht, gibt's 'nen schnöden Ersatzhamster: ⬤ u2b24
  110. self.bild_ID = C.create_text(-24, -24, text="⬤",
  111. font="courier -24 bold",
  112. fill="blue")
  113. # persönliche Bestleistung
  114. self.highscore = 0
  115. self.reset()
  116.  
  117. def reset(self):
  118. # Anzahl der Wiederbelebungen
  119. self.zähler = 0
  120.  
  121. # Aktuell erreichte Punktzahl
  122. self.punkte = 0
  123.  
  124. #
  125. # Philosophischer Einschub:
  126. #
  127. # Wieso bekommt eigentlich der Hamster die Punkte und nicht die
  128. # Schlange?
  129. #
  130. # Wer glaubt, dass es in diesem Spiel darum geht, Hamster zu
  131. # besiegen, hat die Fabel vom Hasen und Igel nicht verstanden.
  132. # In Wirklichkeit gewinnt nämlich immer der Hamster und die
  133. # Schlange stirbt.
  134. #
  135. # Schockierend. Ich weiß.
  136. #
  137.  
  138. #
  139. # Mooment! Wieso gibt es dann umso mehr Punkte für den Hamster, je
  140. # häufiger die Schlange gefüttert wird?
  141. #
  142. # OK, das war vorhin doch nicht die ganze Wahrheit über das Spiel,
  143. # in wirklicher Wirklichkeit gibt es die Punkte für die heimliche
  144. # illegale Entsorgung der ganzen veganen Biodinkelriegel, die der
  145. # Hamster versehentlich gehortet hat.
  146. #
  147. # Jetzt aber weiter im Quelltext:
  148. #
  149.  
  150. def umsetzen(self):
  151. """Setzt den Hamster auf ein zufälliges Feld, das noch nicht von
  152. der Schlange belegt ist."""
  153. while True:
  154. # Irgendwelche Koordinaten 3 Felder vom Rand entfernt auswürfeln
  155. self.xy = randint(3, 27), randint(3, 27)
  156. # Die Felder rings um den Schlangenkopf sind tabu:
  157. umKopf = [(Schlange.Felder[0][0]+x, Schlange.Felder[0][1]+y)
  158. for x in [-1,0,1] for y in [-1,0,1]]
  159. # Ist das außerhalb der Schlange?
  160. if self.xy not in umKopf+Schlange.Felder:
  161. # Freies Feld gefunden, Endlosschleife abbrechen!
  162. break
  163.  
  164. # Das Bild auf die neue Canvas-Position verschieben
  165. x, y = self.xy
  166. C.coords(Hamster.bild_ID, 24*x, 24*y)
  167. T.title("Füttere den Python mit dem %i. Java-Hamster!" %
  168. (self.zähler+1))
  169.  
  170. def verstecken(self):
  171. "Setzt den Hamster auf eine nicht sichtbare Koordinate"
  172. C.coords(Hamster.bild_ID, -100, -100)
  173.  
  174.  
  175. def Highscore(Grund, Punkte):
  176. "Zeigt den Highscore-Dialog an"
  177. # Kennen wir uns?
  178. try:
  179. un = ", "+Hamster.username
  180. # nö.
  181. except AttributeError:
  182. un = ""
  183.  
  184. # Wie laut müssen wir jubeln?
  185. if Punkte > Hamster.highscore:
  186. # Konfetti, Sekt, Fanfaren!
  187. Jubel = "Bravo"+un+", das ist eine neue persönliche "
  188. "Bestleistung!nn"
  189. # Das wird der neue Highscore
  190. Hamster.highscore = Punkte
  191.  
  192. elif Punkte:
  193. # Schlechter geworden?
  194. Jubel = "Beim nächsten Mal schaffst Du wieder mehr"+un+"!nn"
  195.  
  196. else:
  197. # 0 Punkte?
  198. Jubel = "Tipp: Steuere den Python mit den Pfeiltasten!nn"
  199. # Mauszeiger wieder anzeigen
  200. C.config(cursor="arrow")
  201. # Dialog anzeigen
  202. if frage(Grund, "Du hast %i Hamsterpunkte!nn" % Punkte + Jubel +
  203. "Möchtest Du Dich in die Highscoreliste eintragen?"):
  204. showpage(url+"?"+urlencode((("n", username()), ("p", Punkte))))
  205. T.title("Neustart mit der Eingabetaste!")
  206. else:
  207. T.title("Mache Dich bereit!")
  208. newcountdown()
  209.  
  210.  
  211. class C_Schlange:
  212. """Die Schlange merkt sich die durch sie belegten Felder und deren
  213. Canvas-Objekte sowie die letzte Hamstermahlzeit."""
  214.  
  215. def __init__(self):
  216. self.reset()
  217.  
  218. def reset(self):
  219. "Startwerte der Schlange"
  220. # Zufällige Startpostition 10 Felder vom Rand entfernt
  221. self.x = randint(10, 20)
  222. self.dx = 0
  223. self.y = randint(10, 20)
  224. self.dy = 0
  225. # Zufällige Richtung: nach rechts, links, oben oder unten
  226. self.Richtung(choice("rlou"))
  227. # Die Elemente der Liste "Felder" bestehen aus Tupeln
  228. # (x-Position, y-Position).
  229. # Der Kopf der Schlange ist auf (self.x, self.y)
  230. self.Felder = [(self.x, self.y)]
  231. # Vier Rumpfsegmente entgegen der Bewegungsrichtung anhängen:
  232. for i in range(1, 5):
  233. self.Felder.append((self.x-i*self.dx, self.y-i*self.dy))
  234. # Wartezeit zwischen zwei Schritten: anfangs 100 Millisekunden,
  235. # später wird die Schlange immer schneller.
  236. self.ms = 100
  237. # Die Schlange braucht 5 Schritte, um eine Mahlzeit zu verdauen und
  238. # dabei zu wachsen. Jetzt ist der Magen gerade leer.
  239. self.Mageninhalt = 0
  240. # Falls pro Bewegungsschritt mehrere Tasten gedrückt werden,
  241. # kommen die in einen Puffer, sodass pro Schritt nur eine
  242. # Richtungsänderung vorgenommen wird.
  243. self.Tastenpuffer = []
  244.  
  245. def Richtung(self, rlou):
  246. "Setzt den Delta-x- bzw. -y-Wert für die nächsten Bewegungsschritte"
  247. # Unmittelbar tödliche 180°-Kehren werden nicht zugelassen
  248. if rlou == "l":
  249. if not self.dx:
  250. self.dx = -1
  251. elif rlou == "r":
  252. if not self.dx:
  253. self.dx = 1
  254. else:
  255. self.dx = 0
  256. if rlou == "u":
  257. if not self.dy:
  258. self.dy = 1
  259. elif rlou == "o":
  260. if not self.dy:
  261. self.dy = -1
  262. else:
  263. self.dy = 0
  264.  
  265. def Taste(self, key):
  266. "Hängt einen Tastendruck an den Eingabepuffer an."
  267. self.Tastenpuffer.append(key)
  268.  
  269. def zeichneKopf(self):
  270. "Setzt den Kopf in richtiger Orientierung auf sein neues Feld."
  271. x, y = self.Felder[0]
  272. # Trapezförmigen Kopf je nach Bewegungsrichtung drehen:
  273. C.coords(self.Kopf,
  274. 24*x-10-2*self.dy, 24*y-10-2*self.dx,
  275. 24*x+10+2*self.dy, 24*y-10+2*self.dx,
  276. 24*x+10-2*self.dy, 24*y+10-2*self.dx,
  277. 24*x-10+2*self.dy, 24*y+10+2*self.dx)
  278. # Augenlinie (gestrichelt) von links nach rechts:
  279. C.coords(self.Augen,
  280. 24*x-2*self.dx-10*self.dy, 24*y-2*self.dy-10*self.dx,
  281. 24*x-2*self.dx+10*self.dy, 24*y-2*self.dy+10*self.dx)
  282.  
  283. def Start(self, event=None):
  284. "Setzt den Hamster um und baut die Schlange neu auf"
  285. # Schlange zeichnen: zuunterst liegt eine dicke blaue Linie.
  286. self.Körper = [C.create_line(self.Felder,
  287. fill="blue", smooth=1, tag="Schlange",
  288. joinstyle="round", capstyle="round",
  289. width=24), None]
  290. # Der Kopf – und später die Augen – werden hier nur vorbereitet, um die
  291. # Zeichenreihenfolge festzulegen; ihre Koordinaten erhalten die beiden
  292. # Canvas-Objekte erst in der Methode "zeichneKopf".
  293. self.Kopf = C.create_polygon(0, 0, 0, 0, width=4, fill="yellow",
  294. outline="blue", joinstyle="bevel",
  295. tag="Schlange")
  296. # Die gelbe Füllung des Körpers liegt in der Zeichenreihefolge über dem
  297. # Kopf, damit die "Nackenlinie" nicht zu sehen ist. Beide Körperlinien
  298. # werden in einer Liste gespeichert, damit sie mittels einer Schleife
  299. # modifiziert werden können.
  300. self.Körper[1] = C.create_line(self.Felder,
  301. fill="yellow", smooth=1, tag="Schlange",
  302. joinstyle="round", capstyle="round",
  303. width=16, dash=(6,13))
  304. # Die Augen liegen zuoberst und bestehen aus einer einzigen
  305. # geschickt gestrichelten Linie: "* *".
  306. self.Augen = C.create_line(0, 0, 0, 0, width=8, fill="black",
  307. tag="Schlange", capstyle="round",
  308. dash=(5,11,5))
  309.  
  310. # Hamster umsetzen
  311. # (In Wahrheit wird der nämlich gar nicht gefressen, sondern
  312. # immer rechtzeitig weggebeamt. Die Schlange erhält stattdessen
  313. # einen veganen Biodinkelriegel.)
  314. Hamster.umsetzen()
  315. # Go!
  316. C.after(self.ms, self.Bewege)
  317.  
  318. def Stirb(self, innenFarbe, außenFarbe):
  319. "Animation zeigt durch Farbveränderung das Ende der Spielrunde an."
  320. # Die Schlange verändert innerhalb einer Sekunde ihre
  321. # Farbe vom Kopf bis zur Schwanzspitze.
  322.  
  323. # Kopf heben und Augen weit aufreißen!
  324. C.tag_raise(self.Kopf)
  325. C.tag_raise(self.Augen)
  326. C.itemconfig(self.Augen, width=20)
  327. C.update()
  328. sleep(0.1)
  329.  
  330. # Augenfarbe ändern
  331. C.itemconfig(self.Augen, fill=außenFarbe)
  332. C.update()
  333. sleep(0.1)
  334.  
  335. # Kopfinneres umfärben
  336. C.itemconfig(self.Kopf, fill=innenFarbe)
  337. C.update()
  338. sleep(0.1)
  339.  
  340. # Kopfrand umfärben
  341. C.itemconfig(self.Kopf, outline=außenFarbe)
  342. C.update()
  343. sleep(0.1)
  344.  
  345. # Körperinneres umfärben
  346. C.itemconfig(self.Körper[1], fill=innenFarbe)
  347. C.update()
  348. sleep(0.1)
  349.  
  350. # Augen schließen …
  351. C.itemconfig(self.Körper[0], fill=außenFarbe)
  352. for i in range(20,1,-1):
  353. C.itemconfig(self.Augen, width=i)
  354. C.update()
  355. sleep(0.02)
  356.  
  357.  
  358. def Bewege(self):
  359. "Alle paar Millisekunden bewegt sich die Schlange ein Feld weiter"
  360. # Taste gedrückt?
  361. if self.Tastenpuffer:
  362. self.Richtung(self.Tastenpuffer.pop(0))
  363.  
  364. # Neue Kopfposition
  365. self.x += self.dx
  366. self.y += self.dy
  367.  
  368. # Ist da der Hamster?
  369. if (self.x, self.y) == Hamster.xy:
  370. # Punktzahl erhöhen
  371. Hamster.zähler += 1
  372. Hamster.punkte += 100 * Hamster.zähler
  373. # Ein Hamster verlängert die Schlange um 5 Felder
  374. self.Mageninhalt = 5
  375. # Die Schlange wird 5% schneller
  376. self.ms = int(0.95*self.ms)
  377. # Die nächste Mahlzeit materialisiert sich irgendwo
  378. Hamster.umsetzen()
  379.  
  380. # Spielfeldende erreicht?
  381. if self.x < 1 or self.x > 29 or self.y < 1 or self.y > 29:
  382. # Farbveränderung
  383. self.Stirb("black", "grey")
  384.  
  385. # Auswertungsdialog
  386. Highscore("Der Python ist vor die Wand geprallt.", Hamster.punkte)
  387.  
  388. # Schlange und Hamster zurücksetzen
  389. self.reset()
  390. Hamster.reset()
  391. return
  392.  
  393. # Ist gerade etwas furchtbar dummes passiert?
  394. selbstgebissen = False
  395. for Feld in self.Felder[:-1]:
  396. # Wenn an der neuen Kopfposition schon ein Schlangensegment ist:
  397. if (self.x, self.y) == Feld:
  398. selbstgebissen = True
  399. # Die Runde ist zu Ende, aber wir melden das erst gleich, wenn
  400. # die Schlange sich noch einen Schritt bewegt hat.
  401. break
  402.  
  403. # Ist noch etwas vom letzten Hamster übrig?
  404. if self.Mageninhalt:
  405. # bisschen verdauen
  406. self.Mageninhalt -= 1
  407. # Die Schlange wächst nun. Weil gleich das letzte
  408. # Feld abgeschnitten wird, hängen wir ein Dummy-Feld an.
  409. self.Felder.append("dummy")
  410.  
  411. # Am Kopf ein neues Feld anfügen, Schwanzfeld entfernen.
  412. # Das Polygon wird als Trapez gezeichnet, dessen kurze Seite
  413. # in Bewegungsrichtung "schaut".
  414. self.Felder = [(self.x, self.y)] + self.Felder[:-1]
  415. self.zeichneKopf()
  416.  
  417. # Die Schlange gleitet weiter. Die Schleife ist nötig, weil
  418. # der gelbe Schlangenkörper und der blaue Rand zwei eigene
  419. # Canvas-Objekte sind.
  420. for K in self.Körper:
  421. C.coords(K, [xy*24 for Feld in self.Felder for xy in Feld])
  422.  
  423. # War vorhin eigentlich was?
  424. if selbstgebissen:
  425. # Farbveränderung
  426. self.Stirb("red", "darkred")
  427.  
  428. # Auswertungsdialog
  429. Highscore("Der Python hat sich selbst gebissen.",
  430. Hamster.punkte)
  431.  
  432. # Schlange und Hamster zurücksetzen
  433. self.reset()
  434. Hamster.reset()
  435. return
  436.  
  437. # Punkteabzug wegen Trödelns:
  438. if Hamster.punkte:
  439. Hamster.punkte -= 1
  440. C.itemconfig(C.zähler, text="%05i" % Hamster.punkte)
  441.  
  442. # I'll be back
  443. C.after(self.ms, self.Bewege)
  444.  
  445. # Programmfenster
  446. T = Tk()
  447. T.title("Python-Hamster: Steuerung mit Pfeiltasten")
  448.  
  449. # Leinwand
  450. C = Canvas(width=720, height=720, bg="#e0ffe0", highlightthickness=0)
  451. C.pack()
  452.  
  453. # Spielfeldrand
  454. C.create_rectangle(6, 6, 714, 714, width=12, outline="#703030")
  455.  
  456. # Fugen im Spielfeldrand
  457. for a in range(61):
  458. for b, c in ((-1, "#501000"), (0, "#905030")):
  459. C.create_line(b+a*12, 0, b+a*12, 719, fill=c)
  460. C.create_line(0, b+a*12, 719, b+a*12, fill=c)
  461.  
  462. # Bodenkacheln
  463. for x in range(29):
  464. for y in range(29):
  465. # rot- und blau-Anteil der Kachelfarbe sind veränderlich,
  466. # grün hat immer den Wert 0xff.
  467. rb = randint(210, 230)
  468. farbe = "#%0xff%0x" % (rb, rb)
  469. C.create_rectangle(24*x+12, 24*y+12,
  470. 24*x+35, 24*y+35,
  471. fill=farbe, outline=farbe)
  472.  
  473. # Punktezähler
  474. C.zähler = C.create_text(360, 360,
  475. text="00000",
  476. font="courier 96 bold",
  477. fill="#d0efd0")
  478.  
  479. # Der Hamster ist ein Objekt der Klasse C_Hamster
  480. Hamster = C_Hamster()
  481.  
  482. # Die Schlange ist ein Objekt der Klasse C_Schlange
  483. Schlange = C_Schlange()
  484. T.bind("<Return>", newcountdown)
  485.  
  486. # Pfeiltasten an zugehörige Funktionen binden
  487. T.bind("<Left>", lambda event: Schlange.Taste("l"))
  488. T.bind("<Right>", lambda event: Schlange.Taste("r"))
  489. T.bind("<Up>", lambda event: Schlange.Taste("o"))
  490. T.bind("<Down>", lambda event: Schlange.Taste("u"))
  491.  
  492. # Los geht’s
  493. countdown()
  494.  
  495. T.mainloop()

--
Dipl.-Ing. Martin Vogel
Leiter des Bauforums

Bücher:
CAD mit BricsCAD
Bauinformatik mit Python

Tags:
Python, Grafik, Spiel


gesamter Thread:

 RSS-Feed dieser Diskussion

powered by my little forum