Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.43% covered (success)
96.43%
54 / 56
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SimilarSearchLogic
96.23% covered (success)
96.23%
51 / 53
50.00% covered (danger)
50.00%
2 / 4
10
0.00% covered (danger)
0.00%
0 / 1
 __construct
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
2.00
 execute
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 checkCandidate
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
4.00
 addCandidateToResult
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3App::uses('BoardComparator', 'Utility');
4require_once __DIR__ . '/BoardComparator.php';
5require_once __DIR__ . '/SimilarSearchResult.php';
6
7class 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("
30SELECT
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
36FROM tsumego
37JOIN (
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
48LEFT 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}