MAPG-235 basic gameflow works

This commit is contained in:
Balázs Vigh 2021-05-12 21:01:26 +02:00
parent 5daed10036
commit 93f8fc3f34
10 changed files with 209 additions and 55 deletions

View File

@ -14,7 +14,6 @@ CREATE TABLE `user_in_challenge` (
`user_id` int(10) unsigned NOT NULL, `user_id` int(10) unsigned NOT NULL,
`challenge_id` int(10) unsigned NOT NULL, `challenge_id` int(10) unsigned NOT NULL,
`round` smallint(5) signed NOT NULL DEFAULT 0, `round` smallint(5) signed NOT NULL DEFAULT 0,
`score` int(10) unsigned NOT NULL DEFAULT 0,
`time_left` int(10) unsigned, `time_left` int(10) unsigned,
`is_owner` tinyint(1) NOT NULL DEFAULT 0, `is_owner` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
@ -43,6 +42,8 @@ CREATE TABLE `guesses` (
`place_in_challenge_id` int(10) unsigned NOT NULL, `place_in_challenge_id` int(10) unsigned NOT NULL,
`lat` decimal(8,6) NOT NULL, `lat` decimal(8,6) NOT NULL,
`lng` decimal(9,6) NOT NULL, `lng` decimal(9,6) NOT NULL,
`score` int(10) NOT NULL,
`distance` int(10) NOT NULL,
`time_spent` int(10), `time_spent` int(10),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `user_id` (`user_id`), KEY `user_id` (`user_id`),

View File

@ -294,9 +294,6 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2});
return; return;
} }
Game.panoId = this.response.place.panoId;
Game.pov = this.response.place.pov;
for (var i = 0; i < this.response.history.length; ++i) { for (var i = 0; i < this.response.history.length; ++i) {
var round = this.response.history[i]; var round = this.response.history[i];
Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] });
@ -305,6 +302,35 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2});
Game.scoreSum += round.result.score; Game.scoreSum += round.result.score;
} }
if (this.response.finished) {
// TODO: refactor - it is necessary for mobile
if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') {
document.getElementById('showGuessButton').click();
}
if (Game.adaptGuess) {
document.getElementById('guess').classList.remove('adapt');
}
if (Game.guessMarker) {
Game.guessMarker.setMap(null);
Game.guessMarker = null;
}
document.getElementById('guess').classList.add('result');
Game.map.setOptions({
draggableCursor: 'grab'
});
Game.showSummary();
return;
}
Game.panoId = this.response.place.panoId;
Game.pov = this.response.place.pov;
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE); document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
@ -561,7 +587,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2});
data.append('lng', String(guessPosition.lng)); data.append('lng', String(guessPosition.lng));
document.getElementById('loading').style.visibility = 'visible'; document.getElementById('loading').style.visibility = 'visible';
var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json'; var url = Game.getGameIdentifier() + '/guess.json';
MapGuesser.httpRequest('POST', url, function () { MapGuesser.httpRequest('POST', url, function () {
document.getElementById('loading').style.visibility = 'hidden'; document.getElementById('loading').style.visibility = 'hidden';

View File

@ -244,7 +244,9 @@ class GameController
if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) {
$userInChallenge = new UserInChallenge(); $userInChallenge = new UserInChallenge();
$userInChallenge->setUserId($userId); $userInChallenge->setUserId($userId);
$userInChallenge->setChallenge($challenge);
$userInChallenge->setTimeLeft($challenge->getTimerSec()); $userInChallenge->setTimeLeft($challenge->getTimerSec());
$this->pdm->saveToDb($userInChallenge);
} }
$map = $this->mapRepository->getByChallenge($challenge); $map = $this->mapRepository->getByChallenge($challenge);

View File

@ -8,9 +8,13 @@ use MapGuesser\Interfaces\Response\IContent;
use MapGuesser\Multi\MultiConnector; use MapGuesser\Multi\MultiConnector;
use MapGuesser\PersistentData\Model\UserInChallenge; use MapGuesser\PersistentData\Model\UserInChallenge;
use MapGuesser\PersistentData\PersistentDataManager; use MapGuesser\PersistentData\PersistentDataManager;
use MapGuesser\PersistentData\Model\Guess;
use MapGuesser\PersistentData\Model\UserPlayedPlace; use MapGuesser\PersistentData\Model\UserPlayedPlace;
use MapGuesser\Repository\ChallengeRepository; use MapGuesser\Repository\ChallengeRepository;
use MapGuesser\Repository\GuessRepository;
use MapGuesser\Repository\MapRepository;
use MapGuesser\Repository\MultiRoomRepository; use MapGuesser\Repository\MultiRoomRepository;
use MapGuesser\Repository\PlaceInChallengeRepository;
use MapGuesser\Repository\PlaceRepository; use MapGuesser\Repository\PlaceRepository;
use MapGuesser\Repository\UserInChallengeRepository; use MapGuesser\Repository\UserInChallengeRepository;
use MapGuesser\Repository\UserPlayedPlaceRepository; use MapGuesser\Repository\UserPlayedPlaceRepository;
@ -30,12 +34,18 @@ class GameFlowController
private PlaceRepository $placeRepository; private PlaceRepository $placeRepository;
private MapRepository $mapRepository;
private UserPlayedPlaceRepository $userPlayedPlaceRepository; private UserPlayedPlaceRepository $userPlayedPlaceRepository;
private ChallengeRepository $challengeRepository; private ChallengeRepository $challengeRepository;
private UserInChallengeRepository $userInChallengeRepository; private UserInChallengeRepository $userInChallengeRepository;
private PlaceInChallengeRepository $placeInChallengeRepository;
private GuessRepository $guessRepository;
public function __construct(IRequest $request) public function __construct(IRequest $request)
{ {
$this->request = $request; $this->request = $request;
@ -43,9 +53,12 @@ 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->mapRepository = new MapRepository();
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository(); $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
$this->challengeRepository = new ChallengeRepository(); $this->challengeRepository = new ChallengeRepository();
$this->userInChallengeRepository = new UserInChallengeRepository(); $this->userInChallengeRepository = new UserInChallengeRepository();
$this->placeInChallengeRepository = new PlaceInChallengeRepository();
$this->guessRepository = new GuessRepository();
} }
public function initialData(): IContent public function initialData(): IContent
@ -126,7 +139,6 @@ class GameFlowController
public function challengeInitialData(): IContent public function challengeInitialData(): IContent
{ {
// TODO
$session = $this->request->session(); $session = $this->request->session();
$userId = $session->get('userId'); $userId = $session->get('userId');
$challengeToken_str = $this->request->query('challengeToken'); $challengeToken_str = $this->request->query('challengeToken');
@ -138,34 +150,36 @@ class GameFlowController
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge);
$currentRound = $userInChallenge->getRound(); $currentRound = $userInChallenge->getRound();
$currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
$currentPlace = $this->placeRepository->getCurrentInChallenge($challenge, $currentRound);
$response = []; $response = [];
// $last = $state['rounds'][$state['currentRound']]; $response['history'] = [];
// $response['place'] = [
// 'panoId' => $last['panoId'],
// 'pov' => $last['pov']->toArray()
// ];
$guesses = iterator_to_array($this->guessRepository->getAllInChallenge($userId, $challenge));
$places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge));
for($i = 0; $i < $currentRound; ++$i)
{
$response['history'][] = [
'position' => $places[$i]->getPosition()->toArray(),
'result' => [
'guessPosition' => $guesses[$i]->getPosition()->toArray(),
'distance' => $guesses[$i]->getDistance(),
'score' => $guesses[$i]->getScore()
]
];
}
if(!isset($currentPlace)) { // game finished
$response['finished'] = true;
} else { // continue game
$response['place'] = [ $response['place'] = [
'panoId' => $currentPlace->getPanoIdCached(), 'panoId' => $currentPlace->getPanoIdCached(),
'pov' => [$currentPlace->getPov()->toArray()] 'pov' => [$currentPlace->getPov()->toArray()]
]; ];
}
$response['history'] = [];
// for ($i = 0; $i < $state['currentRound']; ++$i) {
// $round = $state['rounds'][$i];
// $response['history'][] = [
// 'position' => $round['position']->toArray(),
// 'result' => [
// 'guessPosition' => $round['guessPosition']->toArray(),
// 'distance' => $round['distance'],
// 'score' => $round['score']
// ]
// ];
// }
return new JsonContent($response); return new JsonContent($response);
} }
@ -181,7 +195,7 @@ class GameFlowController
$last = $state['rounds'][$state['currentRound']]; $last = $state['rounds'][$state['currentRound']];
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
$result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']); $result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
$last['guessPosition'] = $guessPosition; $last['guessPosition'] = $guessPosition;
$last['distance'] = $result['distance']; $last['distance'] = $result['distance'];
@ -205,19 +219,18 @@ class GameFlowController
$session->set('state', $state); $session->set('state', $state);
$this->saveVisit($last); $this->saveVisit($last['placeId']);
return new JsonContent($response); return new JsonContent($response);
} }
// save the selected place for the round in UserPlayedPlace // save the selected place for the round in UserPlayedPlace
private function saveVisit($last): void private function saveVisit($placeId): void
{ {
$session = $this->request->session(); $session = $this->request->session();
$userId = $session->get('userId'); $userId = $session->get('userId');
if(isset($userId)) { if(isset($userId)) {
$placeId = $last['placeId'];
$userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId); $userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId);
if(!$userPlayedPlace) { if(!$userPlayedPlace) {
$userPlayedPlace = new UserPlayedPlace(); $userPlayedPlace = new UserPlayedPlace();
@ -245,7 +258,7 @@ class GameFlowController
$last = $state['rounds'][$state['currentRound']]; $last = $state['rounds'][$state['currentRound']];
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
$result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']); $result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']);
$responseFromMulti = $this->multiConnector->sendMessage('guess', [ $responseFromMulti = $this->multiConnector->sendMessage('guess', [
'roomId' => $roomId, 'roomId' => $roomId,
@ -268,6 +281,61 @@ class GameFlowController
return new JsonContent($response); return new JsonContent($response);
} }
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);
$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
];
// 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()
];
}
$this->saveVisit($currentPlace->getId());
return new JsonContent($response);
}
public function multiNextRound(): IContent public function multiNextRound(): IContent
{ {
$roomId = $this->request->query('roomId'); $roomId = $this->request->query('roomId');
@ -297,7 +365,7 @@ class GameFlowController
return new JsonContent(['ok' => true]); return new JsonContent(['ok' => true]);
} }
private function evalueteGuess(Position $realPosition, Position $guessPosition, float $area) private function evaluateGuess(Position $realPosition, Position $guessPosition, float $area)
{ {
$distance = $this->calculateDistance($realPosition, $guessPosition); $distance = $this->calculateDistance($realPosition, $guessPosition);
$score = $this->calculateScore($distance, $area); $score = $this->calculateScore($distance, $area);

View File

@ -4,9 +4,9 @@ use MapGuesser\Util\Geo\Position;
class Guess extends Model class Guess extends Model
{ {
protected static string $table = 'guess'; protected static string $table = 'guesses';
protected static array $fields = ['user_id', 'place_in_challenge_id', 'lat', 'lng', 'time_spent']; protected static array $fields = ['user_id', 'place_in_challenge_id', 'lat', 'lng', 'score', 'distance', 'time_spent'];
protected static array $relations = ['user' => User::class, 'place_in_challenge' => PlaceInChallenge::class]; protected static array $relations = ['user' => User::class, 'place_in_challenge' => PlaceInChallenge::class];
@ -20,7 +20,16 @@ class Guess extends Model
private Position $position; private Position $position;
private int $timeSpent; private int $score = 0;
private int $distance = 0;
private int $timeSpent = 0;
public function __construct()
{
$this->position = new Position(0.0, 0.0);
}
public function setUser(User $user): void public function setUser(User $user): void
{ {
@ -57,6 +66,16 @@ class Guess extends Model
$this->position->setLng($lng); $this->position->setLng($lng);
} }
public function setScore(int $score): void
{
$this->score = $score;
}
public function setDistance(int $distance): void
{
$this->distance = $distance;
}
public function setTimeSpent(int $timeSpent): void public function setTimeSpent(int $timeSpent): void
{ {
$this->timeSpent = $timeSpent; $this->timeSpent = $timeSpent;
@ -97,6 +116,16 @@ class Guess extends Model
return $this->position->getLng(); return $this->position->getLng();
} }
public function getScore(): int
{
return $this->score;
}
public function getDistance(): int
{
return $this->distance;
}
public function getTimeSpent(): ?int public function getTimeSpent(): ?int
{ {
return $this->timeSpent; return $this->timeSpent;

View File

@ -4,7 +4,7 @@ class UserInChallenge extends Model
{ {
protected static string $table = 'user_in_challenge'; protected static string $table = 'user_in_challenge';
protected static array $fields = ['user_id', 'challenge_id', 'round', 'score', 'time_left', 'is_owner']; protected static array $fields = ['user_id', 'challenge_id', 'round', 'time_left', 'is_owner'];
protected static array $relations = ['user' => User::class, 'challenge' => Challenge::class]; protected static array $relations = ['user' => User::class, 'challenge' => Challenge::class];
@ -18,8 +18,6 @@ class UserInChallenge extends Model
private int $round = 0; private int $round = 0;
private int $score = 0;
private ?int $timeLeft = null; private ?int $timeLeft = null;
private bool $isOwner = false; private bool $isOwner = false;
@ -49,11 +47,6 @@ class UserInChallenge extends Model
$this->round = $round; $this->round = $round;
} }
public function setScore(int $score): void
{
$this->score = $score;
}
public function setTimeLeft(?int $timeLeft): void public function setTimeLeft(?int $timeLeft): void
{ {
if(isset($timeLeft)) { if(isset($timeLeft)) {
@ -91,11 +84,6 @@ class UserInChallenge extends Model
return $this->round; return $this->round;
} }
public function getScore(): int
{
return $this->score;
}
public function getTimeLeft(): ?int public function getTimeLeft(): ?int
{ {
return $this->timeLeft; return $this->timeLeft;

View File

@ -21,7 +21,7 @@ class GuessRepository
public function getAllByUserAndChallenge(User $user, Challenge $challenge) : Generator public function getAllByUserAndChallenge(User $user, Challenge $challenge) : Generator
{ {
$select = new Select(\Container::$dbConnection); $select = new Select(\Container::$dbConnection);
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
$select->where('user_id', '=', $user->getId()); $select->where('user_id', '=', $user->getId());
$select->where('challenge_id', '=', $challenge->getId()); $select->where('challenge_id', '=', $challenge->getId());
@ -31,11 +31,22 @@ class GuessRepository
public function getByUserAndPlaceInChallenge(User $user, Challenge $challenge, Place $place) : ?Guess public function getByUserAndPlaceInChallenge(User $user, Challenge $challenge, Place $place) : ?Guess
{ {
$select = new Select(\Container::$dbConnection); $select = new Select(\Container::$dbConnection);
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
$select->where('user_id', '=', $user->getId()); $select->where('user_id', '=', $user->getId());
$select->where('challenge_id', '=', $challenge->getId()); $select->where('challenge_id', '=', $challenge->getId());
$select->where('place_id', '=', $place->getId()); $select->where('place_id', '=', $place->getId());
return $this->pdm->selectFromDb($select, Guess::class); return $this->pdm->selectFromDb($select, Guess::class);
} }
public function getAllInChallenge(int $userId, Challenge $challenge): Generator
{
$select = new Select(\Container::$dbConnection);
$select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']);
$select->where('user_id', '=', $userId);
$select->where('challenge_id', '=', $challenge->getId());
$select->orderBy('order');
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
}
} }

View File

@ -3,6 +3,7 @@
use MapGuesser\Database\Query\Select; use MapGuesser\Database\Query\Select;
use MapGuesser\PersistentData\Model\Challenge; use MapGuesser\PersistentData\Model\Challenge;
use MapGuesser\PersistentData\Model\Map; use MapGuesser\PersistentData\Model\Map;
use MapGuesser\PersistentData\Model\Place;
use MapGuesser\PersistentData\PersistentDataManager; use MapGuesser\PersistentData\PersistentDataManager;
class MapRepository class MapRepository
@ -19,6 +20,14 @@ class MapRepository
return $this->pdm->selectFromDbById($mapId, Map::class); return $this->pdm->selectFromDbById($mapId, Map::class);
} }
public function getByPlace(Place $place): ?Map
{
$select = new Select(\Container::$dbConnection);
$select->where('id', '=', $place->getMapId());
return $this->pdm->selectFromDb($select, Map::class);
}
public function getByChallenge(Challenge $challenge): ?Map public function getByChallenge(Challenge $challenge): ?Map
{ {
$select = new Select(\Container::$dbConnection); $select = new Select(\Container::$dbConnection);

View File

@ -31,4 +31,13 @@ class PlaceInChallengeRepository
yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class); yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class);
} }
public function getByPlaceAndChallenge(Place $place, Challenge $challenge) : ?PlaceInChallenge
{
$select = new Select(\Container::$dbConnection);
$select->where('place_id', '=', $place->getId());
$select->where('challenge_id', '=', $challenge->getId());
return $this->pdm->selectFromDb($select, PlaceInChallenge::class);
}
} }

View File

@ -178,14 +178,25 @@ class PlaceRepository
return $places; return $places;
} }
public function getCurrentInChallenge(Challenge $challenge, int $round): ?Place public function getByRoundInChallenge(Challenge $challenge, int $round): ?Place
{ {
$select = new Select(\Container::$dbConnection); $select = new Select(\Container::$dbConnection);
$select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']);
$select->where('challenge_id', '=', $challenge->getId());
$select->orderBy('order'); $select->orderBy('order');
$select->limit(1, $round); $select->limit(1, $round);
return $this->pdm->selectFromDb($select, Place::class); return $this->pdm->selectFromDb($select, Place::class);
} }
public function getAllInChallenge(Challenge $challenge): Generator
{
$select = new Select(\Container::$dbConnection);
$select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']);
$select->where('challenge_id', '=', $challenge->getId());
$select->orderBy('order');
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
}
} }