.. include:: /index.rst :start-after: start_hello_message :end-before: end_hello_message .. _py_blindfolded_watermelon_game: (Beispiel) Blindfold-Wassermelonen-Zerschlagspiel ==================================================== **Einführung** Dieses Projekt erstellt ein interaktives **Blindfold-Wassermelonen-Zerschlagspiel**, bei dem sich Spieler mithilfe eines Joysticks auf einem 20×20-Meter-Raster bewegen und dabei auf die Richtungsanweisungen eines AI-Assistenten angewiesen sind. Das System integriert: 1. **Joystick-Steuerung** für die Bewegung des Spielers auf der X-/Y-Achse 2. **AI-gestützte Führung** mit OpenAI GPT-4 3. **Text-to-Speech-Feedback** mit Pico2Wave 4. **Zufällige Zielgenerierung** für die Position der Wassermelone 5. **Interaktive Taste** für die Schlagaktion .. raw:: html Der Spieler startet in der Mitte bei (0,0) und muss eine zufällig platzierte Wassermelone finden, wobei er sich ausschließlich auf die Audiohinweise des AI-Assistenten verlässt. Dadurch entsteht ein spannendes Spielerlebnis mit sensorischer Einschränkung. Sie können verschiedene Eingabegeräte mit LLM-Modulen kombinieren, um interaktive AI-Spiele zu entwickeln. Siehe: * :ref:`py_online_llm` * :ref:`tts_espeak_pico2wave` * :ref:`py_joystick` ---------------------------------------------- **Was Sie benötigen** Für dieses Projekt werden die folgenden Komponenten benötigt: .. list-table:: :widths: 30 20 :header-rows: 1 * - COMPONENT - PURCHASE LINK * - :ref:`cpn_joystick` - \- * - :ref:`cpn_button` - |link_button_buy| * - :ref:`cpn_fusion_hat` - \- * - :ref:`cpn_wires` - |link_wires_buy| * - Raspberry Pi - \- ---------------------------------------------- **Schaltplan** Verbinden Sie die Komponenten wie folgt mit dem Fusion HAT+: .. image:: img/fzz/watermelon_game_bb.png :width: 80% :align: center ---------------------------------------------- .. include:: python_online_llms.rst :start-after: start_setup_openai :end-before: end_setup_openai ---------------------------------------------- **Beispiel ausführen** #. Code ausführen .. raw:: html .. code-block:: shell cd ~/ai-lab-kit/llm sudo python3 llm_openai_blindfolded_game.py #. Das Spiel spielen Nach dem Start des Skripts wird die Wassermelone zufällig auf dem 20×20 Meter großen Spielfeld platziert. Bewegen Sie sich mit dem Joystick Schritt für Schritt und hören Sie auf die Richtungsanweisungen des AI-Assistenten. Wenn Sie glauben, die Position der Wassermelone erreicht zu haben, drücken Sie die Taste, um zuzuschlagen. Stimmen Ihre Koordinaten exakt mit der Position der Wassermelone überein, gewinnen Sie das Spiel. #. Spielmechanik verstehen * Koordinatensystem: - Das Spielfeld ist ein 20×20-Meter-Raster - Die Koordinaten reichen von (-10,-10) bis (10,10) - Positives X = Osten, negatives X = Westen - Positives Y = Süden, negatives Y = Norden (invertierte Y-Achse) - Der Mittelpunkt ist (0,0) * Bewegungsregeln: - Joystick nach rechts → X+1 (Osten) - Joystick nach links → X-1 (Westen) - Joystick nach oben → Y-1 (Norden) - Joystick nach unten → Y+1 (Süden) - Jede Bewegung verändert die Position um 1 Meter * Gewinnbedingung: - Der Spieler muss sich genau auf den Koordinaten der Wassermelone befinden - Drücken Sie die Taste, um an der aktuellen Position zu „zerschlagen“ - Bei exakter Übereinstimmung endet das Spiel mit einer Siegmeldung * Rolle des AI-Assistenten: - Erhält sowohl die Koordinaten des Spielers als auch der Wassermelone - Gibt Richtungsanweisungen anhand der Himmelsrichtungen (N, NE, E, SE, S, SW, W, NW) - Gibt eine ungefähre Entfernung in Metern an - Hält die Antworten kurz, damit sie sich gut für die Audiowiedergabe eignen **Code** Hier ist das vollständige Python-Skript für das Blindfold-Wassermelonen-Zerschlagspiel: .. raw:: html .. code-block:: python from fusion_hat.llm import OpenAI from secret import OPENAI_API_KEY from fusion_hat.adc import ADC from fusion_hat.pin import Pin from fusion_hat.tts import Pico2Wave import random, time # Register OpenAI API # openai.com # Export your openai api key with :LLM_API_KEY # export LLM_API_KEY=sk-xxxxxxxxxxxxxxxxx # Setup TTS tts = Pico2Wave() tts.set_lang('en-US') # Setup Joystick btn_pin = Pin(17, mode=Pin.IN, pull=Pin.PULL_UP, bounce_time=0.05) x_axis = ADC('A1') y_axis = ADC('A0') def MAP(x, in_min, in_max, out_min, out_max): """ Map a value from one range to another. """ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min def activate(): global smash_tips smash_tips = True btn_pin.when_activated = activate # Setup LLM INSTRUCTIONS = "This is a blindfolded watermelon-smashing game. A point representing a watermelon is randomly generated within a 20x20 meter area with coordinates ranging from (-10,-10) to (10,10). The player starts from the origin (0,0) and moves using a joystick. Even if the player can't see anything, they press a button to perform a smash action. After smashing, you will receive the watermelon's and player's coordinates. You need to advise the player on the direction of the watermelon, like 'The watermelon is ten meters to your northeast.' If the smash coordinates match, the game ends. Your responses will be converted into speech via TTS, so please keep them brief, ideally within two sentences." WELCOME = "Hello, I am Blindfolded Watermelon Smashing Game Assistant. Use the joystick to move and press the button to smash. I will guide you to find the watermelon. Good luck!" llm = OpenAI( api_key=OPENAI_API_KEY, model="gpt-4o", ) # Set how many messages to keep llm.set_max_messages(20) # Set instructions llm.set_instructions(INSTRUCTIONS) # Set welcome message llm.set_welcome(WELCOME) print(WELCOME) # Define the map size and the joystick pins watermelon_x, watermelon_y = random.randint(-10, 10), random.randint(-10, 10) player_x, player_y = 0, 0 smash_tips = False while True: x_val = MAP(x_axis.read(), 0, 4095, -100, 100) y_val = MAP(y_axis.read(), 0, 4095, -100, 100) if x_val > 80: player_x += 1 elif x_val < -80: player_x -= 1 if y_val > 80: player_y -= 1 elif y_val < -80: player_y += 1 # Debug positions (commented out in actual game) # print('Watermelon position: %d, %d ' % (watermelon_x, watermelon_y)) # print('Player position: %d, %d ' % (player_x, player_y)) time.sleep(0.3) if smash_tips: smash_tips = False print("Smash!") if (player_x, player_y) == (watermelon_x, watermelon_y): print("Target hit!") tts.say("Target hit!") break else: input_text = f"Watermelon position: ({watermelon_x}, {watermelon_y}), Player position: ({player_x}, {player_y})" # Response with stream response = llm.prompt(input_text, stream=True) string = "" for next_word in response: if next_word: # print(next_word, end="", flush=True) # Uncomment for streaming display string += next_word # print("") # New line after streaming print("AI: " + string) tts.say(string) print("Game over!") ---------------------------------------------- **Code verstehen** 1. Text-to-Speech-Einrichtung Das Spiel verwendet Pico2Wave für die Audioausgabe: .. code-block:: python tts = Pico2Wave() tts.set_lang('en-US') Dadurch werden die Textantworten der AI in gesprochene englische Anweisungen umgewandelt. 2. Verarbeitung der Joystick-Eingaben Der Joystick verwendet zwei ADC-Kanäle zum Auslesen der X- und Y-Achse: .. code-block:: python x_axis = ADC('A1') # Horizontale Bewegung y_axis = ADC('A0') # Vertikale Bewegung def MAP(x, in_min, in_max, out_min, out_max): return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min # ADC-Wert von 0–4095 auf Bereich -100 bis 100 umrechnen x_val = MAP(x_axis.read(), 0, 4095, -100, 100) y_val = MAP(y_axis.read(), 0, 4095, -100, 100) 3. Taster mit Interrupt einrichten Der Taster verwendet einen Interrupt-Callback für eine sofortige Reaktion: .. code-block:: python btn_pin = Pin(17, mode=Pin.IN, pull=Pin.PULL_UP, bounce_time=0.05) def activate(): global smash_tips smash_tips = True btn_pin.when_activated = activate Wenn der Taster gedrückt wird, setzt er ``smash_tips`` auf ``True`` und löst dadurch im Hauptprogramm die Schlagaktion aus. 4. OpenAI-LLM-Konfiguration Der AI-Assistent wird mit spezifischen Spielanweisungen konfiguriert: .. code-block:: python INSTRUCTIONS = "This is a blindfolded watermelon-smashing game..." WELCOME = "Hello, I am Blindfolded Watermelon Smashing Game Assistant..." llm = OpenAI( api_key=OPENAI_API_KEY, model="gpt-4o", ) llm.set_max_messages(20) # Gesprächsverlauf speichern llm.set_instructions(INSTRUCTIONS) # Spielregeln festlegen llm.set_welcome(WELCOME) # Begrüßung festlegen 5. Verwaltung des Spielzustands Das Spiel speichert die Positionen des Spielers und des Ziels: .. code-block:: python # Zufällige Platzierung der Wassermelone watermelon_x, watermelon_y = random.randint(-10, 10), random.randint(-10, 10) # Spieler startet in der Mitte player_x, player_y = 0, 0 # Bewegungsschwellenwert (80 % Joystick-Auslenkung) if x_val > 80: player_x += 1 # Nach rechts bewegen elif x_val < -80: player_x -= 1 # Nach links bewegen if y_val > 80: player_y -= 1 # Nach oben bewegen (negatives Y) elif y_val < -80: player_y += 1 # Nach unten bewegen (positives Y) 6. Schlagaktion und AI-Antwort Wenn der Taster gedrückt wird, überprüft das Spiel, ob ein Treffer vorliegt oder ob eine AI-Anweisung benötigt wird: .. code-block:: python if smash_tips: smash_tips = False print("Smash!") if (player_x, player_y) == (watermelon_x, watermelon_y): print("Target hit!") tts.say("Target hit!") break # Spiel endet else: # Positionen an die AI senden input_text = f"Watermelon position: ({watermelon_x}, {watermelon_y}), Player position: ({player_x}, {player_y})" # Streaming-Antwort von der AI abrufen response = llm.prompt(input_text, stream=True) string = "" for next_word in response: if next_word: string += next_word print("AI: " + string) tts.say(string) # Anleitung aussprechen 7. Verarbeitung der Streaming-Antwort Die AI-Antwort wird Wort für Wort verarbeitet, um eine mögliche Echtzeitanzeige zu ermöglichen: .. code-block:: python response = llm.prompt(input_text, stream=True) string = "" for next_word in response: if next_word: # Zum Anzeigen während des Empfangs auskommentieren entfernen # print(next_word, end="", flush=True) string += next_word 8. Bewegungslogik mit Dead-Zone Der Joystick hat eine Dead-Zone von 80 Einheiten, um unbeabsichtigte Bewegungen zu vermeiden: .. code-block:: python # Bewegung nur, wenn der Joystick >80 % in eine Richtung gedrückt wird # Dies verhindert Drift aus der Mittelposition if x_val > 80: # Rechts elif x_val < -80: # Links if y_val > 80: # Oben elif y_val < -80: # Unten 9. Struktur der Spielschleife Die Hauptschleife des Spiels führt kontinuierlich folgende Schritte aus: 1. Joystick-Position lesen 2. Spielerkoordinaten aktualisieren, wenn der Joystick bewegt wird 3. Prüfen, ob der Schlag-Taster gedrückt wurde 4. AI-Antworten verarbeiten, falls erforderlich 5. Audiofeedback über TTS ausgeben ---------------------------------------------- **Fehlerbehebung** - Keine Reaktion vom Joystick - ADC-Verbindungen prüfen: A0 für Y-Achse, A1 für X-Achse - Stromversorgung prüfen: VCC an 3.3V, GND an Masse - ADC-Werte testen: ``print(x_axis.read())`` sollte Werte von 0–4095 anzeigen - Sicherstellen, dass der Joystick zentriert ist (sollte etwa ~2048 anzeigen) - Kein Ton bei TTS - Audioausgabe prüfen: ``sudo raspi-config`` → **System Options** → **Audio** - Lautsprecher testen: ``speaker-test -t sine -f 440`` - Sicherstellen, dass Pico2Wave installiert ist: ``pico2wave --help`` - Lautstärke prüfen: ``alsamixer`` - Audio-Setup-Skript erneut ausführen: ``sudo /opt/setup_fusion_hat_audio.sh`` - OpenAI-API-Fehler - API-Schlüssel in ``secret.py`` prüfen - Internetverbindung testen: ``ping 8.8.8.8`` - Sicherstellen, dass die Abrechnung im OpenAI-Konto aktiviert ist - Prüfen, ob das Modell „gpt-4o“ für Ihr Konto verfügbar ist - Spieler bewegt sich zu schnell/langsam - Bewegungsschwelle anpassen (derzeit 80): höher = größere Joystick-Auslenkung erforderlich - Bewegungsschritt ändern (derzeit 1): z. B. 0.5 für feinere Steuerung - Wartezeit anpassen (derzeit 0.3 s): längere Zeit = langsamere Bewegung - AI-Antworten sind zu lang - Kürzere Antworten in den ``INSTRUCTIONS`` festlegen - Beispiel hinzufügen: **„Respond in 10 words or less.“** - Begrenzung der Antwortlänge im Code implementieren ---------------------------------------------- Dieses Blindfold-Wassermelonenspiel zeigt, wie physische Steuerungen, AI-gestützte Führung und Audiofeedback kombiniert werden können, um ein spannendes sensorisches Spielerlebnis zu schaffen, das räumliches Denken und Zuhörfähigkeiten herausfordert.