summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/lib/OsikaEvaluator.php504
-rw-r--r--bin/lib/OsikaParser.php226
-rwxr-xr-xbin/osika199
3 files changed, 464 insertions, 465 deletions
diff --git a/bin/lib/OsikaEvaluator.php b/bin/lib/OsikaEvaluator.php
index c20816f..474cffe 100644
--- a/bin/lib/OsikaEvaluator.php
+++ b/bin/lib/OsikaEvaluator.php
@@ -4,286 +4,286 @@ require_once('OsikaParser.php');
class OsikaEvaluator {
- private $_hand;
+ private $_hand;
- public function __construct($hand = NULL) {
- if ($hand) {
- $this->setHand($hand);
- }
- }
-
- public function setHand($hand) {
- $parser = new OsikaParser($hand);
- $this->_hand = $parser->parse();
- }
-
- private static $_honorCounts = array();
- private function _countHonors($suit, $major = FALSE) {
- if (!isset(self::$_honorCounts[$suit])) {
- self::$_honorCounts[$suit] = array(NULL, NULL);
- }
- if (self::$_honorCounts[$suit][$major] === NULL) {
- $dummy = array();
- self::$_honorCounts[$suit][$major] = preg_match_all($major ? '/a|k|q/' : '/a|k|q|j/', $suit, $dummy);
- }
- return self::$_honorCounts[$suit][$major];
- }
+ public function __construct($hand = NULL) {
+ if ($hand) {
+ $this->setHand($hand);
+ }
+ }
- private static $_cardHonorTricks = array(
- 'a' => 1.125,
- 'k' => 0.8125,
- 'q' => 0.4375,
- 'j' => 0.125
- );
- private function _honorTricks($suit) {
- // only 3 highest cards in the suit holding count towards honor tricks
- $suit = substr($suit, 0, 3);
+ public function setHand($hand) {
+ $parser = new OsikaParser($hand);
+ $this->_hand = $parser->parse();
+ }
- $cards = self::$_cardHonorTricks;
- // only count Jack if it's the highest card in suit
- if (strpos($suit, 'j') !== 0) {
- $cards = array_slice($cards, 0, 3);
- }
+ private static $_honorCounts = array();
+ private function _countHonors($suit, $major = FALSE) {
+ if (!isset(self::$_honorCounts[$suit])) {
+ self::$_honorCounts[$suit] = array(NULL, NULL);
+ }
+ if (self::$_honorCounts[$suit][$major] === NULL) {
+ $dummy = array();
+ self::$_honorCounts[$suit][$major] = preg_match_all($major ? '/a|k|q/' : '/a|k|q|j/', $suit, $dummy);
+ }
+ return self::$_honorCounts[$suit][$major];
+ }
- $ret = 0;
- foreach ($cards as $card => $value) {
- $ret += substr_count($suit, $card) * $value;
- }
- return $ret;
- }
+ private static $_cardHonorTricks = array(
+ 'a' => 1.125,
+ 'k' => 0.8125,
+ 'q' => 0.4375,
+ 'j' => 0.125
+ );
+ private function _honorTricks($suit) {
+ // only 3 highest cards in the suit holding count towards honor tricks
+ $suit = substr($suit, 0, 3);
- private function _honorTrickCorrections($suit) {
- // only 3 highest cards in the suit holding count towards honor tricks
- $suit = substr($suit, 0, 3);
- $count = $this->_countHonors($suit);
- // HH = 1/4 trick; HHH = 1/2 trick
- return ($count < 2) ? 0 : $count * 0.25;
- }
+ $cards = self::$_cardHonorTricks;
+ // only count Jack if it's the highest card in suit
+ if (strpos($suit, 'j') !== 0) {
+ $cards = array_slice($cards, 0, 3);
+ }
- private function _honorTrickSupportCorrections($suit) {
- $ret = 0;
- // every 10 within 4+ suit with any of the AKQ = 1/8 of a trick
- if (strlen($suit) > 3 && strpos($suit, 't') !== FALSE && $this->_countHonors($suit, TRUE)) {
- $ret += 0.125;
- }
- // every 109 = 1/16 of a trick
- if (strpos($suit, 't9') !== FALSE) {
- $ret += 0.0625;
- }
- return $ret;
- }
+ $ret = 0;
+ foreach ($cards as $card => $value) {
+ $ret += substr_count($suit, $card) * $value;
+ }
+ return $ret;
+ }
- private function _honorTrickShortCorrections($suit) {
- $length = strlen($suit);
- // either nothing to subtract from or no need to
- if (!$length || $length > 2) {
- return 0;
- }
- $count = $this->_countHonors($suit);
- if (!$count) {
- return 0;
- }
- // H sec = -1/8 of a trick; Hx = -1/16 of a trick; HH sec = -1/8 of a trick
- return $count * (($length === 1) ? -0.125 : -0.0625);
- }
+ private function _honorTrickCorrections($suit) {
+ // only 3 highest cards in the suit holding count towards honor tricks
+ $suit = substr($suit, 0, 3);
+ $count = $this->_countHonors($suit);
+ // HH = 1/4 trick; HHH = 1/2 trick
+ return ($count < 2) ? 0 : $count * 0.25;
+ }
- private static $_lengthDistributionTricks = array(
- 4 => 0.4375,
- 5 => 1.5,
- 6 => 2.75,
- 7 => 3.9375
- );
- private function _distributionTricks($suit) {
- $length = strlen($suit);
- if ($length < 4) {
- return 0;
- }
- if ($length >= 8) {
- return $length - 3;
- }
- return self::$_lengthDistributionTricks[$length];
- }
+ private function _honorTrickSupportCorrections($suit) {
+ $ret = 0;
+ // every 10 within 4+ suit with any of the AKQ = 1/8 of a trick
+ if (strlen($suit) > 3 && strpos($suit, 't') !== FALSE && $this->_countHonors($suit, TRUE)) {
+ $ret += 0.125;
+ }
+ // every 109 = 1/16 of a trick
+ if (strpos($suit, 't9') !== FALSE) {
+ $ret += 0.0625;
+ }
+ return $ret;
+ }
- private function _quickTricks($hand) {
- $hand = '|'.implode('|', $hand);
- // aces and kings contribute towards quick tricks
- $highCards = substr_count($hand, 'a') + substr_count($hand, 'k');
- // queens and jacks contribute against, but we don't count unsupported jacks, because of how they count towards honor tricks in the first place
- $lowCards = substr_count($hand, 'q') + substr_count($hand, 'j') - substr_count($hand, '|j');
- $difference = $highCards - $lowCards;
- // difference of:
- // +3 or more = 1/8
- // +2 = 1/16
- // +1 to -1 = 0
- // -2 = -1/16
- // -3 or less = -1/8
- if (abs($difference) <= 1) {
- return 0;
- }
- if ($difference > 2) {
- return 0.125;
- }
- if ($difference > 1) {
- return 0.0625;
- }
- if ($difference < -2) {
- return -0.125;
- }
- if ($difference < -1) {
- return -0.0625;
- }
- }
+ private function _honorTrickShortCorrections($suit) {
+ $length = strlen($suit);
+ // either nothing to subtract from or no need to
+ if (!$length || $length > 2) {
+ return 0;
+ }
+ $count = $this->_countHonors($suit);
+ if (!$count) {
+ return 0;
+ }
+ // H sec = -1/8 of a trick; Hx = -1/16 of a trick; HH sec = -1/8 of a trick
+ return $count * (($length === 1) ? -0.125 : -0.0625);
+ }
- private function _middleCardCorrections($hand) {
- // count only 10's and 9's in 3+ card suits
- $nonshort = '';
- foreach ($hand as $suit) {
- if (strlen($suit) >= 3) {
- $nonshort .= $suit;
+ private static $_lengthDistributionTricks = array(
+ 4 => 0.4375,
+ 5 => 1.5,
+ 6 => 2.75,
+ 7 => 3.9375
+ );
+ private function _distributionTricks($suit) {
+ $length = strlen($suit);
+ if ($length < 4) {
+ return 0;
+ }
+ if ($length >= 8) {
+ return $length - 3;
}
- }
- // statistically, we should have 2 of those
- // but we're not counting short suits and some long suit 10 configurations
- // so the par for the course is 1 middle card
- $dummy = array(); // this is humiliating
- $count = preg_match_all('/t|9/', $nonshort, $dummy) - 1;
- // if we're better than that single middle card -> 1/16 of a trick
- // if we're worse -> -1/16
- return max(-0.0625, min(0.0625, $count * 0.0625));
- }
+ return self::$_lengthDistributionTricks[$length];
+ }
- private function _shortSuitCorrections($distribution) {
- $shortSuits = array();
- foreach ($distribution as $suit) {
- // short suit is a 3- card suit here
- if ($suit <= 3) {
- $shortSuits[] = $suit;
+ private function _quickTricks($hand) {
+ $hand = '|'.implode('|', $hand);
+ // aces and kings contribute towards quick tricks
+ $highCards = substr_count($hand, 'a') + substr_count($hand, 'k');
+ // queens and jacks contribute against, but we don't count unsupported jacks, because of how they count towards honor tricks in the first place
+ $lowCards = substr_count($hand, 'q') + substr_count($hand, 'j') - substr_count($hand, '|j');
+ $difference = $highCards - $lowCards;
+ // difference of:
+ // +3 or more = 1/8
+ // +2 = 1/16
+ // +1 to -1 = 0
+ // -2 = -1/16
+ // -3 or less = -1/8
+ if (abs($difference) <= 1) {
+ return 0;
}
- }
- // the correction only applies if we're having 2 or more short suits
- if (count($shortSuits) < 2) {
- return 0;
- }
- sort($shortSuits);
- // if the shortest short suits are 3-0, 3-1 or 2-0, we add 1/16 of a trick
- $diff = $shortSuits[1] - $shortSuits[0];
- return ($diff > 1) ? 0.0625 : 0;
- }
+ if ($difference > 2) {
+ return 0.125;
+ }
+ if ($difference > 1) {
+ return 0.0625;
+ }
+ if ($difference < -2) {
+ return -0.125;
+ }
+ if ($difference < -1) {
+ return -0.0625;
+ }
+ }
- private function _majorSuitCorrections($distribution) {
- // at least 8 cards in majors...
- if ($distribution['s'] + $distribution['h'] >= 8) {
- // ...and at least 3 cards in each major...
- if ($distribution['h'] > 2 && $distribution['h'] > 2) {
- // ...constitute a 1/16 of a trick correction
- return 0.0625;
+ private function _middleCardCorrections($hand) {
+ // count only 10's and 9's in 3+ card suits
+ $nonshort = '';
+ foreach ($hand as $suit) {
+ if (strlen($suit) >= 3) {
+ $nonshort .= $suit;
+ }
}
- }
- return 0;
- }
+ // statistically, we should have 2 of those
+ // but we're not counting short suits and some long suit 10 configurations
+ // so the par for the course is 1 middle card
+ $dummy = array(); // this is humiliating
+ $count = preg_match_all('/t|9/', $nonshort, $dummy) - 1;
+ // if we're better than that single middle card -> 1/16 of a trick
+ // if we're worse -> -1/16
+ return max(-0.0625, min(0.0625, $count * 0.0625));
+ }
- // I honestly have no idea what the hell's going on below.
- private function _localizationCorrections($result, $distribution) {
- $strength = array();
- $length = array();
- foreach ($result['lh'] as $index => $value) {
- if (strlen($index) === 1) {
- if ($distribution[$index] >= 3) {
- if (isset($strength[$distribution[$index]])) {
- $strength[$distribution[$index]] += ($result['lh'][$index]+$result['lh_plus'][$index]+$result['lh_pod'][$index]+$result['lh_short'][$index]);
- }
- else {
- $strength[$distribution[$index]] = ($result['lh'][$index]+$result['lh_plus'][$index]+$result['lh_pod'][$index]+$result['lh_short'][$index]);
- }
- if (isset($length[$distribution[$index]])) {
- $length[$distribution[$index]] += $distribution[$index];
- }
- else {
- $length[$distribution[$index]] = $distribution[$index];
- }
- }
+ private function _shortSuitCorrections($distribution) {
+ $shortSuits = array();
+ foreach ($distribution as $suit) {
+ // short suit is a 3- card suit here
+ if ($suit <= 3) {
+ $shortSuits[] = $suit;
+ }
}
- }
- ksort($strength);
- ksort($length);
- $sumLength = array_sum($length);
- $sumStrength = array_sum($strength);
- $longestDiff = end($strength)-$sumStrength*end($length)/$sumLength;
- $shortestDiff = reset($strength)-$sumStrength*reset($length)/$sumLength;
- if (abs($longestDiff) > 0.5) {
- if (abs($longestDiff) > 1) {
- return 0.25*(abs($longestDiff)/$longestDiff);
+ // the correction only applies if we're having 2 or more short suits
+ if (count($shortSuits) < 2) {
+ return 0;
}
- else {
- return 0.125*(abs($longestDiff)/$longestDiff);
+ sort($shortSuits);
+ // if the shortest short suits are 3-0, 3-1 or 2-0, we add 1/16 of a trick
+ $diff = $shortSuits[1] - $shortSuits[0];
+ return ($diff > 1) ? 0.0625 : 0;
+ }
+
+ private function _majorSuitCorrections($distribution) {
+ // at least 8 cards in majors...
+ if ($distribution['s'] + $distribution['h'] >= 8) {
+ // ...and at least 3 cards in each major...
+ if ($distribution['h'] > 2 && $distribution['h'] > 2) {
+ // ...constitute a 1/16 of a trick correction
+ return 0.0625;
+ }
+ }
+ return 0;
+ }
+
+ // I honestly have no idea what the hell's going on below.
+ private function _localizationCorrections($result, $distribution) {
+ $strength = array();
+ $length = array();
+ foreach ($result['lh'] as $index => $value) {
+ if (strlen($index) === 1) {
+ if ($distribution[$index] >= 3) {
+ if (isset($strength[$distribution[$index]])) {
+ $strength[$distribution[$index]] += ($result['lh'][$index]+$result['lh_plus'][$index]+$result['lh_pod'][$index]+$result['lh_short'][$index]);
+ }
+ else {
+ $strength[$distribution[$index]] = ($result['lh'][$index]+$result['lh_plus'][$index]+$result['lh_pod'][$index]+$result['lh_short'][$index]);
+ }
+ if (isset($length[$distribution[$index]])) {
+ $length[$distribution[$index]] += $distribution[$index];
+ }
+ else {
+ $length[$distribution[$index]] = $distribution[$index];
+ }
+ }
+ }
}
- }
- if (abs($shortestDiff) > 0.5) {
- if (abs($shortestDiff) > 1) {
- return -0.125*(abs($shortestDiff)/$shortestDiff);
+ ksort($strength);
+ ksort($length);
+ $sumLength = array_sum($length);
+ $sumStrength = array_sum($strength);
+ $longestDiff = end($strength)-$sumStrength*end($length)/$sumLength;
+ $shortestDiff = reset($strength)-$sumStrength*reset($length)/$sumLength;
+ if (abs($longestDiff) > 0.5) {
+ if (abs($longestDiff) > 1) {
+ return 0.25*(abs($longestDiff)/$longestDiff);
+ }
+ else {
+ return 0.125*(abs($longestDiff)/$longestDiff);
+ }
}
- else {
- return -0.0625*(abs($shortestDiff)/$shortestDiff);
+ if (abs($shortestDiff) > 0.5) {
+ if (abs($shortestDiff) > 1) {
+ return -0.125*(abs($shortestDiff)/$shortestDiff);
+ }
+ else {
+ return -0.0625*(abs($shortestDiff)/$shortestDiff);
+ }
}
- }
- return 0;
- }
+ return 0;
+ }
- private static $_suits = array('s','h','d','c');
- public function evaluate() {
- $result = array();
- $result['lh'] = array();
- $result['lh_plus'] = array();
- $result['lh_pod'] = array();
- $result['lh_short'] = array();
- $result['lu'] = array();
- foreach ($this->_hand as $ind => $suit) {
- $suitChar = self::$_suits[$ind];
- $result['lh'][$suitChar] = $this->_honorTricks($suit);
- $result['lh_plus'][$suitChar] = $this->_honorTrickCorrections($suit);
- $result['lh_pod'][$suitChar] = $this->_honorTrickSupportCorrections($suit);
- $result['lh_short'][$suitChar] = $this->_honorTrickShortCorrections($suit);
- $result['lu'][$suitChar] = $this->_distributionTricks($suit);
- }
- $result['lh']['total'] = array_sum($result['lh']);
- $result['lh_plus']['total'] = array_sum($result['lh_plus']);
- $result['lh_pod']['total'] = array_sum($result['lh_pod']);
- $result['lh_short']['total'] = array_sum($result['lh_short']);
- $result['lu']['total'] = array_sum($result['lu']);
+ private static $_suits = array('s','h','d','c');
+ public function evaluate() {
+ $result = array();
+ $result['lh'] = array();
+ $result['lh_plus'] = array();
+ $result['lh_pod'] = array();
+ $result['lh_short'] = array();
+ $result['lu'] = array();
+ foreach ($this->_hand as $ind => $suit) {
+ $suitChar = self::$_suits[$ind];
+ $result['lh'][$suitChar] = $this->_honorTricks($suit);
+ $result['lh_plus'][$suitChar] = $this->_honorTrickCorrections($suit);
+ $result['lh_pod'][$suitChar] = $this->_honorTrickSupportCorrections($suit);
+ $result['lh_short'][$suitChar] = $this->_honorTrickShortCorrections($suit);
+ $result['lu'][$suitChar] = $this->_distributionTricks($suit);
+ }
+ $result['lh']['total'] = array_sum($result['lh']);
+ $result['lh_plus']['total'] = array_sum($result['lh_plus']);
+ $result['lh_pod']['total'] = array_sum($result['lh_pod']);
+ $result['lh_short']['total'] = array_sum($result['lh_short']);
+ $result['lu']['total'] = array_sum($result['lu']);
- $result['lsz'] = array('total' => $this->_quickTricks($this->_hand));
- $result['lu_plus'] = array('total' => $this->_middleCardCorrections($this->_hand));
+ $result['lsz'] = array('total' => $this->_quickTricks($this->_hand));
+ $result['lu_plus'] = array('total' => $this->_middleCardCorrections($this->_hand));
- $distribution = array_combine(self::$_suits, array_map('strlen', $this->_hand));
- $result['short_suit'] = array('total' => $this->_shortSuitCorrections($distribution));
- $result['major_suit'] = array('total' => $this->_majorSuitCorrections($distribution));
+ $distribution = array_combine(self::$_suits, array_map('strlen', $this->_hand));
+ $result['short_suit'] = array('total' => $this->_shortSuitCorrections($distribution));
+ $result['major_suit'] = array('total' => $this->_majorSuitCorrections($distribution));
- $result['l10n'] = array('total' => $this->_localizationCorrections($result, $distribution));
+ $result['l10n'] = array('total' => $this->_localizationCorrections($result, $distribution));
- $subtotal = array();
- $lhSubtotal = array();
- $total = 0;
- foreach ($result as $category => $factor) {
- if (count($factor) === 5) {
- foreach ($factor as $index => $subt) {
- if (!isset($subtotal[$index])) {
- $subtotal[$index] = 0;
- $lhSubtotal[$index] = 0;
- }
- $subtotal[$index] += $subt;
- if ($category !== 'lu') {
- $lhSubtotal[$index] += $subt;
- }
- }
+ $subtotal = array();
+ $lhSubtotal = array();
+ $total = 0;
+ foreach ($result as $category => $factor) {
+ if (count($factor) === 5) {
+ foreach ($factor as $index => $subt) {
+ if (!isset($subtotal[$index])) {
+ $subtotal[$index] = 0;
+ $lhSubtotal[$index] = 0;
+ }
+ $subtotal[$index] += $subt;
+ if ($category !== 'lu') {
+ $lhSubtotal[$index] += $subt;
+ }
+ }
+ }
+ $total += $factor['total'];
}
- $total += $factor['total'];
- }
- $result['lh_subtotal'] = $lhSubtotal;
- $result['subtotal'] = $subtotal;
- $result['total'] = array('total' => $total);
+ $result['lh_subtotal'] = $lhSubtotal;
+ $result['subtotal'] = $subtotal;
+ $result['total'] = array('total' => $total);
- return $result;
- }
+ return $result;
+ }
}
diff --git a/bin/lib/OsikaParser.php b/bin/lib/OsikaParser.php
index 75fc302..d2046f4 100644
--- a/bin/lib/OsikaParser.php
+++ b/bin/lib/OsikaParser.php
@@ -5,111 +5,111 @@
**/
class OsikaParser {
- private $_hand;
+ private $_hand;
- /**
- * Constructor for the parser class
- * @param $hand (optional) hand to parse
- **/
- public function __construct($hand = NULL) {
- $this->setHand($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;
- }
+ /**
+ * Sets the hand string to parse
+ * @param $hand string of xxxx|xxx|xxx|xxx format
+ **/
+ public function setHand($hand) {
+ $this->_hand = $hand;
+ }
- /**
- * 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);
- }
+ /**
+ * 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);
+ /**
+ * 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);
}
- // 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);
- }
+ unset($suit);
+ // check for wrong card count
+ if ($cardCount !== 13) {
+ throw new OsikaParserException('Ręka nie zawiera 13 kart', OsikaParserException::INVALID_CARD_COUNT);
}
- $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;
- }
+ return $suits;
+ }
}
@@ -118,21 +118,21 @@ class OsikaParser {
**/
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
+ 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?
- */
+ /*
+ 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
+?>
diff --git a/bin/osika b/bin/osika
index 91acbc3..2262d17 100755
--- a/bin/osika
+++ b/bin/osika
@@ -1,40 +1,39 @@
#!/usr/bin/env php
<?php
-class Osika
-{
-
- private static $suitHeaders = array(
- 'c' => '♣',
- 'd' => '♦',
- 'h' => '♥',
- 's' => '♠',
- 'total' => 'Σ'
- );
- private static $catHeaders = array(
- 'lh' => "Honory\t",
- 'lh_plus' => 'Zgrupowania',
- 'lh_10' => 'Podwiązania',
- 'lh_short' => 'Krótkie honory',
- 'lh_subtotal' => 'Lewy honorowe',
- 'lu' => 'Lewy układowe',
- 'subtotal' => "Razem\t",
- 'lsz' => 'Lewy szybkie',
- 'lu_plus' => 'Wysokie blotki',
- 'short_suit' => 'Kolory krótkie',
- 'major_suit' => 'Kolory starsze',
- 'l10n' => 'Lokalizacja',
- 'total' => "Łącznie\t"
- );
-
-
- public static function printHelp() {
- print "OSiKa, v1.0.0, autor: M. Klichowicz (mkl)
+class Osika {
+
+ private static $suitHeaders = array(
+ 'c' => '♣',
+ 'd' => '♦',
+ 'h' => '♥',
+ 's' => '♠',
+ 'total' => 'Σ'
+ );
+ private static $catHeaders = array(
+ 'lh' => "Honory\t",
+ 'lh_plus' => 'Zgrupowania',
+ 'lh_10' => 'Podwiązania',
+ 'lh_short' => 'Krótkie honory',
+ 'lh_subtotal' => 'Lewy honorowe',
+ 'lu' => 'Lewy układowe',
+ 'subtotal' => "Razem\t",
+ 'lsz' => 'Lewy szybkie',
+ 'lu_plus' => 'Wysokie blotki',
+ 'short_suit' => 'Kolory krótkie',
+ 'major_suit' => 'Kolory starsze',
+ 'l10n' => 'Lokalizacja',
+ 'total' => "Łącznie\t"
+ );
+
+
+ public static function printHelp() {
+ print "OSiKa, v1.0.0, autor: M. Klichowicz (mkl)
Program do analizy siły ręki brydżowej metodami algorytmów licytacji naturalnej wg Łukasza Sławińskiego.
Sposób użycia:
-
+
php osika [OPCJE] KARTY
KARTY
@@ -53,48 +52,48 @@ OPCJE
Lista (rozdzielonych przecinkami) składowych łącznej siły ręki,
które program ma wyświetlić.
Domyślna wartość: all. Dostępne wartości:";
- foreach (self::$catHeaders as $cat => $header) {
- print "
+ foreach (self::$catHeaders as $cat => $header) {
+ print "
$cat: $header";
- }
- print "
+ }
+ print "
-s KOLORY
Lista (rozdzielonych przecinkami) kolorów,
dla których składowe program ma wyświetlić.
Możliwe wartości: s, h, d, c, total, all. Domyślna wartość: all
";
- }
-
- public static function printTable($output, $categories, $suits, $raw = FALSE) {
- if (!$raw) {
- print "\t\t";
- foreach ($suits as $suit) {
- if (isset(self::$suitHeaders[$suit])) {
- print self::$suitHeaders[$suit];
- }
- print "\t";
- }
- print "\n";
- }
- foreach ($categories as $cat) {
+ }
+
+ public static function printTable($output, $categories, $suits, $raw = FALSE) {
if (!$raw) {
- if (isset(self::$catHeaders[$cat])) {
- print self::$catHeaders[$cat];
- }
- print "\t";
+ print "\t\t";
+ foreach ($suits as $suit) {
+ if (isset(self::$suitHeaders[$suit])) {
+ print self::$suitHeaders[$suit];
+ }
+ print "\t";
+ }
+ print "\n";
}
- foreach ($suits as $suit) {
- if (isset($output[$cat][$suit])) {
- print $output[$cat][$suit];
- }
- print "\t";
+ foreach ($categories as $cat) {
+ if (!$raw) {
+ if (isset(self::$catHeaders[$cat])) {
+ print self::$catHeaders[$cat];
+ }
+ print "\t";
+ }
+ foreach ($suits as $suit) {
+ if (isset($output[$cat][$suit])) {
+ print $output[$cat][$suit];
+ }
+ print "\t";
+ }
+ print "\n";
}
- print "\n";
- }
- }
+ }
-}
+ }
require_once('lib/OsikaEvaluator.php');
@@ -102,81 +101,81 @@ $hand = str_replace(',', '|', array_pop($argv));
$options = getopt('hf:c:s:', array('help', 'format:', 'categories:', 'suits:'));
if (isset($options['h']) || isset($options['help']) || $argc < 2) {
- Osika::printHelp();
- exit;
+ Osika::printHelp();
+ exit;
}
$format = isset($options['f']) ? $options['f'] : (isset($options['format']) ? $options['format'] : 'table');
if (is_array($format)) {
- $format = array_pop($format);
+ $format = array_pop($format);
}
$catOptions = array();
if (isset($options['c'])) {
- $catOptions = array_merge($catOptions, (array)$options['c']);
+ $catOptions = array_merge($catOptions, (array)$options['c']);
}
if (isset($options['categories'])) {
- $catOptions = array_merge($catOptions, (array)$options['categories']);
+ $catOptions = array_merge($catOptions, (array)$options['categories']);
}
$categories = array();
foreach ($catOptions as $cat) {
- $categories = array_merge($categories, explode(',', $cat));
+ $categories = array_merge($categories, explode(',', $cat));
}
if (empty($categories)) {
- $categories = array('all');
+ $categories = array('all');
}
if (in_array('all', $categories)) {
- $categories = array('lh','lh_plus','lh_10','lh_short','lh_subtotal','lu','subtotal','lsz','lu_plus','short_suit','major_suit','l10n','total');
+ $categories = array('lh','lh_plus','lh_10','lh_short','lh_subtotal','lu','subtotal','lsz','lu_plus','short_suit','major_suit','l10n','total');
}
$suitOptions = array();
if (isset($options['s'])) {
- $suitOptions = array_merge($suitOptions, (array)$options['s']);
+ $suitOptions = array_merge($suitOptions, (array)$options['s']);
}
if (isset($options['suits'])) {
- $suitOptions = array_merge($suitOptions, (array)$options['suits']);
+ $suitOptions = array_merge($suitOptions, (array)$options['suits']);
}
$suits = array();
foreach ($suitOptions as $suit) {
- $suits = array_merge($suits, explode(',', $suit));
+ $suits = array_merge($suits, explode(',', $suit));
}
if (empty($suits)) {
- $suits = array('all');
+ $suits = array('all');
}
if (in_array('all', $suits)) {
- $suits = array('s','h','d','c','total');
+ $suits = array('s','h','d','c','total');
}
try {
- $eval = new OsikaEvaluator($hand);
- $result = $eval->evaluate();
- $output = array();
- foreach ($result as $categoryName => $category) {
- if (in_array($categoryName, $categories)) {
- $outCat = array();
- foreach ($category as $suitName => $suit) {
- if (in_array($suitName, $suits)) {
- $outCat[$suitName] = $suit;
- }
+ $eval = new OsikaEvaluator($hand);
+ $result = $eval->evaluate();
+ $output = array();
+ foreach ($result as $categoryName => $category) {
+ if (in_array($categoryName, $categories)) {
+ $outCat = array();
+ foreach ($category as $suitName => $suit) {
+ if (in_array($suitName, $suits)) {
+ $outCat[$suitName] = $suit;
+ }
+ }
+ $output[$categoryName] = $outCat;
}
- $output[$categoryName] = $outCat;
- }
- }
- switch ($format) {
- case 'json':
- print json_encode($output)."\n";
- exit;
- case 'raw':
- Osika::printTable($output, $categories, $suits, TRUE);
- exit;
- case 'table':
- default:
- Osika::printTable($output, $categories, $suits);
- exit;
- }
+ }
+ switch ($format) {
+ case 'json':
+ print json_encode($output)."\n";
+ exit;
+ case 'raw':
+ Osika::printTable($output, $categories, $suits, TRUE);
+ exit;
+ case 'table':
+ default:
+ Osika::printTable($output, $categories, $suits);
+ exit;
+ }
}
catch (Exception $e) {
- print 'ERROR: '.$e->getMessage()."\n";
+ print 'ERROR: '.$e->getMessage()."\n";
}
?>