game_api.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from flask import Blueprint, abort, request, jsonify
  2. import db
  3. import lib
  4. game = Blueprint("game", __name__)
  5. def require_game(game_id):
  6. g = db.Game.query.get(game_id)
  7. if g is None:
  8. abort(404)
  9. return g
  10. def require_player(game_id):
  11. auth = request.headers.get("Authorization", type=str)
  12. if auth is None:
  13. abort(401)
  14. try:
  15. pid = int(auth.split(maxsplit=1)[-1])
  16. except ValueError:
  17. abort(401)
  18. player = db.Player.query.get(pid)
  19. if player is None:
  20. abort(404)
  21. return player
  22. @game.route("", methods=["PUT"])
  23. def create_game():
  24. js = request.get_json()
  25. if js is None:
  26. abort(400)
  27. timer = js.get("timer", None)
  28. if not isinstance(timer, int) or timer <= 0:
  29. abort(400)
  30. rounds = js.get("rounds", None)
  31. if not isinstance(rounds, int) or rounds <= 0:
  32. abort(400)
  33. only_america = js.get("onlyAmerica", False)
  34. if not isinstance(only_america, bool):
  35. abort(400)
  36. coords = []
  37. for round_num in range(rounds):
  38. maybe_coord = lib.generate_coord(only_america=only_america)
  39. while maybe_coord is None:
  40. maybe_coord = lib.generate_coord(only_america=only_america)
  41. coords.append(maybe_coord)
  42. new_game = db.Game.create(timer, rounds, only_america, coords)
  43. return jsonify({"gameId": new_game.game_id})
  44. @game.route("/<game_id>/config")
  45. def game_config(game_id):
  46. g = require_game(game_id)
  47. return jsonify({
  48. "timer": g.timer,
  49. "rounds": g.rounds,
  50. "onlyAmerica": g.only_america,
  51. })
  52. @game.route("/<game_id>/coords")
  53. def coords(game_id):
  54. g = require_game(game_id)
  55. return jsonify({
  56. str(c.round_number): {
  57. "lat": c.latitude,
  58. "lng": c.longitude,
  59. } for c in g.coordinates
  60. })
  61. @game.route("/<game_id>/players")
  62. def players(game_id):
  63. g = require_game(game_id)
  64. return jsonify({
  65. "players": [{
  66. "name": p.player_name,
  67. "currentRound": p.get_current_round(),
  68. "totalScore": p.get_total_score(),
  69. "guesses": {
  70. str(g.round_number): {
  71. "lat": g.latitude,
  72. "lng": g.longitude,
  73. "score": g.round_score,
  74. "timeRemaining": g.time_remaining,
  75. } for g in p.guesses
  76. },
  77. } for p in g.players]
  78. })
  79. @game.route("/<game_id>/linked", methods=["GET", "POST"])
  80. def link_game(game_id):
  81. g = require_game(game_id)
  82. if request.method == "GET":
  83. return jsonify({"linkedGame": g.linked_game})
  84. js = request.get_json()
  85. if js is None:
  86. abort(400)
  87. link_id = js.get("linkedGame", None)
  88. if link_id is None or db.Game.query.get(link_id) is None:
  89. abort(401)
  90. g.link(link_id)
  91. return "", 201
  92. @game.route("/<game_id>/join", methods=["POST"])
  93. def join(game_id):
  94. js = request.get_json()
  95. if js is None:
  96. abort(400)
  97. name = js.get("playerName", None)
  98. if name is None:
  99. abort(400)
  100. g = require_game(game_id)
  101. if db.Player.query.filter(db.Player.game_id == game_id, db.Player.player_name == name).first() is not None:
  102. abort(409)
  103. p = g.join(name)
  104. return jsonify({
  105. "playerId": str(p.player_id)
  106. }), 201
  107. @game.route("/<game_id>/current")
  108. def current_round(game_id):
  109. g = require_game(game_id)
  110. player = require_player(game_id)
  111. cur_rnd = player.get_current_round()
  112. if cur_rnd is None:
  113. coord = None
  114. else:
  115. lookup = db.Coordinate.query.get((game_id, cur_rnd))
  116. cur_rnd = str(cur_rnd)
  117. if lookup is None:
  118. coord = None
  119. else:
  120. coord = {
  121. "lat": lookup.latitude,
  122. "lng": lookup.longitude,
  123. }
  124. return jsonify({
  125. "currentRound": cur_rnd,
  126. "coord": coord,
  127. "timer": g.timer,
  128. })
  129. @game.route("/<game_id>/guesses/<int:round_num>", methods=["POST"])
  130. def make_guess(game_id, round_num):
  131. player = require_player(game_id)
  132. if round_num != player.get_current_round():
  133. abort(409)
  134. js = request.get_json()
  135. if js is None:
  136. abort(400)
  137. timed_out = js.get("timeout", False)
  138. if timed_out:
  139. player.add_timeout(round_num)
  140. db.session.commit()
  141. return jsonify({
  142. "score": 0,
  143. "totalScore": player.get_total_score(),
  144. "distance": None,
  145. }), 201
  146. try:
  147. lat = float(js.get("lat", None))
  148. lng = float(js.get("lng", None))
  149. remaining = int(js.get("timeRemaining", None))
  150. except ValueError:
  151. abort(400)
  152. target = db.Coordinate.query.get((game_id, round_num))
  153. if target is None:
  154. abort(400)
  155. guess_score, distance = lib.score((target.latitude, target.longitude), (lat, lng))
  156. player.add_guess(round_num, lat, lng, guess_score, remaining)
  157. return jsonify({
  158. "score": guess_score,
  159. "totalScore": player.get_total_score(),
  160. "distance": distance,
  161. }), 201