import math import time import datetime # time since last ping before deactivating/deleting BIKE_TIMEOUT = 60000 # 3 minutes endtime = datetime.datetime(2024, 11, 16, hour=18, minute=45) # data structures class Point: def __init__(self, lat, long): self.longitude = long self.latitude = lat def toJSON(self): json_dict = {"longitude": self.longitude, "latitude": self.latitude} return json_dict def setCoords(self, lat, long): self.longitude = long self.latitude = lat def move(self, d_lat, d_long): self.longitude += d_long self.latitude += d_lat def __str__(self): return f"{self.longitude},{self.latitude}" def __repr__(self): return f"{self.longitude},{self.latitude}" def distanceTo(self, pt): # distance between points in miles lat1 = self.latitude lon1 = self.longitude lat2 = pt.latitude lon2 = pt.longitude R = 3958.8 # Radius of the earth lat_d = math.radians(lat2 - lat1) lon_d = math.radians(lon2 - lon1) a = math.sin(lat_d / 2) * math.sin(lat_d / 2) + math.cos( math.radians(lat1) ) * math.cos(math.radians(lat2)) * math.sin(lon_d / 2) * math.sin(lon_d / 2) c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) d = R * c # Distance in mi return d class Clue(Point): def __init__(self, lat, long, name, info, status, assigned_team = 0, required=False): self.longitude = long self.latitude = lat self.name = name self.info = info self.status = status # UNVISITED | VISITED | DISABLED | ASSIGNED self.assigned_team = assigned_team # 0 = to be assigned algorithmically self.required = required def visit(self): self.status = "VISITED" def unvisit(self): self.status = "UNVISITED" def toggle_enable(self): if self.status == "UNVISITED" or self.status == "ASSIGNED": self.status = "DISABLED" elif self.status == "DISABLED": self.status = "UNVISITED" def toggle_required(self): self.required = False if self.required else True def set_team(self, team): self.assigned_team = team def toJSON(self): json_dict = {'longitude' : self.longitude, 'latitude' : self.latitude, 'clue_name' : self.name.replace('"', "'"), 'clue_info' : self.info.replace('"', "'"), 'clue_status' : self.status, 'assigned_team' : self.assigned_team, 'clue_required' : self.required} return json_dict class Bike(Point): def __init__(self, lat, long, name): self.longitude = long self.latitude = lat self.name = name self.deadline = endtime self.last_contact = time.time() self.target_clue = None self.time_modifier = 1 # factor by which to modulate expected arrival time self.cluster = [] # list of clues this bike team is responsible for self.status = "DISABLED" # DISABLED | ACTIVE | INACTIVE self.clue_assignment_time = 0 # when clue was assigned, so that upon clue visitation we can determine speed def setTarget(self, clue_name): self.target_name = clue_name def setCluster(self, clue_cluster): self.cluster = clue_cluster self.updateTarget() def setDeadline(self, new_deadline): self.deadline = new_deadline def timeTilDeadline(self): return (self.deadline - datetime.datetime.now()).total_seconds() def updateTarget(self): self.clue_assignment_time = time.time() if len(self.cluster) <= 0: self.target_clue = None self.status = "INACTIVE" else: self.status = "ACTIVE" self.target_clue = self.cluster[0] def visitTarget(self): print("clue visit time: ", time.time() - self.clue_assignment_time) self.target_clue.visit() self.cluster.pop(0) self.updateTarget() while len(self.cluster) > 0 and self.cluster[0].status == "VISITED": self.cluster.pop(0) # skip next node if it has been somehow visited self.updateTarget() def ping(self): self.status = "ACTIVE" self.last_contact = time.time() def disable(self): self.status = "DISABLED" self.target = None def enable(self): self.status = "INACTIVE" self.target = None def tripTime(self): return datetime.datetime.now() - self.deadline def toJSON(self): json_dict = { "longitude": self.longitude, "latitude": self.latitude, "time_since_last_contact": time.time() - self.last_contact, "team_deadline" : str(self.deadline), "team_name": self.name, "team_status": self.status, "target_clue": self.target_clue.name if self.target_clue else "N/A", } return json_dict