Manager’s Life Update

Gerade habe ich ein neues Manager’s Life Update eingespielt. Es enthält das versprochene Casino mit dem Poker Multiplayer!

Das Poker-Spiel ist komplett in HTML5+Canvas, JavaScript und PHP realisiert und sollte auf jedem HTML5-Endgerät funktionieren.

Außerdem habe ich noch viele kleinere Bugs behoben und ein paar Änderungen an der Sprite-Engine vorgenommen.

Viel Spaß beim Pokern! 😉

 

Poker Abstraktion

Wie bereits angekündigt wird die nächste Version von Manager’s Life ein Casino in die Spielwelt integrieren. Dabei darf natürlich kein Multiplayer-Poker fehlen!

Poker mit PHP umzusetzen ist eigentlich eine relativ einfache Sache – der schwierigere Teil ist das erkennen der Poker-Hände beim Showdown (Flush, Straight, Pair, …). Natürlich kann man einfach alle möglichen Hände hardcoden und dann einfach eine nach der anderen Abarbeiten (siehe zB phppokerengine), das ist aber nicht besonderst elegant, performant oder erweiterbar. Außerdem ist es sehr unübersichtlich und fehleranfällig. Deshalb habe ich mir gedacht, man könnte alle möglichen Poker-Hände mit einer Abstraktions-Sprache definieren und diese dann mit entsprechendem Parser zu behandeln.

Zuerst zu meiner Sprache, die ich wie folgt definiert habe:

  • Gleiche Zahlen stellen gleiche Karten-Werte dar
  • Gleiche Buchstaben stellen gleiche Farben dar
  • Ein Fragezeichen steht für eine beliebige Karte
  • Ein > sagt, dass die nächste Karte direkt auf die vorherige folgen muss.
  • Ein [ sagt, dass das nächste Zeichen kein Symbol, sondern kein expliziter Kartenwert ist
  • {x} nach einem Symbol gibt an, wie oft es wiederholt wird

Um das ganze zu konkretisieren habe ich nun alle Poker-Hände in meine Sprache übersetzt:

// pair (zB ♥A - ♦A)
1{2}

// two pair (zB ♣A - ♦A - ♠3 - ♣3)
1{2}2{2}

// three of a kind (zB ♣A - ♦A - ♠A)
1{3}

// straight (zB ♣K - ♦Q - ♠J - ♣10 - ♣9)
?>?>?>?>?

// flush (zB ♣2 - ♣Q - ♣9 - ♣10 - ♣9)
a{5}

// full house (zB ♣K - ♦K - ♠Q - ♣Q - ♣Q)
1{3}2{2}

// four of a kind (zB ♣K - ♦K - ♠K - ♣K)
1{4}

// straight flush (zB ♣K - ♣Q - ♣J - ♣10 - ♣9)
a>a>a>a>a

// royal flush (zB ♣A - ♣K - ♣Q - ♣J - ♣10)
[a>a>a>a>a

Das Parsen und Verarbeiten ist zwar etwas trickreicher, aber machbar. Ich habe einen Quick-n-Dirty PHP-Prototypen gebaut – er funktioniert, ist allerdings nicht besonderst sauber geschrieben. Im Vergleich zu der Methode „alle-Möglichkeiten-hardcoren“ entsteht übrigens noch ein weiterer Vorteil: Meine Klasse ist 7kB groß, die oben verlinkte 0,5 MB…

Die Verwendung der Klasse erfolgt so:

a>a>a>a');

$hand = array(
  array('color' => 'spades', 'card' => 'k'),
  array('color' => 'spades', 'card' => 'q'),
  array('color' => 'spades', 'card' => 'j'),
  array('color' => 'spades', 'card' => '10'),
  array('color' => 'spades', 'card' => '9')
);

if ($parser->check($hand)) {
   echo "Deine Hand enthält ein Straight Flush!
n"; } else { echo "Kein Straight Flush vorhanden.
n"; } ?>

Die Ausgabe sollte „Deine Hand enthält ein Straight Flush!“ sein.

Die Klasse findet sich dort: PokerParser.class.php

 

RedBean PHP – Top oder Flop?

Eine Datenbank-Abstraktion mit ORM kann sehr praktisch sein und viel Arbeit sparen, vorallem wenn sie so funktioniert wie RedBean-PHP.
Nun, wie funktioniert RedBean-PHP? Eigentlich relativ simpel. Anderst als bei sehr bekannten PHP-ORM-Libraries wie Doctrine oder Propel muss sich der Programmierer theoretisch gar nicht um das Layout der Datenbank kümmern. Er muss also keine Felder oder Relationen vor dem eigentlichen Programmieren definieren, sondern kann sie einfach verwenden, und RedBean-PHP kümmert sich um den Rest. Klingt ziemlich cool, oder? Hier mal ein Beispiel:

// RedBean-PHP besteht aus einer einzigen Datei
require "rb.php";

// Zur Datenbank verbinden:
// derzeit wird SQLite, MySQL und PostgreSQL unterstützt
R::setup('mysql:host=localhost;dbname=redbeantest','root','');

// ein haus erstellen
$house = R::dispense('house');
$house->color = 'blue';
$house->size = 34;
R::store($house); // in der db speichern

// einen besitzer erstellen
$owner = R::dispense('owner');
$owner->name = 'Alex';
R::store($owner); // in der db speichern

// das Haus gehört dem Besitzer
// Many-to-Many Relation, da jeder Besitzer 
// viele Häuser haben kann und ein Haus 
// mehrere Besitzer
R::associate($owner, $house);

// eine stadt erstellen
$city = R::dispense('city');
$city->name = 'Zauberstadt';

// eine One-to-Many Relation, da 
// jedes Haus nur zu einer Stadt 
// gehört
$city->ownHouse[] = $house; 

R::store($city); // speichern

// jetzt laden wir mal ein paar sachen aus der DB
// einen besitzer mit dem name Alex finden
$person = R::findOne('owner', ' name = ?', array('Alex'));

// seine häuser finden
$houses = R::related($person, 'house');
var_dump($houses); // häuser dumpen

// die häuser der stadt ausgeben
foreach ($city->ownHouse as $key => $house) {
   echo "Haus Nummer $key ist ".$house->color."n";
}

Das ganze funktioniert, sobald es eine Datenbank „redbeantest“ gibt. Es müssen keine Tabellen in der Datenbank erstellt werden, noch Forgein-Keys angelegt werden. (Weitere Beispiele und die Dokumentation sind auf der Webseite.)

Alles wunderbar, allerdings kommt hier schon ein erstes Problem auf! RedBean PHP stellt nämlich alle Forgein-Keys und dessen Relations auf ON UPDATE SET NULL, ON DELETE SET NULL. Dieses Verhalten ist aber nicht immer gewünscht, das heißt hier muss man aufjedenfall noch nach RedBean-PHP aufräumen bevor die Software in Produktion gehen kann. Ein weiteres Problem ist, dass RedBean-PHP den Typ eines Datenbankfelds nur dann optimal wählt, wenn man es mit genügend Test-Daten füttert. Ansonsten ist auch hier ein nachträgliches Aufräumen nötig. Hier kann man sich also die Frage stellen – möchte ich lieber später meine Datenbank aufräumen müssen, oder sie direkt von Anfang an korrekt designen?

Ein zweites Problem von RedBean-PHP ist Performance. Das Framework macht, vorallem bei Relations, einfach viel zu viele Datenbank-Abfragen, die bei vielen Zugriffen zu hoher Datenbanklast führen können (Abhilfe schafft hier ein Caching System, allerdings ist das nur hilfreich, wenn keine aktuellen Daten benötigt werden). Hier endet man dann also damit einige R::find und R::related durch eigene Queries zu ersetzen. (was übrigens auch prima funktioniert, aber dann nicht mehr im Sinne von ORM ist…)

Abgesehen davon ist es aber eine super Sache, man kommt aufjedenfall schnell zu Ergebnissen. Ich würde es für alle kleineren PHP-Projekte empfehlen! Für größere Projekte würde ich davon abraten, da man einfach zu schnell den Überblick verliert und man mit dem „Aufräumen“ schnell mal etwas übersieht was dann zu kritischen Fehlern führen kann.