mapguesser/src/Controller/GameFlowController.php

473 lines
17 KiB
PHP
Raw Normal View History

<?php namespace MapGuesser\Controller;
use DateTime;
use MapGuesser\Interfaces\Request\IRequest;
use MapGuesser\Util\Geo\Position;
use MapGuesser\Response\JsonContent;
use MapGuesser\Interfaces\Response\IContent;
use MapGuesser\Multi\MultiConnector;
use MapGuesser\PersistentData\Model\UserInChallenge;
use MapGuesser\PersistentData\PersistentDataManager;
2021-05-12 21:01:26 +02:00
use MapGuesser\PersistentData\Model\Guess;
use MapGuesser\PersistentData\Model\UserPlayedPlace;
use MapGuesser\Repository\ChallengeRepository;
2021-05-12 21:01:26 +02:00
use MapGuesser\Repository\GuessRepository;
use MapGuesser\Repository\MapRepository;
use MapGuesser\Repository\MultiRoomRepository;
2021-05-12 21:01:26 +02:00
use MapGuesser\Repository\PlaceInChallengeRepository;
use MapGuesser\Repository\PlaceRepository;
use MapGuesser\Repository\UserInChallengeRepository;
use MapGuesser\Repository\UserPlayedPlaceRepository;
use MapGuesser\Repository\UserRepository;
class GameFlowController
{
const NUMBER_OF_ROUNDS = 5;
const MAX_SCORE = 1000;
private IRequest $request;
private PersistentDataManager $pdm;
private MultiConnector $multiConnector;
private MultiRoomRepository $multiRoomRepository;
private PlaceRepository $placeRepository;
2021-05-12 21:01:26 +02:00
private MapRepository $mapRepository;
private UserRepository $userRepository;
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
private ChallengeRepository $challengeRepository;
private UserInChallengeRepository $userInChallengeRepository;
2021-05-12 21:01:26 +02:00
private PlaceInChallengeRepository $placeInChallengeRepository;
private GuessRepository $guessRepository;
public function __construct(IRequest $request)
{
$this->request = $request;
$this->pdm = new PersistentDataManager();
$this->multiConnector = new MultiConnector();
$this->multiRoomRepository = new MultiRoomRepository();
$this->placeRepository = new PlaceRepository();
2021-05-12 21:01:26 +02:00
$this->mapRepository = new MapRepository();
$this->userRepository = new UserRepository();
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
$this->challengeRepository = new ChallengeRepository();
$this->userInChallengeRepository = new UserInChallengeRepository();
2021-05-12 21:01:26 +02:00
$this->placeInChallengeRepository = new PlaceInChallengeRepository();
$this->guessRepository = new GuessRepository();
}
public function initialData(): 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 (!isset($state['currentRound']) || $state['currentRound'] == -1 || $state['currentRound'] >= static::NUMBER_OF_ROUNDS) {
$this->startNewGame($state, $mapId);
$session->set('state', $state);
}
$response = [];
$last = $state['rounds'][$state['currentRound']];
$response['place'] = [
'panoId' => $last['panoId'],
'pov' => $last['pov']->toArray()
];
$response['history'] = [];
for ($i = 0; $i < $state['currentRound']; ++$i) {
$round = $state['rounds'][$i];
$response['history'][] = [
'position' => $round['position']->toArray(),
2021-04-03 13:38:16 +02:00
'result' => [
'guessPosition' => $round['guessPosition']->toArray(),
'distance' => $round['distance'],
'score' => $round['score']
]
];
}
return new JsonContent($response);
}
public function multiInitialData(): IContent
{
$roomId = $this->request->query('roomId');
$session = $this->request->session();
if (!($multiState = $session->get('multiState')) || $multiState['roomId'] !== $roomId) {
return new JsonContent(['error' => 'no_session_found']);
}
$room = $this->multiRoomRepository->getByRoomId($roomId);
$state = $room->getStateArray();
$members = $room->getMembersArray();
if ($members['owner'] !== $multiState['token']) {
return new JsonContent(['error' => 'not_owner_of_room']);
}
if ($state['currentRound'] == -1 || $state['currentRound'] >= static::NUMBER_OF_ROUNDS - 1) {
$this->startNewGame($state, $state['mapId']);
$room->setStateArray($state);
$room->setUpdatedDate(new DateTime());
$this->pdm->saveToDb($room);
}
$places = [];
foreach ($state['rounds'] as $round) {
$places[] = [
'position' => $round['position']->toArray(),
'panoId' => $round['panoId'],
'pov' => $round['pov']->toArray()
];
}
$this->multiConnector->sendMessage('start_game', ['roomId' => $roomId, 'places' => $places]);
return new JsonContent(['ok' => true]);
}
public function challengeInitialData(): IContent
{
$session = $this->request->session();
$userId = $session->get('userId');
$challengeToken_str = $this->request->query('challengeToken');
$challenge = $this->challengeRepository->getByTokenStr($challengeToken_str);
if (!isset($challenge)) {
return new JsonContent(['error' => 'game_not_found']);
}
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge);
$currentRound = $userInChallenge->getRound();
2021-05-12 21:01:26 +02:00
$currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
$response = [];
2021-05-12 21:01:26 +02:00
$response['history'] = [];
$guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge));
2021-05-12 21:01:26 +02:00
$places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge));
for($i = 0; $i < $currentRound; ++$i) {
2021-05-12 21:01:26 +02:00
$response['history'][] = [
'position' => $places[$i]->getPosition()->toArray(),
'result' => [
'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(),
'distance' => $guessesByUser[$i]->getDistance(),
'score' => $guessesByUser[$i]->getScore()
],
'allResults' => []
2021-05-12 21:01:26 +02:00
];
foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) {
if($guess->getUserId() != $userId) {
$user = $this->userRepository->getByGuess($guess);
$response['history'][$i]['allResults'][] = [
'userName' => $user->getDisplayName(),
'guessPosition' => $guess->getPosition()->toArray(),
'distance' => $guess->getDistance(),
'score' => $guess->getScore()
];
}
}
2021-05-12 21:01:26 +02:00
}
if(!isset($currentPlace)) { // game finished
$response['finished'] = true;
} else { // continue game
$response['place'] = [
'panoId' => $currentPlace->getPanoIdCached(),
'pov' => [$currentPlace->getPov()->toArray()]
];
}
return new JsonContent($response);
}
public function guess(): 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'][$state['currentRound']];
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
2021-05-12 21:01:26 +02:00
$result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
$last['guessPosition'] = $guessPosition;
$last['distance'] = $result['distance'];
$last['score'] = $result['score'];
2021-03-15 12:28:06 +01:00
$response = [
2021-04-03 13:38:16 +02:00
'position' => $last['position']->toArray(),
'result' => $result
2021-03-15 12:28:06 +01:00
];
$state['rounds'][$state['currentRound']] = $last;
$state['currentRound'] += 1;
if ($state['currentRound'] < static::NUMBER_OF_ROUNDS) {
$next = $state['rounds'][$state['currentRound']];
$response['place'] = [
'panoId' => $next['panoId'],
'pov' => $next['pov']->toArray()
2021-03-15 12:28:06 +01:00
];
}
$session->set('state', $state);
2021-05-12 21:01:26 +02:00
$this->saveVisit($last['placeId']);
2021-05-06 10:09:28 +02:00
return new JsonContent($response);
}
// save the selected place for the round in UserPlayedPlace
2021-05-12 21:01:26 +02:00
private function saveVisit($placeId): void
{
$session = $this->request->session();
$userId = $session->get('userId');
if(isset($userId)) {
$userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId);
if(!$userPlayedPlace) {
$userPlayedPlace = new UserPlayedPlace();
$userPlayedPlace->setUserId($userId);
$userPlayedPlace->setPlaceId($placeId);
} else {
$userPlayedPlace->incrementOccurrences();
}
$userPlayedPlace->setLastTimeDate(new DateTime());
$this->pdm->saveToDb($userPlayedPlace);
}
}
public function multiGuess(): IContent
{
$roomId = $this->request->query('roomId');
$session = $this->request->session();
if (!($multiState = $session->get('multiState')) || $multiState['roomId'] !== $roomId) {
return new JsonContent(['error' => 'no_session_found']);
}
$room = $this->multiRoomRepository->getByRoomId($roomId);
$state = $room->getStateArray();
$last = $state['rounds'][$state['currentRound']];
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
2021-05-12 21:01:26 +02:00
$result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
$responseFromMulti = $this->multiConnector->sendMessage('guess', [
'roomId' => $roomId,
'token' => $multiState['token'],
2021-04-03 13:38:16 +02:00
'guessPosition' => $guessPosition->toArray(),
'distance' => $result['distance'],
'score' => $result['score']
]);
if (isset($responseFromMulti['error'])) {
return new JsonContent(['error' => $responseFromMulti['error']]);
}
$response = [
2021-04-03 13:38:16 +02:00
'position' => $last['position']->toArray(),
'result' => $result,
'allResults' => $responseFromMulti['allResults']
];
return new JsonContent($response);
}
2021-05-12 21:01:26 +02:00
public function challengeGuess(): IContent
{
$session = $this->request->session();
$userId = $session->get('userId');
$challengeToken_str = $this->request->query('challengeToken');
$challenge = $this->challengeRepository->getByTokenStr($challengeToken_str);
if (!isset($challenge)) {
return new JsonContent(['error' => 'game_not_found']);
}
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge);
$currentRound = $userInChallenge->getRound();
$currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
$map = $this->mapRepository->getByPlace($currentPlace);
$placeInChallenge = $this->placeInChallengeRepository->getByPlaceAndChallenge($currentPlace, $challenge);
// fill in all other results for the round
$response['allResults'] = [];
foreach($this->guessRepository->getAllInChallengeByRound($currentRound, $challenge) as $guess) {
$user = $this->userRepository->getByGuess($guess);
$response['allResults'][] = [
'userName' => $user->getDisplayName(),
'guessPosition' => $guess->getPosition()->toArray(),
'distance' => $guess->getDistance(),
'score' => $guess->getScore()
];
}
2021-05-12 21:01:26 +02:00
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
$result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea());
// save guess
$guess = new Guess();
$guess->setUserId($userId);
$guess->setPlaceInChallenge($placeInChallenge);
$guess->setPosition($guessPosition);
$guess->setDistance($result['distance']);
$guess->setScore($result['score']);
$this->pdm->saveToDb($guess);
// $response = [
// 'position' => $currentPlace->getPosition()->toArray(),
// 'result' => $result
// ];
$response['position'] = $currentPlace->getPosition()->toArray();
$response['result'] = $result;
2021-05-12 21:01:26 +02:00
// update round
$nextRound = $currentRound + 1;
$userInChallenge->setRound($nextRound);
$this->pdm->saveToDb($userInChallenge);
if ($nextRound < static::NUMBER_OF_ROUNDS) {
$nextPlace = $this->placeRepository->getByRoundInChallenge($challenge, $nextRound);
$response['place'] = [
'panoId' => $nextPlace->getPanoIdCached(),
'pov' => $nextPlace->getPov()->toArray()
];
} else {
$guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge));
$places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge));
for($i = 0; $i < $nextRound; ++$i) {
$response['history'][] = [
'position' => $places[$i]->getPosition()->toArray(),
'result' => [
'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(),
'distance' => $guessesByUser[$i]->getDistance(),
'score' => $guessesByUser[$i]->getScore()
],
'allResults' => []
];
foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) {
if($guess->getUserId() != $userId) {
$user = $this->userRepository->getByGuess($guess);
$response['history'][$i]['allResults'][] = [
'userName' => $user->getDisplayName(),
'guessPosition' => $guess->getPosition()->toArray(),
'distance' => $guess->getDistance(),
'score' => $guess->getScore()
];
}
}
}
}
2021-05-12 21:01:26 +02:00
$this->saveVisit($currentPlace->getId());
return new JsonContent($response);
}
public function multiNextRound(): IContent
{
$roomId = $this->request->query('roomId');
$session = $this->request->session();
if (!($multiState = $session->get('multiState')) || $multiState['roomId'] !== $roomId) {
return new JsonContent(['error' => 'no_session_found']);
}
$room = $this->multiRoomRepository->getByRoomId($roomId);
$state = $room->getStateArray();
$members = $room->getMembersArray();
if ($members['owner'] !== $multiState['token']) {
return new JsonContent(['error' => 'not_owner_of_room']);
}
$state['currentRound'] += 1;
if ($state['currentRound'] < static::NUMBER_OF_ROUNDS) {
$this->multiConnector->sendMessage('next_round', ['roomId' => $roomId, 'currentRound' => $state['currentRound']]);
}
$room->setStateArray($state);
$room->setUpdatedDate(new DateTime());
$this->pdm->saveToDb($room);
return new JsonContent(['ok' => true]);
}
2021-05-12 21:01:26 +02:00
private function evaluateGuess(Position $realPosition, Position $guessPosition, float $area)
{
$distance = $this->calculateDistance($realPosition, $guessPosition);
$score = $this->calculateScore($distance, $area);
return ['distance' => $distance, 'score' => $score];
}
private function startNewGame(array &$state, int $mapId): void
{
$session = $this->request->session();
2021-05-06 20:25:48 +02:00
$userId = $session->get('userId');
$places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId);
$state['rounds'] = [];
$state['currentRound'] = 0;
foreach ($places as $place) {
$state['rounds'][] = [
'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));
}
}