PowerShell Game 04

Nachdem die Spielwelt für das PowerShell Game existiert, wäre es doch schön wenn sich etwas darin bewegen könnte. Üblicherweise ist das die Spielfigur. In diesem Fall ein freundlicher Smiley.

Variablen

Alle Informationen die eine Spielfigur benötigt bekommt diese nun als Attribute.

$player = New-Object PSObject
Add-Member -InputObject $player -MemberType NoteProperty -Name x -Value 1
Add-Member -InputObject $player -MemberType NoteProperty -Name y -Value 0
Add-Member -InputObject $player -MemberType NoteProperty -Name symbol -Value '☺'

Bewegung

Im GameLoop sollen die Tasten n/e/s/w (North/East/South/West) eine Bewegung der Spielfigur ermöglichen.

switch($action)
{
    'q' { $runGame = $false; }
    'e' { $player.x++ }
    'w' { $player.x-- }
    'n' { $player.y-- }
    's' { $player.y++ }
}

Neben der Bewegung ist es außerdem hilfreich wenn der Spieler das Spielfeld nicht verlassen kann.

if($player.x -lt 0) { $player.x = 0 }
if($player.x -ge $global:mapWidth ) { $player.x = $global:mapWidth -1 }
if($player.y -lt 0) { $player.y = 0 }
if($player.y -ge $global:mapHeight ) { $player.y = $global:mapHeight -1 }

Spielfeld

Beim Spielfeld wird nun vor der Ausgabe eines Feldes ermittelt ob der Spieler auf diesem Feld steht. Falls dies der Fall ist, ist das angezeigte Symbol der Smiley in der Farbe des Spielers. Der Hintergrund erhält die Farbe des ursprünglichen Feldes.

function DrawMap()
[...]
# Farbe ermitteln
    $char = $global:map[$x + $y * $global:mapWidth]
        $foregroundColor = "Black"
        $backgroundColor = "White"

# Feldspezifische Farbe und Symbol ermitteln		
    switch($char)
    {
        'E' { $foregroundColor = "Black"; $backgroundColor = "DarkRed"; $char = '^' }
        '#' { $foregroundColor = "Gray"; $backgroundColor = "DarkGray"; $char = '#' }
        'L' { $foregroundColor = "DarkGreen"; $backgroundColor = "DarkGreen"; $char = ' ' }
        'S' { $foregroundColor = "Yellow"; $backgroundColor = "Yellow"; $char = ' ' }
        'B' { $foregroundColor = "White"; $backgroundColor = "White"; $char = ' ' }
        'D' { $foregroundColor = "Black"; $backgroundColor = "Black"; $char = ' ' }
        'G' { $foregroundColor = "DarkGreen"; $backgroundColor = "Green"; $char = '*' }
        'J' { $foregroundColor = "DarkGray"; $backgroundColor = "Gray"; $char = '''' }
        'W' { $foregroundColor = "DarkBlue"; $backgroundColor = "Blue"; $char = '~' }
    }
    
# Wenn der Spieler auf dem Feld ist - Symbold und Vordergrundfarbe ersetzen		
    if($x -eq $player.x -and $y -eq $player.y)
    {
        $foregroundColor = "Blue"
        $char = $player.symbol
    }
    
# Aktuelle Symbol-/Farbkombination ausgeben
    Write-Host -NoNewline -ForegroundColor $foregroundColor -BackgroundColor $backgroundColor $char
[...]
PowerShell Spiel Zahlen raten

Ein kurzweiliges PowerShell Spiel lässt sich mit Hilfe der PowerShell in wenige Zeilen Code gießen. Der Spieler soll einen Schwierigkeitsgrad auswählen. Dieser bestimmt in welchem Bereich eine Zahl erraten werden muss. Das Programm gibt dem Spieler nach jeder Eingabe einen Hinweis ob die gewählte Zahl zu groß oder zu klein war. In jeder Runde erhöht sich die Anzahl der Versuche um eins.

Schwierigkeitsgrad wählen

Der Spieler erhält solange einen Auswahldialog bis er sich für einen der drei Stufen entschieden hat.

# Hilfsvariable um die Abfrage zu wiederholen
$isRunning = $true

# Solange ausführen bis der Spieler eine korrekte Wahl getroffen hat
while($isRunning)
{
    # Ausgabe der Wahlmöglichkeiten
    write-host "BITTE AUSWÄHLEN:"
    write-host "    l) Leichtes Spiel"
    write-host "    m) Mittleres Spiel"
    write-host "    s) Schwieriges Spiel"
    write-host "    q) Beenden"
    write-host -nonewline "> "

    # Einlesen der Eingabe
    $eingabe = read-host

    # Auswertung der Eingabe und ggf. Start des Spiels mit der höchstmöglichen Zahl
    switch($eingabe)
    {
        "l" { write-host "Leichtes Spiel gestartet"; Spiel(10) }
        "m" { write-host "Mittleres Spiel gestartet"; Spiel(100) }
        "s" { write-host "Schwieriges Spiel gestartet"; Spiel(1000) }
        "q" { write-host "Auf Wiedersehen"; $isRunning = $false }
        default { write-host "Ich verstehe dich nicht :-(" }
    }
}

Wählt der Spieler die Option l, m oder s aus, wird die Funktion Spiel aufgerufen. Die Funktion enthält den Spielablauf und muss vor der while Schleife definiert werden.

Funktion Spiel

Die Funktion Spiel erhält als Parameter die größte zu ratende Zahl. Basierend auf dem Maximum wird eine zufällige Zahl in dem Bereich eins bis Maximum ermittelt. Danach bekommt der Spieler solange Informationen über seine Eingabe bis er die korrekte Zahl erraten hat. Konnte dir korrekte Zahl ermittelt werden, gibt das Programm eine Nachricht aus.

function Spiel($max)
{
    # Hilfsvariable für die Wiederholung der Abfragen
    $raten = $true

    # Die zu ratende Zahl zufällig ermitteln
    $zahl = Get-Random -Minimum 1 -Maximum ($max+1)

    # Hilfsvariable für die Anzahl der Versuche
    $versuche = 0
    
    # Die Hauptschleife für das Raten
    while($raten)
    {
        # Jede Runde die Anzahl der Versuche erhöhen
        $versuche++

        # Auforderung für den Spieler ausgeben und Werte einlesen
        $eingabe = read-host "Bitte raten Sie die Zahl zwischen 1 und $max ($versuche)"
        
        # Die Eingabe des Benutzers in eine Zahl umwandeln, wenn möglich
        $out = 0
        if([int32]::TryParse($eingabe, [ref] $out)) {
            # Prüfen ob die Zahl zu hoch ist
            if($eingabe -gt $zahl) {
                "Ihre Eingabe war zu hoch."
            # Prüfen ob die Zahl zu niedrig ist
            } elseif($eingabe -lt $zahl) {
                "Ihre Eingabe war zu niedrig."
            # Die Zahl wurde erraten. Schleife abbrechen
            } else {
                $raten = $false
            }
        } else {
            # Umwandlung in eine Zahl nicht möglich - Fehler
            "Ungültige Eingabe: $eingabe"
        }
    }

    "Glückwunsch! Sie haben gewonnen in $versuche Versuchen!"
}
PowerShell Game 03

Unser PowerShell Game ist langweilig ohne eine Welt in der die Handlung stattfinden kann. Dieser Artikel umfasst die Definition und Darstellung einer kleinen Welt.

Map

Ein einfaches Textformat soll die Karte darstellen. Die Zeilen mit einem Fragezeichen kennzeichnen eine Konfiguration der Rest ist die Beschreibung der Welt. E steht für Erde, # für Wände, W für Wasser usw. Die tatsächliche Formatierung und Einfärbung übernimmt das Skript.

    ?SIZE 20 18
    EEE#EEEEE#GG#EEEEE#E
    ##########GG########
    #LSSSSSSSSSSSSSSSSL#
    #LSSBBBBSSSSBBBBSSL#
    #LSSBBBBSSSSBBBBSSL#
    #LSSBDBBSSSSBDBBSSL#
    #LSSSSSSSSSSSSSSSSL#
    #LSSSSSSSSSSSSSSSSL#
    #LSSSSSSSSBBBBBBSSL#
    #LSSJJJJSSBBBBBBSSL#
    #LSSLLLLSSBBBBBBSSL#
    #LSSLLLLSSBBDDBBSSL#
    #LSSSSSSSSSSSSSSSSL#
    #LSSSSSSSSJJJJJJSSL#
    #LLLWWWWSSLLLLLLSSL#
    #LLLWWWWSSLLLLLLSSL#
    #LLLWWWWSSSSSSSSSSL#
    ##LLWWWW############

Welt einlesen

Die Welt besteht aus den einzelnen Zeichen. Jedes Zeichen umfasst dabei ein Spielfeld. Für die Verwaltung werden noch weitere Variablen benötigt.

$mapRaw = "" # Kompletter Dateiinhalt
$map = "" # Spielfelder
$mapWidth = 0 # Breite
$mapHeight = 0 # Höhe

function ReadMap($mapName)
{
    # Datei lesen
    $global:mapRaw = Get-Content "maps/$mapName"
    $global:map = ""

    # Zeilenweise durch die Daten bewegen
    foreach ($line in $global:mapRaw)
    {
        # Fängt die Zeile mit einem ? an, handelt es sich um eine Konfigurationsanagbe
        if($line[0] -eq "?")
        {
            $param = $line.Substring(1)
            Write-Host "Found parameter: $param"
            $paramParts = $param.Split(" ")
            
            # Der SIZE Parameter gibt die Breite und Höhe der Karte an
            if($paramParts[0] -eq "SIZE")
            {
                $global:mapWidth = $paramParts[1]
                $global:mapHeight = $paramParts[2]
            }
        } else {
            # Alle Daten ohne Fragezeichen sind Spielfelder
            $global:map += $line
        }
    }
}

Welt zeichnen

Die Welt wird Zeichenweise ausgegeben. Jedes Zeichen der Datei kann als farbiges Feld und Zeichen ausgegeben werden.

function DrawMap()
{
    # Write-Host $global:map
    for($y = 0; $y -lt $global:mapHeight; $y++)
    {
        for($x = 0; $x -lt $global:mapWidth; $x++)
        {
            $char = $global:map[$x + $y * $global:mapWidth]
            switch($char)
            {
                'E' { Write-Host -NoNewline -ForegroundColor Black -BackgroundColor DarkRed '^' }
                '#' { Write-Host -NoNewline -ForegroundColor Gray -BackgroundColor DarkGray '#' }
                'L' { Write-Host -NoNewline -ForegroundColor DarkGreen -BackgroundColor DarkGreen ' ' }
                'S' { Write-Host -NoNewline -ForegroundColor Yellow -BackgroundColor Yellow ' ' }
                'B' { Write-Host -NoNewline -ForegroundColor White -BackgroundColor White ' ' }
                'D' { Write-Host -NoNewline -ForegroundColor Black -BackgroundColor Black ' ' }
                'G' { Write-Host -NoNewline -ForegroundColor DarkGreen -BackgroundColor Green '*' }
                'J' { Write-Host -NoNewline -ForegroundColor DarkGray -BackgroundColor Gray '''' }
                'W' { Write-Host -NoNewline -ForegroundColor DarkBlue -BackgroundColor Blue '~' }
                default { Write-Host -NoNewline -ForegroundColor Black -BackgroundColor White '$char' }
            }
        }
        Write-Host
    }
}

Game-Loop

Der Hauptteil ist um die Ausgabe der Welt zu ergänzen.

    while($runGame)
    {
        Clear-Host
    
        DrawMap
    
        [...]
    }
Anki OVERDRIVE SDK

Nachdem ich das Anki OVERDRIVE Starter Kit und die ganzen Autos und Erweiterungen ausprobiert hatte, packte mich natürlich die Neugier eines Entwicklers. Der Anki Support hat mich mit vielen Informationen versorgt. Wie ich das Ganze mit dem Raspberry Pi verbinde und was noch damit möglich ist erkläre ich in diesem Beitrag.

Die Technik von Anki OVERDRIVE

Ein YouTube Video von Anki erklärt grob „Wie funktioniert Anki OVERDRIVE?“ Weiterführende Informationen und eine tiefer-gehende Erklärung sind dort aber nicht zu entnehmen. Die folgenden Angaben habe ich im Wesentlichen durch ein paar Experimente und Dokumentationen ermitteln können. Alle Aussagen ohne Gewähr 😉

Infrarot

Werden die Streckenteile unter Infrarotlicht gehalten, lässt sich die tatsächliche Magie der Fahrzeuge entdecken. Im Infrarotlicht tauchen Linien und Barcodes auf. Diese lesen die Autos mit ihren Kameras aus (laut Video 500x/sek.). Dabei scannt die Kamera auf der Unterseite die Fahrbahn und analysiert die Codes. Hinter dem Bardcode liegen Identifikationen und Informationen darüber, wie die Autos sich verhalten sollen im Bezug auf die Geschwindigkeit.

Kommunikation

Die Kommunikation mit den Autos funktioniert mittels Bluetooth. Die Anki Macher haben dazu ein SDK erstellt. Die Anki OVERDRIVE App spricht mit anderen Teilnehmern über die W-LAN Verbindung des Smartphones. Die tatsächliche Intelligenz des Spiel ist vollständig in der App abgebildet. Das SDK erlaubt rudimentäre Befehle wie das Wechseln der Spur und Drehungen. Alle Waffen und Upgrades existieren nur virtuell in der Applikation.

SDK und Raspberry Pi

Auf GitHub stellt Anki das anki/drive-sdk Repository bereit. Der dort enthaltende Code lässt sich auf einem Raspberry Pi kompilieren. Eine kurze Anleitung ist vorhanden. Mit Hilfe der Testprogramme konnte ich die Fahrzeuge scannen. Die hinterlegten Fahrzeuge sind aus dem alten Anki Set, die neuen OVERDRIVE IDs sind noch nicht codiert.

$ sudo ~/anki/drive-sdk/build/dist/bin/hciconfig hci0 up
$ sudo ~/anki/drive-sdk/build/dist/bin/vehicle-scan
LE Scan ...
C1:4A:82:93:CE:38 Drive [v266e] (Unknown cb02)
E5:A2:3D:0C:DB:82 Drive [v266e] (Unknown d8e0)

Der Erste Code ist von „Skull“, der zweite Eintrag ist bei mir „Ground Shock“. Eine Verbindung ist leider noch nicht möglich (dies liegt aber vermutlich an meiner Bluetooth Konfigurationen).

$ sudo ~/anki/drive-sdk/build/dist/bin/vehicle-tool --adapter=hci0 --device=E5:A2:3D:0C:DB:82
[E5:A2:3D:0C:DB:82][LE]> connect
Attempting to connect to E5:A2:3D:0C:DB:82
Error: connect: Connection refused (111)

Für die Analyse habe ich mir BlueZ selber gebaut.

sudo apt-get --purge remove bluez
sudo apt-get update
sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev
sudo wget https://www.kernel.org/pub/linux/bluetooth/bluez-5.37.tar.xz
sudo tar xvf bluez-5.37.tar
cd bluez-5.37
sudo ./configure --disable-systemd
sudo make
sudo make install
sudo cp attrib/gatttool /usr/local/bin/
sudo reboot
$ sudo /etc/init.d/bluetooth stop
$ sudo ./bluez-5.37/tools/hciconfig hci0 up
$ sudo ./bluez-5.37/tools/hciconfig hci0

$ sudo ~/bluez-5.37/tools/hcitool lescan
E5:A2:3D:0C:DB:82 (unknown)
E5:A2:3D:0C:DB:82 n&    Drive
$ sudo ~/bluez-5.37/tools/hcitool  lecc E5:A2:3D:0C:DB:82
Could not create connection: Input/output error
$ sudo ~/bluez-5.37/attrib/gatttool -i hci0 -b C1:4A:82:93:CE:38 -t random -I
[C1:4A:82:93:CE:38][LE]> connect
Attempting to connect to C1:4A:82:93:CE:38
Error: connect: Connection refused (111)

MORE: COMING SOON! In diesem Artikel 🙂

Update: Artikel aus dem Netz zu der Technologie hinter Anki. Leider gibt es die Original Blogeinträge nicht mehr.

Patente für Anki, Inc. (Skizzen)

TNW News: Anki Drive reveals ‘secret’ about the track technology which guides its AI-controlled cars

VB: Anki Overdrive will overhaul the robot battle-car racing game with customizable tracks

TEST: ToDo: Neubau mit

http://stackoverflow.com/questions/32947807/cannot-connect-to-ble-device-on-raspberry-pi
I had to disable the plugin pnat in /etc/bluetooth/main.conf -> DisablePugins=pnat. I read that it's unstable, but I don't know much about this plugin.
Anki OVERDRIVE Strecken

Anki OVERDRIVE ist wohl DAS Weihnachtsgeschenk von 2015. Der Trend ist natürlich auch an mir nicht vorbei gegangen. In dieser kleinen Reihe schreibe ich über die ersten Experiment mit dem Anki OVERDRIVE Starter Kit und später der Entwickler SDK.

Die folgenden Strecken entstammen dem Starter Kit und einigen Erweiterungen. Nach diversen Kaufrunden sind auch alle Autos mittlerweile in meinem Besitz.

Kleiner Doppelkreis
Kleiner Doppelkreis
Doppelkreis Kreuzung
Doppelkreis Kreuzung
Beule
Beule
Zwei Ebenen
Zwei Ebenen
Große Kreise
Große Kreise