Jint – JavaScripting für .NET

Viele Spiele bieten die Möglichkeit diese per Scripting zu erweitern. Dabei kann man etwa eine Map oder eine KI mit Scriptsprachen realisieren. Sehr beliebt ist hier LUA. Leider ist jedoch LUA unter den Leuten deutlich weniger bekannt und verbreitet als JavaScript, weshalb ich neulich nach einer Möglichkeit gesucht habe Scripting per JavaScript zu ermöglichen. Da ich derzeit an einem .NET Projekt arbeite, habe ich natürlich nach entsprechenden .NET Lösungen gesucht. Hierbei bin ich auf Jint – JavaScript Interpreter for .NET gestoßen. Das ganze bietet einen vollen JavaScript Interpreter, der so ziemlich die meisten JavaScript Features unterstützt (für mehr Info’s siehe Webseite). Das Coole daran ist, dass sich auch sehr einfach .NET Objekte und Funktionen in JavaScript aufrufen lassen und umgekehrt. Hier mal ein einfaches Beispiel:

string script = @"var distance = function(x1, y1, x2, y2)
{
	return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};";

double dist = (double)new JintEngine()
			.Run(script)
			.CallFunction("distance", 5, 5, 5, 10);

Würde man script jetzt aus einer Datei lesen, könnte man dem Benutzer ermöglichen eine eigene Funktion zur Berrechnung einer Distanz zu definieren. Das gute hierbei ist auch, dass man genau Kontrollieren kann auf welche Objekte aus .NET der Benutzer Zugriff hat. (JEngine.AddPermission(), JEngine.AllowClr, JEngine.DisableSecurity())
Dadurch entstehen auch keine Sicherheitsprobleme. Man kann sogar die maximale Anzahl von Rekursionen begrenzen (JEngine.SetMaxRecursions).

Ein .NET Objekt an ein Script zu übergeben geht auch sehr einfach:

public class Randomizer {
	private Random r = new Random();
	
	public int getRandomInt()
	{
		return r.Next();
	}
}

int random123 = (int)new JintEngine()
	.setParameter("Randomizer", new Randomizer())
	.Run(@"var random_number = Randomizer.getRandomInt();
	return random_number + 123;");

Das ganze ist also wirklich einfach zu verwenden und bietet sehr viele Möglichkeiten. Man könnte ein Poker-Spiel programmieren und dann KIs per JavaScript ermöglichen, oder aber zum Beispiel Quests und NPCs in Rollenspielen per JavaScript programmieren. Ein weiterer Vorteil ist, dass die JavaScripts zur Laufzeit geladen werden können, und jeder Zeit neugeladen werden können. Das heißt auch das Testen geht wesentlich einfacher! 🙂

 

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