Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
96.43% |
54 / 56 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
| SimilarSearchLogic | |
96.23% |
51 / 53 |
|
50.00% |
2 / 4 |
10 | |
0.00% |
0 / 1 |
| __construct | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
2.00 | |||
| execute | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
| checkCandidate | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
4.00 | |||
| addCandidateToResult | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | App::uses('BoardComparator', 'Utility'); |
| 4 | require_once __DIR__ . '/BoardComparator.php'; |
| 5 | require_once __DIR__ . '/SimilarSearchResult.php'; |
| 6 | |
| 7 | class SimilarSearchLogic |
| 8 | { |
| 9 | public function __construct($setConnection) |
| 10 | { |
| 11 | $this->setConnection = $setConnection; |
| 12 | $this->result = new SimilarSearchResult(); |
| 13 | $this->sourceTsumegoID = $this->setConnection['tsumego_id']; |
| 14 | $this->sourceTsumego = ClassRegistry::init('Tsumego')->findById($this->sourceTsumegoID)['Tsumego']; |
| 15 | $sgf = ClassRegistry::init('Sgf')->find('first', ['order' => 'id DESC', 'conditions' => ['tsumego_id' => $this->sourceTsumegoID]]); |
| 16 | if (!$sgf) |
| 17 | throw new NotFoundException('SGF not found'); |
| 18 | $this->sourceBoard = SgfParser::process($sgf['Sgf']['sgf'], SgfBoard::decodePositionString($sgf['Sgf']['correct_moves'] ?? '')); |
| 19 | $this->sourceFirstMoveColor = $sgf['Sgf']['first_move_color'] ?? 'N'; |
| 20 | $this->sourceStoneCount = $this->sourceBoard->getStoneCount(); |
| 21 | $this->sourceMoveCount = substr_count($sgf['Sgf']['sgf'], ';'); |
| 22 | $set = ClassRegistry::init('Set')->findById($this->setConnection['set_id'])['Set']; |
| 23 | $this->result->title = $set['title']; |
| 24 | } |
| 25 | |
| 26 | public function execute() |
| 27 | { |
| 28 | $start = microtime(true); |
| 29 | $candidates = Util::query(" |
| 30 | SELECT |
| 31 | tsumego.id AS tsumego_id, |
| 32 | set_connection_latest.id AS set_connection_id, |
| 33 | sgf.sgf AS sgf, |
| 34 | sgf.first_move_color AS first_move_color, |
| 35 | sgf.correct_moves AS correct_moves |
| 36 | FROM tsumego |
| 37 | JOIN ( |
| 38 | SELECT |
| 39 | set_connection.tsumego_id, |
| 40 | MAX(set_connection.id) AS id |
| 41 | FROM set_connection |
| 42 | JOIN `set` |
| 43 | ON `set`.id = set_connection.set_id |
| 44 | AND `set`.public = 1 |
| 45 | GROUP BY set_connection.tsumego_id |
| 46 | ) AS set_connection_latest |
| 47 | ON set_connection_latest.tsumego_id = tsumego.id |
| 48 | LEFT JOIN sgf |
| 49 | ON sgf.tsumego_id = tsumego.id |
| 50 | AND sgf.accepted = 1 |
| 51 | AND sgf.id = ( |
| 52 | SELECT MAX(id) |
| 53 | FROM sgf |
| 54 | WHERE sgf.tsumego_id = tsumego.id |
| 55 | AND sgf.accepted = 1 |
| 56 | )"); |
| 57 | |
| 58 | foreach ($candidates as $candidate) |
| 59 | if ($candidate['tsumego_id'] != $this->sourceTsumegoID) |
| 60 | $this->checkCandidate($candidate); |
| 61 | |
| 62 | usort($this->result->items, [SimilarSearchResultItem::class, 'compare']); |
| 63 | |
| 64 | $this->result->elapsed = microtime(true) - $start; |
| 65 | } |
| 66 | |
| 67 | private function checkCandidate($candidate): void |
| 68 | { |
| 69 | $correctMoves = SgfBoard::decodePositionString($candidate['correct_moves'] ?? ''); |
| 70 | if (count($this->sourceBoard->correctMoves) != count($correctMoves)) |
| 71 | return; |
| 72 | $board = SgfParser::process($candidate['sgf'], $correctMoves); |
| 73 | $numStones = $board->getStoneCount(); |
| 74 | $stoneNumberDiff = abs($numStones - $this->sourceStoneCount); |
| 75 | if ($stoneNumberDiff > $this->maxDifference) |
| 76 | return; |
| 77 | |
| 78 | $comparisonResult = BoardComparator::compare( |
| 79 | $this->sourceBoard->stones, |
| 80 | $this->sourceFirstMoveColor, |
| 81 | $this->sourceBoard->correctMoves, |
| 82 | $board->stones, |
| 83 | $candidate['first_move_color'] ?? 'N', |
| 84 | $board->correctMoves); |
| 85 | if (!$comparisonResult) |
| 86 | return; |
| 87 | $this->addCandidateToResult($candidate, $comparisonResult); |
| 88 | } |
| 89 | |
| 90 | private function addCandidateToResult($candidate, BoardComparisonResult $comparisonResult): void |
| 91 | { |
| 92 | $setConnection = ClassRegistry::init('SetConnection')->findById($candidate['set_connection_id'])['SetConnection']; |
| 93 | // not so many should match, so I get the sql additional data manually instead in the original select, which is big |
| 94 | $set = ClassRegistry::init('Set')->findById($setConnection['set_id']); |
| 95 | |
| 96 | $item = new SimilarSearchResultItem(); |
| 97 | $item->difference = $comparisonResult->difference; |
| 98 | $item->diff = $comparisonResult->diff; |
| 99 | $item->title = $set['Set']['title']; |
| 100 | $item->moveCount = substr_count($candidate['sgf'], ';'); |
| 101 | |
| 102 | $tsumegoStatus = ClassRegistry::init('TsumegoStatus')->find('first', ['conditions' => ['user_id' => Auth::getUserID(), 'tsumego_id' => $candidate['tsumego_id']]]); |
| 103 | |
| 104 | $item->tsumegoButton = new TsumegoButton( |
| 105 | $candidate['tsumego_id'], |
| 106 | $setConnection['id'], |
| 107 | $setConnection['num'], |
| 108 | $tsumegoStatus['TsumegoStatus']['status']); |
| 109 | $this->result->items[] = $item; |
| 110 | } |
| 111 | |
| 112 | public $sourceTsumegoID; |
| 113 | public $sourceTsumego = null; |
| 114 | public $setConnection; |
| 115 | public $maxDifference = 5; |
| 116 | public $sourceBoard; |
| 117 | public $sourceFirstMoveColor; |
| 118 | public int $sourceMoveCount; |
| 119 | public $sourceStoneCount; |
| 120 | public SimilarSearchResult $result; |
| 121 | } |