diff options
Diffstat (limited to 'content/posts/2024-12-12-guessr')
-rw-r--r-- | content/posts/2024-12-12-guessr/index.md | 106 | ||||
-rw-r--r-- | content/posts/2024-12-12-guessr/logo.jpg | bin | 0 -> 121344 bytes |
2 files changed, 106 insertions, 0 deletions
diff --git a/content/posts/2024-12-12-guessr/index.md b/content/posts/2024-12-12-guessr/index.md new file mode 100644 index 0000000..81125f7 --- /dev/null +++ b/content/posts/2024-12-12-guessr/index.md @@ -0,0 +1,106 @@ ++++ +title = "Guessr" +description = "" +date = 2024-12-12T22:27:49+03:00 +categories = [ "Мои проекты" ] +tags = [ "IT", "Проект выходного дня" ] +location = "Казань" +image="logo.jpg" ++++ + +На недавних выходных я запилил очередной «проект выходного дня». На этот раз — аналог известного сервиса GeoGuessr, но +в отличие от него, все точки сконцентрированы в моей родной Казани. Ну и я не использую панорамы, а фотографии мест. + +Я обещал выложить исходники, и в общем, вот они: https://git.neonxp.ru/guessr.git/ + +## Немного про разработку + +Первым встал вопрос, откуда брать данные, а именно фотографии и координаты точек. Пару лет назад нашу страну покинул +такой проект, как Ingress, представлявший собой гео игру в дополненной реальности. В свою очередь, я посчитал, что раз +проект решил отказаться от нас, как игроков, я посчитал морально оправданным ~~спиз~~экспропреировать кусочек их данных, +а именно спарсил с их карты intel.ingress.com т.н. «порталы», которые, по сути и есть эти самые геоточки с фотографиями. + +Дамп я загнал в Postgresql с подключенным расширением [Postgis](https://postgis.net/). + +Ну а далее написал достаточно простой API на Golang, который реализует следующие методы: + +- Создание новой игровой сессии, в ответ ставится кука внутри которой зашифровано текущее состояние — ник, количество + очков, ID текущего угадываемого объекта (в начале пустое). + ```http + POST /api/state + Content-Type: application/json + + { + "username": "NeonXP" + } + ``` +- Получение состояния. Просто возвращает вышеуказанные параметры + ```http + GET /api/state + ``` +- Выдача нового объекта для угадывания. При этом возвращается ссылка на фото и обновляется состояние, тем что в него + вписывается ID объекта + ```http + POST /api/next + ``` +- Угадывание. Собственно, на вход передаются координаты куда на карте указал игрок. А в ответ возвращается: + - Название объекта + - Расстояние от переданной точки до реального размещения объекта + - Geojson строка в которой зашифрована линия соединяющая точку и объект (нужна для отрисовки красной линии на карте) + При этом высчитываются очки которые получает игрок за попытку по формуле max(1000-d, 0), где d - расстояние между + выбранной точкой и объектом в метрах. То есть, если разница меньше 1000м, то чем ближе - тем больше очков (максимум + 1000 очков за 1 очень точное угадывание). + ```http + POST /api/guess + Content-Type: application/json + + { + "lat": 55.123, + "lon": 49.123 + } + ``` + +Вот в общем-то и всё API! + +Из интересностей, при выборе очередной точки у неё в БД увеличивается счетчик, а сам select выбирает случайную точку +только среди тех точек, где этот счетчик минимальный. То есть, пока не будут выданы игрокам все точки, уже выбранные +заново не будут выданы. Вот это место в коде: https://git.neonxp.ru/guessr.git/tree/pkg/service/places.go#n26 +(стр. 26-32) +```go +err = btx.NewSelect(). + ColumnExpr(`p.guid, p.img`). + Model(r). + Where(`p.count = (SELECT MIN(pl.count) FROM places pl WHERE pl.deleted_at IS NULL)`). + OrderExpr(`RANDOM()`). + Limit(1). + Scan(ctx, r) +``` + +Ещё я бы отметил то, что я решил по максимуму логику вынести в БД, и, например, при угадывании расстояние до точки, а +также вышеупомянутый geojson формируются так же на стороне БД: +https://git.neonxp.ru/guessr.git/tree/pkg/service/places.go#n50 (стр. 50-59) + +```go +err := p.db.NewSelect(). + Model(&model.Place{GUID: guid}). + WherePK("guid"). + ColumnExpr(`p.name, p.guid, p.img, + ST_Distance(ST_MakePoint(?, ?)::geography, p.position::geography)::int AS distance, + ST_AsGeoJSON(ST_MakeLine( + ST_SetSRID(ST_MakePoint(?, ?), 4326), + ST_SetSRID(p.position, 4326) + )) AS geojson`, lon, lat, lon, lat). + Scan(ctx, r) +``` + +# Дальнейшие планы + +В комментах к анонсу ребята накидали достаточно много хороших идей, синтезировав которые, и добавив свои хотелки я +составил примерно такой чеклист: + +- [ ] Авторизация и общая доска лидерства +- [ ] После угадывания спрашивать у игрока «сложность», чтобы потом можно было, например, настраивать чтобы попадались + только простые объекты. И, например, разное количество очков за простые и сложные объекты +- [ ] Подумать как вынести игру в оффлайн, по типу того же ингресса. Это сложно и предстоит хорошо это обдумать + +Как-то так :) А впереди новые выходные и новые «проекты выходного дня»!
\ No newline at end of file diff --git a/content/posts/2024-12-12-guessr/logo.jpg b/content/posts/2024-12-12-guessr/logo.jpg Binary files differnew file mode 100644 index 0000000..d9cfbfd --- /dev/null +++ b/content/posts/2024-12-12-guessr/logo.jpg |