prefix = $prefix; $this->round = $round; $this->board = $board; if (file_exists('translations.json')) { static::$translations = json_decode(file_get_contents('translations.json'), TRUE); } $this->deals_by_tables = array(); if (!file_exists($this->get_filename())) { throw new Exception('file not found: ' . $this->get_filename()); } } static function __($string) { if (isset(static::$translations[$string])) { return static::$translations[$string]; } return $string; } function get_filename() { return '..' . DIRECTORY_SEPARATOR . $this->prefix . $this->round . 'b-' . $this->board . '.html'; } function set_deal($table, $deal) { $this->deals_by_tables[$table] = $deal; } function findByID($id) { foreach ($this->deals_by_tables as $deal) { if ($deal->id === $id) { return $deal; } } return NULL; } function getTablesByID($id) { $tables = array(); foreach ($this->deals_by_tables as $table => $deal) { if ($deal->id === $id) { $tables[] = $table; } } return $tables; } function areBoardsPlayed($boards) { foreach ($boards as $board) { $dom = str_get_html($board); $isFirstRow = count($dom->find('td/a')); $contract = trim(str_replace(' ', '', $dom->find('td[class="bdc"]', 0)->innertext)); $score = trim(str_replace(' ', '', $dom->find('td', 5 + $isFirstRow)->innertext)); // note that the contract field for arbitral scores starts with 'A' (e.g. 'ARB' or 'AAA') if ($score == '' && $contract[0] != 'A') { return FALSE; } } return TRUE; } function output() { $content = file_get_contents($this->get_filename()); $dom = str_get_html($content); $header_td1 = $dom->find('/html/body/table/tr/td[class="bdcc12"]', 0); $header_tr = $header_td1->parent; $tr = @$header_tr->next_sibling(); $columnCount = 0; $groupedBoards = array('default' => array()); while ($tr) { $td = $tr->find('td/a', 0); if ($td) { $columnCount = max($columnCount, count($tr->find('td'))); $table = trim($td->innertext); $table = str_replace(' ', '', $table); $table = (int)$table; if ($table) { if (array_key_exists($table, $this->deals_by_tables)) { // add table rows to specific board record if (!isset($groupedBoards[$this->deals_by_tables[$table]->id])) { $groupedBoards[$this->deals_by_tables[$table]->id] = array(); } $groupedBoards[$this->deals_by_tables[$table]->id][] = $tr->outertext; $groupedBoards[$this->deals_by_tables[$table]->id][] = $tr->next_sibling()->outertext; $tr->outertext = ''; $tr->next_sibling()->outertext = ''; } else { // add table rows to default board record $groupedBoards['default'][] = $tr->outertext; $groupedBoards['default'][] = $tr->next_sibling()->outertext; } } } $tr = @$tr->next_sibling(); } $table = $dom->find('/html/body/table', 0); $table->find('tr', 0)->class = 'tdd-header'; foreach ($groupedBoards as $boardId => $groupedBoard) { if ($boardId === 'default') { $innerTable = $table->find('td/table', 0); $rows = $innerTable->find('tr'); $firstRow = array_shift($rows); $dealNumber = array(); if (preg_match('/#(\d+)/', $firstRow->find('h4', 0)->innertext, $dealNumber)) { $firstRow->innertext = '

' . static::__("Rozdanie") . ' ' . $dealNumber[1] . '

'; } if (!$this->areBoardsPlayed($groupedBoard)) { foreach ($rows as $row) { $row->outertext = ''; } $innerTable->innertext = trim($innerTable->innertext) . '

...

'; } } else { $deal = $this->findByID($boardId); if ($deal) { $tables = $this->getTablesByID($boardId); sort($tables); $insert = '

'; $insert .= static::__("Stół") . ' ' . implode(', ', $tables) . ' – '; $insert .= static::__("Rozdanie") . ' ' . $deal->deal_num . '

'; // if the board has been played on all tables if ($this->areBoardsPlayed($groupedBoard)) { $insert .= $deal->html(); } else { $insert .= '

...

'; } $table->innertext .= '' . $insert . ''; $table->innertext .= implode('', $groupedBoard); } } } $head = $dom->find('/html/head', 0); $head->innertext .= '' . '' . ''; // replacing meta http-equiv refresh with a javascript refresh to preserve hash in the result page $meta = $head->find('meta'); foreach ($meta as $metaTag) { if ($metaTag->hasAttribute('http-equiv') && strtolower($metaTag->getAttribute('http-equiv')) == 'refresh') { $head->innertext = str_replace($metaTag->outertext, '', $head->innertext) . ''; break; } } print $dom->outertext; } } class NoSuchDealNumber extends Exception { } class Deal { function __construct($pbnfile, $num_in_pbn) { $this->id = md5($pbnfile); $this->deal_num = $num_in_pbn; $this->_parse($pbnfile, $num_in_pbn); } function _parse($pbn, $num_in_pbn) { $start = strpos($pbn, '[Board "' . $num_in_pbn . '"]'); if($start === false) { throw new NoSuchDealNumber($num_in_pbn); } $pbn = substr($pbn, $start + 5); $stop = strpos($pbn,'[Board "'); if($stop != false) { $pbn = substr($pbn, 0, $stop); } preg_match('|Dealer "([NESW])"|', $pbn, $m); $this->dealer = $m[1]; preg_match('|Vulnerable "([^"]+)"|', $pbn, $m); $this->vuln = $m[1]; if($this->vuln == 'None') { $this->vuln = '-'; } else if($this->vuln == 'All') { $this->vuln = 'Obie'; } preg_match('|Ability "([^"]+)"|', $pbn, $m); if($m[1]) { $this->ability = explode(' ',$m[1]); } preg_match('|Minimax "([^"]+)"|', $pbn, $m); $this->minimax = $m[1]; preg_match('|Deal "(N:)?([^"]+)"|', $pbn, $m); $this->hands = explode(' ',$m[2]); } function html() { ob_start(); include('tdd-handrecord-tpl.php'); return ob_get_clean(); } function format_hand($hand_num) { $hand = $this->hands[$hand_num]; $hand = str_replace('T','10',$hand); $suits = explode('.',$hand); $str = 'S '.$suits[0].'
'; $str .= 'H '.$suits[1].'
'; $str .= 'D '.$suits[2].'
'; $str .= 'C '.$suits[3]; return $str; } function format_ability($ability_num) { $ability = $this->ability[$ability_num]; $ab = array($ability[0], $ability[2], $ability[3], $ability[4], $ability[5], $ability[6]); foreach($ab as $k=>$v) { switch($v) { case 'A': $ab[$k] = '10'; break; case 'B': $ab[$k] = '11'; break; case 'C': $ab[$k] = '12'; break; case 'D': $ab[$k] = '13'; break; } } return "{$ab[0]} {$ab[1]} {$ab[2]} {$ab[3]} {$ab[4]} {$ab[5]}"; } function format_minimax() { $minimax = $this->minimax; $minimax = preg_replace('|^(..)D(.+)|','$1x$2', $minimax); $minimax = preg_replace('|^(..)R(.+)|','$1xx$2', $minimax); $minimax = preg_replace('|^(.)N(.+)|','$1NT$2', $minimax); $minimax = preg_replace('/(\d)([SHDCN])(T?)(x*)([NESW])(.*)/','$1 $2$3 $4 $5, $6', $minimax); return $minimax; } } class BoardDB { private $__timestampFile = '.tdd-timestamps.cache'; private $__dbFile = '.tdd-records.cache'; private $__database = array(); public function __construct($timestampFile = '.tdd-timestamps.cache', $dbFile = '.tdd-records.cache') { $this->__timestampFile = $timestampFile; $this->__dbFile = $dbFile; $this->__database = unserialize(file_get_contents($this->__dbFile)); $this->refreshBoardDatabase(); } public function getDB() { return $this->__database; } private function __getRecordFiles($directory = '.') { return glob($directory . DIRECTORY_SEPARATOR . '*.pbn'); } private function __getFilesTimestamps($files = array()) { return array_combine( $files, array_map('filemtime', $files) ); } private function __compileRecordDatabase($files, $dbFile) { $this->__database = array(); foreach ($files as $filename) { $filename = basename($filename); $fileParts = array(); if (preg_match('/^(.*)-r(\d+)-t([0-9,-]+)-b(\d+)\.pbn$/', $filename, $fileParts)) { $prefix = $fileParts[1]; if (!isset($this->__database[$prefix])) { $this->__database[$prefix] = array(); } $round = (int)($fileParts[2]); if (!isset($this->__database[$prefix][$round])) { $this->__database[$prefix][$round] = array(); } $tableString = $fileParts[3]; $tables = array(); foreach (explode(',', $tableString) as $tableSets) { $tableDelimiters = array_filter(explode('-', $tableSets)); if (count($tableDelimiters) > 1) { for ($table = (int)($tableDelimiters[0]); $table <= (int)($tableDelimiters[1]); $table++) { $tables[] = $table; } } else { $tables[] = (int)($tableDelimiters[0]); } } $firstBoard = (int)($fileParts[4]); $chunks = preg_split('/(\[Board "(\d+)"\])/', file_get_contents($filename), -1, PREG_SPLIT_DELIM_CAPTURE); $boardHeader = ''; $boardNumber = 1; $firstBoardNumber = -1; foreach ($chunks as $chunk) { $chunk = trim($chunk); if (strpos($chunk, '% PBN') > -1) { continue; } if (strpos($chunk, '[Board ') === 0) { $boardHeader = $chunk; continue; } if (strpos($chunk, '[') === 0) { try { $deal = new Deal($boardHeader . $chunk, $boardNumber); $boardNumberJFR = $boardNumber + $firstBoard - $firstBoardNumber; if (!isset($this->__database[$prefix][$round][$boardNumberJFR])) { $this->__database[$prefix][$round][$boardNumberJFR] = array(); } foreach ($tables as $table) { $this->__database[$prefix][$round][$boardNumberJFR][$table] = $deal; } } catch (NoSuchDealNumber $e) { // ignore if the deal does not exist in the file } } else { $boardNumber = (int)($chunk); if ($firstBoardNumber < 0) { $firstBoardNumber = $boardNumber; } } } } } file_put_contents($this->__dbFile, serialize($this->__database)); } public function refreshBoardDatabase() { $recordFiles = $this->__getRecordFiles(); $savedTimestamps = file_exists($this->__timestampFile) ? json_decode(file_get_contents($this->__timestampFile), TRUE) : array(); $timestamps = $this->__getFilesTimestamps($recordFiles); if (array_diff_assoc($savedTimestamps, $timestamps) || array_diff_assoc($timestamps, $savedTimestamps)) { $this->__compileRecordDatabase($recordFiles, $this->__dbFile); file_put_contents($this->__timestampFile, json_encode($timestamps)); } } }