scoring.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import math
  2. from typing import Tuple
  3. import haversine
  4. mean_earth_radius_km = (6378 + 6357) / 2
  5. # if you're more than 1/4 of the Earth's circumfrence away, you get 0
  6. max_dist_km = (math.pi * mean_earth_radius_km) / 2 # this is about 10,000 km
  7. # if you're within 1/16 of the Earth's circumfrence away, you get at least 1000 points
  8. quarter_of_max_km = max_dist_km / 4 # this is about 2,500 km
  9. # this is the total of the land area of all continents on Earth except Antarctica
  10. relevant_land_area = sum([
  11. 3.036e7, # https://www.wolframalpha.com/input?i=geographic+area+of+africa+in+km2
  12. 4.981e7, # https://www.wolframalpha.com/input?i=geographic+area+of+asia+in+km2
  13. 2.450e7, # https://www.wolframalpha.com/input?i=geographic+area+of+north+america+in+km2
  14. 1.782e7, # https://www.wolframalpha.com/input?i=geographic+area+of+south+america+in+km2
  15. 5.973e6, # https://www.wolframalpha.com/input?i=geographic+area+of+europe+in+km2
  16. 8.563e6, # https://www.wolframalpha.com/input?i=geographic+area+of+oceania+in+km2
  17. ])
  18. # this is the "radius" of an "average" continent
  19. # within this radius, you get at least 2000 points
  20. avg_continental_rad_km = math.sqrt(relevant_land_area / (6 * math.pi))
  21. # this works out to be approx 2700 km
  22. # somewhat arbitrarily, if you're within 1000 km, you get at least 3000 points
  23. one_thousand = 1000.0
  24. # this is the "radius" of the "average" country
  25. # within this radius, you get at least 4000 points
  26. # TODO increment or decrement number of countries as wars develop
  27. avg_country_rad_km = math.sqrt(relevant_land_area / (206 * math.pi))
  28. # this works out to be approx 460 km
  29. # if you're within 150m, you get a perfect score of 5000
  30. min_dist_km = 0.15
  31. def score_within(raw_dist: float, min_dist: float, max_dist: float) -> int:
  32. """
  33. Gives a score between 0 and 1000, with 1000 for the min_dist and 0 for the max_dist
  34. """
  35. # scale the distance down to [0.0, 1.0], then multiply it by 2 for easing
  36. pd2 = 2 * (raw_dist - min_dist) / (max_dist - min_dist)
  37. # perform a quadratic ease-in-out on pd2
  38. r = (pd2 ** 2) / 2 if pd2 < 1 else 1 - (((2 - pd2) ** 2) / 2)
  39. # use this to ease between 1000 and 0
  40. return int(1000 * (1 - r))
  41. def score(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]:
  42. """
  43. Takes in two (latitude, longitude) pairs and produces an int score.
  44. Score is in the (inclusive) range [0, 5000]
  45. Higher scores are closer.
  46. Returns (score, distance in km)
  47. """
  48. dist_km = haversine.haversine(target, guess)
  49. if dist_km <= min_dist_km:
  50. point_score = 5000
  51. elif dist_km <= avg_country_rad_km:
  52. point_score = 4000 + score_within(dist_km, min_dist_km, avg_country_rad_km)
  53. elif dist_km <= one_thousand:
  54. point_score = 3000 + score_within(dist_km, avg_country_rad_km, one_thousand)
  55. elif dist_km <= avg_continental_rad_km:
  56. point_score = 2000 + score_within(dist_km, one_thousand, avg_continental_rad_km)
  57. elif dist_km <= quarter_of_max_km:
  58. point_score = 1000 + score_within(dist_km, avg_continental_rad_km, quarter_of_max_km)
  59. elif dist_km <= max_dist_km:
  60. point_score = score_within(dist_km, quarter_of_max_km, max_dist_km)
  61. else: # dist_km > max_dist_km
  62. point_score = 0
  63. return point_score, dist_km
  64. def score_country_race(target: str, guess: str, time_remaining: int, time_total: int):
  65. if target != guess:
  66. return 0
  67. time_used = time_total - time_remaining
  68. if time_used <= 5:
  69. return 5000
  70. # TODO make this into an interesting curve but for now linear is fine
  71. return int(5000 * (time_remaining / time_total))
  72. def score_hard(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]:
  73. """
  74. Takes in two (latitude, longitude) pairs and produces an int score.
  75. Score is in the (inclusive) range [0, 5000]
  76. Higher scores are closer.
  77. Scoring is much more punishing than standard
  78. Returns (score, distance in km)
  79. """
  80. dist_km = haversine.haversine(target, guess)
  81. return max(0, 5000 - int(dist_km * 10)), dist_km
  82. def score_nightmare(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]:
  83. """
  84. Takes in two (latitude, longitude) pairs and produces an int score.
  85. Score is in the range (-inf, 5000]
  86. Higher scores are closer.
  87. Scoring is much, MUCH more punishing than standard
  88. Returns (score, distance in km)
  89. """
  90. dist_km = haversine.haversine(target, guess)
  91. return 5000 - int(dist_km * 1000), dist_km