summaryrefslogtreecommitdiff
path: root/bin/lib/OsikaParser.php
diff options
context:
space:
mode:
authoremkael <emkael@tlen.pl>2013-09-14 18:59:45 +0200
committeremkael <emkael@tlen.pl>2013-09-14 18:59:45 +0200
commit5cd13672421f067034cffb77b96e10c91b6f4161 (patch)
tree3d7b276dc6c913ed1379d8c09960802ad785c91e /bin/lib/OsikaParser.php
parent62e228801c814dc9bc73ff8e11fbe2a400198e83 (diff)
* parser ręki
Diffstat (limited to 'bin/lib/OsikaParser.php')
-rw-r--r--bin/lib/OsikaParser.php138
1 files changed, 138 insertions, 0 deletions
diff --git a/bin/lib/OsikaParser.php b/bin/lib/OsikaParser.php
new file mode 100644
index 0000000..75fc302
--- /dev/null
+++ b/bin/lib/OsikaParser.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * Parser class. Converts hand input string to array of sorted and normalized suits.
+ **/
+class OsikaParser {
+
+ private $_hand;
+
+ /**
+ * Constructor for the parser class
+ * @param $hand (optional) hand to parse
+ **/
+ public function __construct($hand = NULL) {
+ $this->setHand($hand);
+ }
+
+ /**
+ * Sets the hand string to parse
+ * @param $hand string of xxxx|xxx|xxx|xxx format
+ **/
+ public function setHand($hand) {
+ $this->_hand = $hand;
+ }
+
+ /**
+ * Card comparison function
+ * @param $cardA, $cardB - characters denoting cards
+ * @return 1 or -1, as usort() expects
+ **/
+ private function _sort($cardA, $cardB) {
+ // aces first
+ if ($cardA == 'a') {
+ return -1;
+ }
+ // ...than kings...
+ if ($cardA == 'k') {
+ return ($cardB == 'a') ? 1 : -1;
+ }
+ // ...queens...
+ if ($cardA == 'q') {
+ return (in_array($cardB, array('a', 'k'))) ? 1 : -1;
+ }
+ // ...jacks...
+ if ($cardA == 'j') {
+ return (in_array($cardB, array('a', 'k', 'q'))) ? 1 : -1;
+ }
+ // ...tens...
+ if ($cardA == 't') {
+ return (in_array($cardB, array('a', 'k', 'q', 'j'))) ? 1 : -1;
+ }
+ // ... and nines
+ if ($cardA == '9') {
+ return (in_array($cardB, array('a', 'k', 'q', 'j', 't'))) ? 1 : -1;
+ }
+ // anything else goes last, as it was
+ return 1;
+ }
+
+ /**
+ * Suit sorting function. Uses OsikaParser::_sort as user-defined sort function for the exploded string.
+ **/
+ private function _sortSuit(&$suit) {
+ $temp = str_split($suit);
+ usort($temp, array($this, '_sort'));
+ $suit = implode('', $temp);
+ }
+
+ /**
+ * Where the magic happens.
+ **/
+ public function parse() {
+ if (!$this->_hand) {
+ throw new OsikaParserException('Brak podanej ręki', OsikaParserException::NO_HAND);
+ }
+ // input is case-insensitive
+ $this->_hand = strtolower($this->_hand);
+ // allow (and interpret) Polish figures abbrevs. and "10" as Ten
+ $this->_hand = strtr($this->_hand,
+ array(
+ '10' => 't',
+ 'w' => 'j',
+ 'd' => 'q'));
+ // strip whitespace
+ $this->_hand = preg_replace('/\s/', '', $this->_hand);
+ $suits = explode('|', $this->_hand);
+ // check for invalid number of suits suits in the hand
+ if (count($suits) !== 4) {
+ throw new OsikaParserException('Ręka nie zawiera 4 kolorów', OsikaParserException::INVALID_SUIT_COUNT);
+ }
+ $cardCount = 0;
+ foreach ($suits as &$suit) {
+ // check for invalid characters
+ if (preg_match('/[^akqjtx2-9]/', $suit)) {
+ throw new OsikaParserException('Kolor '.$suit.' zawiera nieprawidłowe znaki', OsikaParserException::INVALID_CHARS);
+ }
+ // check for duplicate cards
+ foreach (array('a', 'k', 'q', 'j', 't', '9') as $honor) {
+ if (substr_count($suit, $honor) > 1) {
+ throw new OsikaParserException('Kolor '.$suit.' zawiera zduplikowany honor (lub 9)', OsikaParserException::DUPLICATE_CHARS);
+ }
+ }
+ $this->_sortSuit($suit);
+ $cardCount += strlen($suit);
+ }
+ unset($suit);
+ // check for wrong card count
+ if ($cardCount !== 13) {
+ throw new OsikaParserException('Ręka nie zawiera 13 kart', OsikaParserException::INVALID_CARD_COUNT);
+ }
+ return $suits;
+ }
+
+}
+
+/**
+ * Exception class for parser errors
+ **/
+class OsikaParserException extends Exception {
+
+ const NO_HAND = 1; // empty (or equivalent) string provided
+ const INVALID_SUIT_COUNT = 2; // the hand does not contain 4 suit (i.e. 3 "|" chars)
+ const INVALID_CHARS = 3; // the hand contains characters that make no sense
+ const DUPLICATE_CHARS = 4; // the hand contains duplicate honors (or 9)
+ const INVALID_CARD_COUNT = 5; // the hand does not contain 13 cards
+
+ /*
+ Should we check if the hand contains exactly 13 cards?
+ I don't think the algorithm technically relies on the hand being complete.
+ But does the evaluation stand for incomplete hands?
+ E.g. if we played the first 7 tricks and are left with AK AK AK ==,
+ do all the quick tricks, short honor and grouped honor evaluations compute correctly?
+ Or even make sense?
+ */
+
+};
+
+?> \ No newline at end of file