request = $request; $this->placeRepository = new PlaceRepository(); } public function getInitialData(): 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) { $place = $this->selectNewPlace($state, $mapId); $session->set('state', $state); $data = [ 'place' => [ '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, 'place' => [ '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; $response = [ 'result' => [ 'position' => $position->toArray(), 'distance' => $distance, 'score' => $score ] ]; if (count($state['rounds']) < static::NUMBER_OF_ROUNDS) { $place = $this->selectNewPlace($state, $mapId); $response['newPlace'] = [ 'panoId' => $place->getPanoIdCached(), 'pov' => $place->getPov()->toArray() ]; } else { $state['rounds'] = []; } $session->set('state', $state); return new JsonContent($response); } private function selectNewPlace(array &$state, int $mapId) { $exclude = array_column($state['rounds'], 'placeId'); $place = $this->placeRepository->getRandomForMapWithValidPano($mapId, $exclude); $state['rounds'][] = [ 'placeId' => $place->getId(), 'position' => $place->getPosition(), 'panoId' => $place->getPanoIdCached(), 'pov' => $place->getPov() ]; return $place; } 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)); } }