import math from typing import Tuple import haversine mean_earth_radius_km = (6378 + 6357) / 2 # if you're more than 1/4 of the Earth's circumfrence away, you get 0 max_dist_km = (math.pi * mean_earth_radius_km) / 2 # this is about 10,000 km # if you're within 1/16 of the Earth's circumfrence away, you get at least 1000 points quarter_of_max_km = max_dist_km / 4 # this is about 2,500 km # this is the total of the land area of all continents on Earth except Antarctica relevant_land_area = sum([ 3.036e7, # https://www.wolframalpha.com/input?i=geographic+area+of+africa+in+km2 4.981e7, # https://www.wolframalpha.com/input?i=geographic+area+of+asia+in+km2 2.450e7, # https://www.wolframalpha.com/input?i=geographic+area+of+north+america+in+km2 1.782e7, # https://www.wolframalpha.com/input?i=geographic+area+of+south+america+in+km2 5.973e6, # https://www.wolframalpha.com/input?i=geographic+area+of+europe+in+km2 8.563e6, # https://www.wolframalpha.com/input?i=geographic+area+of+oceania+in+km2 ]) # this is the "radius" of an "average" continent # within this radius, you get at least 2000 points avg_continental_rad_km = math.sqrt(relevant_land_area / (6 * math.pi)) # this works out to be approx 2700 km # somewhat arbitrarily, if you're within 1000 km, you get at least 3000 points one_thousand = 1000.0 # this is the "radius" of the "average" country # within this radius, you get at least 4000 points # TODO increment or decrement number of countries as wars develop avg_country_rad_km = math.sqrt(relevant_land_area / (206 * math.pi)) # this works out to be approx 460 km # if you're within 150m, you get a perfect score of 5000 min_dist_km = 0.15 def score_within(raw_dist: float, min_dist: float, max_dist: float) -> int: """ Gives a score between 0 and 1000, with 1000 for the min_dist and 0 for the max_dist """ # scale the distance down to [0.0, 1.0], then multiply it by 2 for easing pd2 = 2 * (raw_dist - min_dist) / (max_dist - min_dist) # perform a quadratic ease-in-out on pd2 r = (pd2 ** 2) / 2 if pd2 < 1 else 1 - (((2 - pd2) ** 2) / 2) # use this to ease between 1000 and 0 return int(1000 * (1 - r)) def ramp(dist_km: float) -> float: if dist_km <= min_dist_km: return 5000 elif dist_km <= avg_country_rad_km: return 4000 + score_within(dist_km, min_dist_km, avg_country_rad_km) elif dist_km <= one_thousand: return 3000 + score_within(dist_km, avg_country_rad_km, one_thousand) elif dist_km <= avg_continental_rad_km: return 2000 + score_within(dist_km, one_thousand, avg_continental_rad_km) elif dist_km <= quarter_of_max_km: return 1000 + score_within(dist_km, avg_continental_rad_km, quarter_of_max_km) elif dist_km <= max_dist_km: return score_within(dist_km, quarter_of_max_km, max_dist_km) else: # dist_km > max_dist_km return 0 def score(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]: """ Takes in two (latitude, longitude) pairs and produces an int score. Score is in the (inclusive) range [0, 5000] Higher scores are closer. Returns (score, distance in km) """ dist_km = haversine.haversine(target, guess) return ramp(dist_km), dist_km def score_pow(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]: """ Takes in two (latitude, longitude) pairs and produces an int score. Score is in the (inclusive) range [0, 5000] Higher scores are closer. Uses the same ramp as standard score, but raises the distance to a power first Returns (score, distance in km) """ dist_km = haversine.haversine(target, guess) return ramp(dist_km ** 1.2), dist_km def score_country_race(target: str, guess: str, time_remaining: int, time_total: int): if target != guess: return 0 time_used = time_total - time_remaining if time_used <= 5: return 5000 # TODO make this into an interesting curve but for now linear is fine return int(5000 * (time_remaining / time_total)) def score_hard(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]: """ Takes in two (latitude, longitude) pairs and produces an int score. Score is in the (inclusive) range [0, 5000] Higher scores are closer. Scoring is much more punishing than standard Returns (score, distance in km) """ dist_km = haversine.haversine(target, guess) return max(0, 5000 - int(dist_km * 10)), dist_km def score_nightmare(target: Tuple[float, float], guess: Tuple[float, float]) -> Tuple[int, float]: """ Takes in two (latitude, longitude) pairs and produces an int score. Score is in the range (-inf, 5000] Higher scores are closer. Scoring is much, MUCH more punishing than standard Returns (score, distance in km) """ dist_km = haversine.haversine(target, guess) return 5000 - int(dist_km * 1000), dist_km