from flask import Blueprint, abort, request, jsonify import db import lib from sources import sources, restock_all restock_all() game = Blueprint("game", __name__) def require_game(game_id): g = db.Game.query.get(game_id) if g is None: abort(404) return g def require_player(game_id): auth = request.headers.get("Authorization", type=str) if auth is None: abort(401) try: pid = int(auth.split(maxsplit=1)[-1]) except ValueError: abort(401) player = db.Player.query.get(pid) if player is None: abort(404) return player @game.route("", methods=["PUT"]) def create_game(): js = request.get_json() if js is None: abort(400) timer = js.get("timer", None) if not isinstance(timer, int) or timer <= 0: abort(400) rounds = js.get("rounds", None) if not isinstance(rounds, int) or rounds <= 0: abort(400) only_america = js.get("onlyAmerica", False) if not isinstance(only_america, bool): abort(400) gen_method = js.get("generationMethod", "MAPCRUNCH") if not isinstance(gen_method, str): abort(400) rule_set = js.get("ruleSet", "NORMAL") if not isinstance(rule_set, str): abort(400) src = sources.get((gen_method, only_america), None) if src is None: abort(400) coords = src.get_points(n=rounds) new_game = db.Game.create(timer, rounds, only_america, gen_method, rule_set, coords) return jsonify({"gameId": new_game.game_id}) @game.route("//config") def game_config(game_id): g = require_game(game_id) return jsonify({ "timer": g.timer, "rounds": g.rounds, "onlyAmerica": g.only_america, "generationMethod": g.gen_method, "ruleSet": g.rule_set, }) @game.route("//coords") def coords(game_id): g = require_game(game_id) return jsonify({ str(c.round_number): { "lat": c.latitude, "lng": c.longitude, } for c in g.coordinates }) @game.route("//players") def players(game_id): g = require_game(game_id) return jsonify({ "players": [{ "name": p.player_name, "currentRound": p.get_current_round(), "totalScore": p.get_total_score(), "guesses": { str(g.round_number): { "lat": g.latitude, "lng": g.longitude, "score": g.round_score, "timeRemaining": g.time_remaining, } for g in p.guesses }, } for p in g.players] }) @game.route("//linked", methods=["GET", "POST"]) def link_game(game_id): g = require_game(game_id) if request.method == "GET": return jsonify({"linkedGame": g.linked_game}) js = request.get_json() if js is None: abort(400) link_id = js.get("linkedGame", None) if link_id is None or db.Game.query.get(link_id) is None: abort(401) g.link(link_id) return "", 201 @game.route("//round//first") def first_submitter(game_id, round_num): fs = db.FirstSubmission.query.get((game_id, round_num)) if fs is None: abort(404) return jsonify({ "first": fs.player_name }) @game.route("//join", methods=["POST"]) def join(game_id): js = request.get_json() if js is None: abort(400) name = js.get("playerName", None) if name is None: abort(400) g = require_game(game_id) if db.Player.query.filter(db.Player.game_id == game_id, db.Player.player_name == name).first() is not None: abort(409) p = g.join(name) return jsonify({ "playerId": str(p.player_id) }), 201 @game.route("//current") def current_round(game_id): g = require_game(game_id) player = require_player(game_id) cur_rnd = player.get_current_round() if cur_rnd is None: coord = None else: lookup = db.Coordinate.query.get((game_id, cur_rnd)) cur_rnd = str(cur_rnd) if lookup is None: coord = None else: coord = { "lat": lookup.latitude, "lng": lookup.longitude, } if g.rule_set == "TIMEBANK": timer = player.get_last_round_time_remaining() if timer is None: timer = g.timer * g.rounds else: timer = g.timer return jsonify({ "currentRound": cur_rnd, "coord": coord, "timer": int(timer), }) @game.route("//guesses/", methods=["POST"]) def make_guess(game_id, round_num): g = require_game(game_id) player = require_player(game_id) if round_num != player.get_current_round(): abort(409) js = request.get_json() if js is None: abort(400) timed_out = js.get("timeout", False) if timed_out: player.add_timeout(round_num) if g.rule_set == "TIMEBANK": player.timeout_remaining_rounds() db.session.commit() return jsonify({ "score": 0, "totalScore": player.get_total_score(), "distance": None, }), 201 try: lat = float(js.get("lat", None)) lng = float(js.get("lng", None)) remaining = int(js.get("timeRemaining", None)) except ValueError: abort(400) target = db.Coordinate.query.get((game_id, round_num)) if target is None: abort(400) guess_score, distance = lib.score((target.latitude, target.longitude), (lat, lng)) player.add_guess(round_num, lat, lng, guess_score, remaining) if remaining <= 0 and g.rule_set == "TIMEBANK": player.timeout_remaining_rounds() return jsonify({ "score": guess_score, "totalScore": player.get_total_score(), "distance": distance, }), 201