feature/avoid-repeating-places-in-game #38
@ -7,8 +7,10 @@ use MapGuesser\Response\JsonContent;
|
|||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Multi\MultiConnector;
|
use MapGuesser\Multi\MultiConnector;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
use MapGuesser\PersistentData\Model\UserPlayedPlace;
|
||||||
use MapGuesser\Repository\MultiRoomRepository;
|
use MapGuesser\Repository\MultiRoomRepository;
|
||||||
use MapGuesser\Repository\PlaceRepository;
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
|
|
||||||
class GameFlowController
|
class GameFlowController
|
||||||
{
|
{
|
||||||
@ -25,6 +27,8 @@ class GameFlowController
|
|||||||
|
|
||||||
private PlaceRepository $placeRepository;
|
private PlaceRepository $placeRepository;
|
||||||
|
|
||||||
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@ -32,6 +36,7 @@ class GameFlowController
|
|||||||
$this->multiConnector = new MultiConnector();
|
$this->multiConnector = new MultiConnector();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
$this->placeRepository = new PlaceRepository();
|
$this->placeRepository = new PlaceRepository();
|
||||||
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialData(): IContent
|
public function initialData(): IContent
|
||||||
@ -43,8 +48,10 @@ class GameFlowController
|
|||||||
return new JsonContent(['error' => 'no_session_found']);
|
return new JsonContent(['error' => 'no_session_found']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
|
||||||
if (!isset($state['currentRound']) || $state['currentRound'] == -1 || $state['currentRound'] >= static::NUMBER_OF_ROUNDS) {
|
if (!isset($state['currentRound']) || $state['currentRound'] == -1 || $state['currentRound'] >= static::NUMBER_OF_ROUNDS) {
|
||||||
$this->startNewGame($state, $mapId);
|
$this->startNewGame($state, $mapId, $userId);
|
||||||
$session->set('state', $state);
|
$session->set('state', $state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +152,22 @@ class GameFlowController
|
|||||||
|
|
||||||
$session->set('state', $state);
|
$session->set('state', $state);
|
||||||
|
|
||||||
|
// save the selected place for the round in UserPlayedPlace
|
||||||
|
$userId = $session->get('userId');
|
||||||
|
if(isset($userId)) {
|
||||||
|
$placeId = $last['placeId'];
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonContent($response);
|
return new JsonContent($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,9 +245,9 @@ class GameFlowController
|
|||||||
return ['distance' => $distance, 'score' => $score];
|
return ['distance' => $distance, 'score' => $score];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function startNewGame(array &$state, int $mapId): void
|
private function startNewGame(array &$state, int $mapId, $userId = null): void
|
||||||
{
|
{
|
||||||
$places = $this->placeRepository->getRandomNForMapWithValidPano($mapId, static::NUMBER_OF_ROUNDS);
|
$places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId);
|
||||||
|
|
||||||
$state['rounds'] = [];
|
$state['rounds'] = [];
|
||||||
$state['currentRound'] = 0;
|
$state['currentRound'] = 0;
|
||||||
|
@ -28,6 +28,16 @@ class PlaceRepository
|
|||||||
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
|
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: use Map and User instead of id
|
||||||
|
public function getRandomNPlaces(int $mapId, int $n, int $userId = null): array
|
||||||
|
{
|
||||||
|
if(!isset($userId)) {
|
||||||
|
return $this->getRandomNForMapWithValidPano($mapId, $n);
|
||||||
|
} else {
|
||||||
|
return $this->getRandomNewNForMapWithValidPano($mapId, $n, $userId);
|
||||||
|
}
|
||||||
|
|||||||
|
}
|
||||||
|
|
||||||
//TODO: use Map instead of id
|
//TODO: use Map instead of id
|
||||||
public function getRandomNForMapWithValidPano(int $mapId, int $n, array $exclude = []): array
|
public function getRandomNForMapWithValidPano(int $mapId, int $n, array $exclude = []): array
|
||||||
{
|
{
|
||||||
@ -79,4 +89,119 @@ class PlaceRepository
|
|||||||
|
|
||||||
return $this->pdm->selectFromDb($select, Place::class);
|
return $this->pdm->selectFromDb($select, Place::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRandomNewNForMapWithValidPano(int $mapId, int $n, int $userId, array $exclude = []): array
|
||||||
|
{
|
||||||
|
$places = [];
|
||||||
|
|
||||||
|
// Never visited places
|
||||||
|
do {
|
||||||
|
$place = $this->getRandomNeverVisitedForMapWithValidPano($mapId, $userId, $exclude);
|
||||||
|
if(isset($place)) {
|
||||||
|
$places[] = $place;
|
||||||
|
$exclude[] = $place->getId();
|
||||||
|
}
|
||||||
|
} while(count($places) < $n && isset($place));
|
||||||
|
|
||||||
|
// Places visited in the longest time
|
||||||
|
while(count($places) < $n)
|
||||||
|
{
|
||||||
|
$place = $this->getRandomOldPlaceForMapWithValidPano($mapId, $userId, $exclude);
|
||||||
|
if(isset($place))
|
||||||
|
{
|
||||||
|
$places[] = $place;
|
||||||
|
$exclude[] = $place->getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $places;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRandomNeverVisitedForMapWithValidPano(int $mapId, $userId, array $exclude = []): ?Place
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
$place = $this->selectRandomNeverVisitedFromDbForMap($mapId, $userId, $exclude);
|
||||||
|
if($place === null) {
|
||||||
|
// there is no more never visited place left
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$panoId = $place->getFreshPanoId();
|
||||||
|
if($panoId === null) {
|
||||||
|
$exclude[] = $place->getId();
|
||||||
|
}
|
||||||
|
} while($panoId === null);
|
||||||
|
|
||||||
|
return $place;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function selectRandomNeverVisitedFromDbForMap(int $mapId, $userId, array $exclude): ?Place
|
||||||
|
{
|
||||||
|
$select_places_by_current_user = new Select(\Container::$dbConnection, 'user_played_place');
|
||||||
|
$select_places_by_current_user->columns(['place_id', 'last_time']);
|
||||||
|
$select_places_by_current_user->where('user_id', '=', $userId);
|
||||||
|
$select_places_by_current_user->setDerivedTableAlias('places_by_current_user');
|
||||||
|
|
||||||
|
$select_unvisited = new Select(\Container::$dbConnection, 'places');
|
||||||
|
$select_unvisited->leftJoin($select_places_by_current_user, ['places', 'id'], '=', ['places_by_current_user', 'place_id']);
|
||||||
|
$select_unvisited->where('map_id', '=', $mapId);
|
||||||
|
$select_unvisited->where('last_time', '=', null);
|
||||||
|
$select_unvisited->where('id', 'NOT IN', $exclude);
|
||||||
|
$numberOfUnvisitedPlaces = $select_unvisited->count();
|
||||||
|
|
||||||
|
if($numberOfUnvisitedPlaces == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$randomOffset = random_int(0, $numberOfUnvisitedPlaces - 1);
|
||||||
|
|
||||||
|
// $select_unvisited->orderBy('last_time');
|
||||||
|
$select_unvisited->limit(1, $randomOffset);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select_unvisited, Place::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRandomOldPlaceForMapWithValidPano(int $mapId, $userId, array $exclude = []): ?Place
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
$place = $this->selectRandomOldPlaceFromDbForMap($mapId, $userId, $exclude);
|
||||||
|
if($place === null) {
|
||||||
|
// there is no more never visited place left
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$panoId = $place->getFreshPanoId();
|
||||||
|
if($panoId === null) {
|
||||||
|
$exclude[] = $place->getId();
|
||||||
|
}
|
||||||
|
} while($panoId === null);
|
||||||
|
|
||||||
|
return $place;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function selectRandomOldPlaceFromDbForMap(int $mapId, $userId, array $exclude): ?Place
|
||||||
|
{
|
||||||
|
$select_places_by_current_user = new Select(\Container::$dbConnection, 'user_played_place');
|
||||||
|
$select_places_by_current_user->columns(['place_id', 'last_time']);
|
||||||
|
$select_places_by_current_user->where('user_id', '=', $userId);
|
||||||
|
$select_places_by_current_user->setDerivedTableAlias('places_by_current_user');
|
||||||
|
|
||||||
|
$select_old_place = new Select(\Container::$dbConnection, 'places');
|
||||||
|
$select_old_place->leftJoin($select_places_by_current_user, ['places', 'id'], '=', ['places_by_current_user', 'place_id']);
|
||||||
|
$select_old_place->where('map_id', '=', $mapId);
|
||||||
|
$select_old_place->where('last_time', 'IS NOT', null);
|
||||||
|
$select_old_place->where('id', 'NOT IN', $exclude);
|
||||||
|
$numberOfOldPlaces = $select_old_place->count();
|
||||||
|
|
||||||
|
if($numberOfOldPlaces == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$randomOffset = random_int(0, $numberOfOldPlaces - 1);
|
||||||
|
|
||||||
|
// $select_unvisited->orderBy('last_time');
|
||||||
|
$select_old_place->limit(1, $randomOffset);
|
||||||
|
|
||||||
|
return $this->pdm->selectFromDb($select_old_place, Place::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user
I would check if
count($unvisitedPlaces) == $n
before calling this function so we could save a function call and a DB query in a lot of cases.