request = $request; $this->placeRepository = new PlaceRepository(); } public function getNewPlace(): IContent { $mapId = (int) $this->request->query('mapId'); $session = $this->request->session(); if (!($state = $session->get('state')) || $state['mapId'] !== $mapId) { return new JsonContent(['error' => 'no_session_found']); } if (count($state['rounds']) === 0) { $placesWithoutPano = []; $place = $this->placeRepository->getRandomForMapWithValidPano($mapId, [], $placesWithoutPano); $this->addNewRoundToState($state, $place, $placesWithoutPano); $session->set('state', $state); $data = [ 'panoId' => $place->getPanoIdCached(), 'pov' => $place->getPov()->toArray() ]; } else { $rounds = count($state['rounds']); $last = $state['rounds'][$rounds - 1]; $history = []; for ($i = 0; $i < $rounds - 1; ++$i) { $round = $state['rounds'][$i]; $history[] = [ 'position' => $round['position']->toArray(), 'guessPosition' => $round['guessPosition']->toArray(), 'distance' => $round['distance'], 'score' => $round['score'] ]; } $data = [ 'history' => $history, 'panoId' => $last['panoId'], 'pov' => isset($last['pov']) ? // should be checked not to break with old sessions $last['pov']->toArray() : ['heading' => 0.0, 'pitch' => 0.0, 'zoom' => 0.0], ]; } return new JsonContent($data); } public function evaluateGuess(): IContent { $mapId = (int) $this->request->query('mapId'); $session = $this->request->session(); if (!($state = $session->get('state')) || $state['mapId'] !== $mapId) { return new JsonContent(['error' => 'no_session_found']); } $last = $state['rounds'][count($state['rounds']) - 1]; $position = $last['position']; $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $distance = $this->calculateDistance($position, $guessPosition); $score = $this->calculateScore($distance, $state['area']); $last['guessPosition'] = $guessPosition; $last['distance'] = $distance; $last['score'] = $score; $state['rounds'][count($state['rounds']) - 1] = $last; if (count($state['rounds']) < static::NUMBER_OF_ROUNDS) { $exclude = []; foreach ($state['rounds'] as $round) { $exclude = array_merge($exclude, $round['placesWithoutPano'], [$round['placeId']]); } $placesWithoutPano = []; $place = $this->placeRepository->getRandomForMapWithValidPano($mapId, $exclude, $placesWithoutPano); $this->addNewRoundToState($state, $place, $placesWithoutPano); $panoId = $place->getPanoIdCached(); $pov = $place->getPov()->toArray(); } else { $state['rounds'] = []; $panoId = null; $pov = null; } $session->set('state', $state); return new JsonContent([ 'result' => [ 'position' => $position->toArray(), 'distance' => $distance, 'score' => $score ], 'panoId' => $panoId, 'pov' => $pov ]); } private function addNewRoundToState(array &$state, Place $place, array $placesWithoutPano): void { $state['rounds'][] = [ 'placesWithoutPano' => $placesWithoutPano, 'placeId' => $place->getId(), 'position' => $place->getPosition(), 'panoId' => $place->getPanoIdCached(), 'pov' => $place->getPov() ]; } private function calculateDistance(Position $realPosition, Position $guessPosition): float { return $realPosition->calculateDistanceTo($guessPosition); } private function calculateScore(float $distance, float $area): int { $goodness = 1.0 - ($distance / (sqrt($area) * 1000)); return (int) round(pow(static::MAX_SCORE, $goodness)); } }