106 lines
3.8 KiB
PHP
106 lines
3.8 KiB
PHP
<?php
|
|
namespace EmotionHero\Models;
|
|
|
|
use Doctrine\ORM\EntityRepository;
|
|
use EmotionHero\Tools\Position;
|
|
|
|
class HitRepository extends EntityRepository
|
|
{
|
|
/**
|
|
*
|
|
* @param \EmotionHero\Models\Hit $hit
|
|
* @return Hit The hit that is considered better than the given one (used for hints)
|
|
*/
|
|
public function getBetterHit(Hit $hit) {
|
|
$targets = $this->getTargetSetForTarget($hit->getTarget());
|
|
if(count($targets) > 1) {
|
|
// we want only the slightly better hit...
|
|
$query = $this->_em->createQuery(
|
|
"SELECT h, SUM(h.score) as HIDDEN totalScore FROM ".Hit::class." h WHERE h.target IN (:targets) GROUP BY h.game HAVING totalScore > :score ORDER BY totalScore DESC"
|
|
)
|
|
->setMaxResults(1)
|
|
->setParameters([
|
|
'score' => $this->getTotalScoreForHit($hit),
|
|
'targets' => $targets,
|
|
]);
|
|
$betterHit = $query->getOneOrNullResult();
|
|
} else { // run a faster query if there's just on target (no grouping needed)
|
|
// we want only the slightly better hit...
|
|
$query = $this->_em->createQuery(
|
|
"SELECT h FROM ".Hit::class." h WHERE h.target = :target AND h.score > :score ORDER BY h.score DESC"
|
|
)
|
|
->setMaxResults(1)
|
|
->setParameters([
|
|
'score' => $this->getTotalScoreForHit($hit),
|
|
'target' => array_shift($targets),
|
|
]);
|
|
$betterHit = $query->getOneOrNullResult();
|
|
}
|
|
|
|
return $betterHit;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param string $emotionField
|
|
* @param float $score
|
|
* @return Hit
|
|
* @throws \Exception
|
|
*/
|
|
public function getClosestHitWithImage(string $emotionField, float $score) {
|
|
if (!in_array($emotionField, Emotions::$EMOTIONS)) {
|
|
throw new \Exception("Invalid emotion!");
|
|
}
|
|
|
|
$scoreMin = $score < 0.1 ? 0 : $score - 0.3;
|
|
$scoreMax = $score < 0.1 ? 0 : $score + 0.3;
|
|
$query = $this->_em->createQuery(
|
|
// add BETWEEN so we can use _some_ keying to improve performance.
|
|
"SELECT h, ABS(:score - h.emotions.$emotionField) as HIDDEN distance, RAND() AS HIDDEN lala FROM ".Hit::class." h WHERE h.hasImage = :has AND h.emotions.$emotionField BETWEEN :score_min AND :score_max ORDER BY distance ASC, lala"
|
|
)
|
|
->setParameters([
|
|
'score' => $score,
|
|
'score_min' => $scoreMin,
|
|
'score_max' => $scoreMax,
|
|
'has' => true,
|
|
])
|
|
->setMaxResults(1);
|
|
return $query->getSingleResult();
|
|
}
|
|
|
|
/**
|
|
* Get targets of the same level at the same time (targetset)
|
|
* TODO: move to TargetRepository
|
|
* @param Target $target [description]
|
|
* @return ArrayCollection|null The result or nul if no results
|
|
*/
|
|
public function getTargetSetForTarget(Target $target) {
|
|
return $this->_em->createQuery(
|
|
"SELECT t FROM ".Target::class." t WHERE t.level = :level AND t.time = :time"
|
|
)
|
|
->setParameters([
|
|
'level' => $target->getLevel(),
|
|
'time' => $target->getTime()
|
|
])
|
|
->getResult();
|
|
|
|
}
|
|
|
|
/**
|
|
* Get the total score of the TargetSet to which this hit belongs
|
|
* @param \EmotionHero\Models\Hit $hit
|
|
* @return string (return float as string, so it can be fed back to mysql without floating point issues)
|
|
*/
|
|
public function getTotalScoreForHit(Hit $hit) {
|
|
$targets = $this->getTargetSetForTarget($hit->getTarget());
|
|
return $this->_em->createQuery(
|
|
"SELECT SUM(h.score) FROM ".Hit::class." h WHERE h.game = :game AND h.target IN (:targets)"
|
|
)
|
|
->setParameters([
|
|
'game' => $hit->getGame(),
|
|
'targets' => $targets
|
|
])
|
|
->getSingleScalarResult();
|
|
}
|
|
}
|