from functools import wraps import uuid from flask import Blueprint, abort, request, current_app, jsonify from db import db, Game, CoordSet, GuessSet from lib import generate_coord, score game = Blueprint("game", __name__) def require_name(): name = request.headers.get("Authorization", type=str) if name is None: abort(401) return name.split(maxsplit=1)[-1] def require_game(game_id): g = Game.query.get(game_id) if g is None: abort(404) return g def require_guess_set(game_id): name = require_name() gs = GuessSet.query.get((game_id, name)) if gs is None: abort(404) return gs @game.route("", methods=["PUT"]) def create_game(): name = require_name() timer = request.json.get("timer", None) if not isinstance(timer, int) or timer <= 0: abort(400) game_id = str(uuid.uuid4()) while Game.query.get(game_id) is not None: # basically impossible collision, but let's be safe game_id = str(uuid.uuid4()) key = current_app.config["GOOGLE_API_KEY"] cs = CoordSet() for round_num in "12345": coord = generate_coord(key) while coord is None: # very unlikely, but it is possible for generate_coord to fail coord = generate_coord(key) cs.set_coord(round_num, *coord) db.session.add(cs) db.session.commit() new_game = Game( game_id=game_id, timer=timer, creator=name, coord_set=cs ) db.session.add(new_game) db.session.commit() gs = GuessSet(game_id=game_id, player_name=name, coord_set=CoordSet()) db.session.add(gs) db.session.commit() return jsonify({ "gameId": game_id }) @game.route("/") def game_settings(game_id): g = require_game(game_id) return jsonify({ "gameId": g.game_id, "creator": g.creator, "timer": g.timer, "coords": g.coord_set.to_dict(), "players": sorted([ { "name": gs.player_name, "currentRound": gs.get_current_round(), "totalScore": gs.get_total_score(), "guesses": gs.to_dict(), } for gs in g.guess_sets ], key=lambda x: x["totalScore"] or 0, reverse=True), }) @game.route("//join", methods=["POST"]) def join(game_id): name = require_name() g = require_game(game_id) if GuessSet.query.get((g.game_id, name)) is not None: abort(409) cs = CoordSet() db.session.add(cs) db.session.commit() gs = GuessSet(game_id=g.game_id, player_name=name, coord_set=cs) db.session.add(gs) db.session.commit() return "", 201 @game.route("//guesses") def guesses(game_id): gs = require_guess_set(game_id) return jsonify({ "currentRound": gs.get_current_round(), "guesses": gs.to_dict(), }) @game.route("//guesses/", methods=["POST"]) def make_guess(game_id, round_num): gs = require_guess_set(game_id) if round_num != gs.get_current_round(): abort(409) timed_out = request.json.get("timeout", False) if timed_out: gs.set_timed_out(round_num) db.session.commit() return jsonify({ "score": 0, "totalScore": gs.get_total_score(), }), 201 lat = request.json.get("lat", None) lng = request.json.get("lng", None) if not isinstance(lat, float) or not isinstance(lng, float): abort(400) target = require_game(game_id).coord_set.get_coord(round_num) guess_score = score(target, (lat, lng)) gs.set_guess(round_num, lat, lng, guess_score) db.session.commit() return jsonify({ "score": guess_score, "totalScore": gs.get_total_score(), }), 201