def unit_attack_ranged(contubernium: BattleContuberniumInTurn, target_contubernium: BattleContuberniumInTurn): for soldier in contubernium.battlesoldierinturn_set.all(): target_soldier = random.choice( target_contubernium.battlesoldierinturn_set.all()[:]) while (random.random() < 0.3 * soldier.attack_chance_multiplier()): if target_soldier.wound_status < BattleSoldierInTurn.DEAD: target_soldier.take_hit() target_soldier.save() contubernium.ammo_remaining -= 1 if contubernium.ammo_remaining < 0: contubernium.ammo_remaining = 0 contubernium.save()
def optimistic_move_desire_formulation( battle_contubernium_in_turn: BattleContuberniumInTurn, target_distance_function): def tile_availability_test(coords: Coordinates): return True if target_distance_function(battle_contubernium_in_turn.coordinates()) > 0: path = find_path(battle_contubernium_in_turn, target_distance_function, tile_availability_test) if len(path) > 1: battle_contubernium_in_turn.desires_pos = True battle_contubernium_in_turn.desired_x_pos = path[1].x battle_contubernium_in_turn.desired_z_pos = path[1].z battle_contubernium_in_turn.save()
def safe_move(battle_contubernium_in_turn: BattleContuberniumInTurn, target_distance_function): turn = battle_contubernium_in_turn.battle_turn def tile_availability_test(coords: Coordinates): return True if turn.get_contubernium_in_position( coords) is None else False if target_distance_function(battle_contubernium_in_turn.coordinates()) > 0: path = find_path(battle_contubernium_in_turn, target_distance_function, tile_availability_test) if len(path) > 1: battle_contubernium_in_turn.moved_this_turn = True battle_contubernium_in_turn.x_pos = path[1].x battle_contubernium_in_turn.z_pos = path[1].z #TODO WARNING: HORRIBLE HACK STARTS HERE #(to avoid unique constraint errors when contubs overlap for some reason) with transaction.atomic(): try: battle_contubernium_in_turn.save() except django.db.utils.IntegrityError as e: pass
def find_path(battle_contubernium_in_turn: BattleContuberniumInTurn, target_distance_function, tile_availability_test) -> list: starting_coordinates = battle_contubernium_in_turn.coordinates() if target_distance_function(starting_coordinates) <= 0: return True closed_set = set() open_set = set() open_set.add(starting_coordinates) came_from = {} g_score = {} g_score[starting_coordinates] = 0 f_score = {} f_score[starting_coordinates] = target_distance_function( starting_coordinates) while open_set: minel = None for el in open_set: if minel is None or f_score[el] < f_score[minel]: minel = el current = minel open_set.remove(minel) if target_distance_function(current) <= 0: # RECONSTRUCT # print("REACHED GOAL, backtracing") total_path = [current] while current in came_from.keys(): current = came_from[current] if current != starting_coordinates and not tile_availability_test( current): return [] total_path.append(current) # print("Backtrace {}".format(current)) total_path.reverse() return total_path closed_set.add(current) for neighbor in coordinate_neighbours(current): if neighbor in closed_set: # print("Already closed: {}".format(neighbor)) continue tentative_g_score = g_score[current] + euclidean_distance( current, neighbor) if not tile_availability_test(neighbor): tentative_g_score += 20 # print("Considering {} with score {}".format(neighbor, tentative_g_score)) if neighbor not in open_set: # print("Adding to open set") open_set.add(neighbor) elif tentative_g_score >= g_score[neighbor]: # print("Better value in g_score map") continue # print("Found better path") came_from[neighbor] = current g_score[neighbor] = tentative_g_score f_score[neighbor] = g_score[neighbor] + target_distance_function( neighbor) return []
def get_target_distance_function( battle_contubernium_in_turn: BattleContuberniumInTurn): order = battle_contubernium_in_turn.battle_unit_in_turn.battle_unit.get_order( ) enemy_contubernia = BattleContuberniumInTurn.objects.filter( battle_turn=battle_contubernium_in_turn.battle_turn).exclude( battle_contubernium__battle_unit__battle_side= battle_contubernium_in_turn.battle_contubernium.battle_unit. battle_side) if order: if order.what == Order.STAND: return None if order.what == Order.MOVE: unit_target = order.target_location_coordinates() target = Coordinates( x=(unit_target.x + battle_contubernium_in_turn. battle_contubernium.x_offset_to_unit), z=(unit_target.z + battle_contubernium_in_turn. battle_contubernium.z_offset_to_unit)) def result(coords: Coordinates): return euclidean_distance(coords, target) return result if order.what == Order.FLEE: original_enemy_distance = closest_in_set( battle_contubernium_in_turn.coordinates(), enemy_contubernia)[1] def result(coords: Coordinates): closest, distance = closest_in_set(coords, enemy_contubernia) return (original_enemy_distance + 10) - distance if distance is not None else 0 return result if order.what == Order.CHARGE: def result(coords: Coordinates): closest, distance = closest_in_set(coords, enemy_contubernia) return distance if distance is not None and distance >= 2 else 0 return result if order.what == Order.ADVANCE_IN_FORMATION: z_direction = -1 if battle_contubernium_in_turn.battle_contubernium.battle_unit.battle_side.z else 1 z_offset = battle_contubernium_in_turn.battle_turn.num * z_direction target = Coordinates(x=battle_contubernium_in_turn. battle_contubernium.starting_x_pos, z=battle_contubernium_in_turn. battle_contubernium.starting_z_pos + z_offset) def result(coords: Coordinates): return euclidean_distance(coords, target) return result if order.what == Order.RANGED_ATTACK: pass
def grant_position_desire(desire_getter: BattleContuberniumInTurn): desire_getter.desires_pos = False desire_getter.moved_this_turn = True desire_getter.x_pos = desire_getter.desired_x_pos desire_getter.z_pos = desire_getter.desired_z_pos desire_getter.save()
def grant_position_swap(contubernium1: BattleContuberniumInTurn, contubernium2: BattleContuberniumInTurn): contubernium1.x_pos = 31337 contubernium1.save() grant_position_desire(contubernium2) grant_position_desire(contubernium1)
def get_target_distance_function( battle_contubernium_in_turn: BattleContuberniumInTurn): order = battle_contubernium_in_turn.battle_unit_in_turn.battle_unit.get_order( ) enemy_contubernia = BattleContuberniumInTurn.objects.filter( battle_turn=battle_contubernium_in_turn.battle_turn).exclude( battle_contubernium__battle_unit__battle_side= battle_contubernium_in_turn.battle_contubernium.battle_unit. battle_side) if order: if order.what == Order.STAND or ( order.what == Order.RANGED_AND_STAND and battle_contubernium_in_turn.ammo_remaining == 0): return None if order.what == Order.MOVE: unit_target = order.target_location_coordinates() target = Coordinates( x=(unit_target.x + battle_contubernium_in_turn. battle_contubernium.x_offset_to_unit), z=(unit_target.z + battle_contubernium_in_turn. battle_contubernium.z_offset_to_unit)) def result(coords: Coordinates): return euclidean_distance(coords, target) return result if order.what == Order.FLEE or ( order.what == Order.RANGED_AND_FLEE and battle_contubernium_in_turn.ammo_remaining == 0): original_enemy_distance = closest_in_set( battle_contubernium_in_turn.coordinates(), enemy_contubernia)[1] def result(coords: Coordinates): closest, distance = closest_in_set(coords, enemy_contubernia) return (original_enemy_distance + 10) - distance if distance is not None else 0 return result if order.what == Order.CHARGE or ( order.what == Order.RANGED_AND_CHARGE and battle_contubernium_in_turn.ammo_remaining == 0): def result(coords: Coordinates): closest, distance = closest_in_set(coords, enemy_contubernia) return distance if distance is not None and distance >= 2 else 0 return result if order.what == Order.ADVANCE_IN_FORMATION: z_direction = -1 if battle_contubernium_in_turn.battle_contubernium.battle_unit.battle_side.z else 1 z_offset = battle_contubernium_in_turn.battle_turn.num * z_direction target = Coordinates(x=battle_contubernium_in_turn. battle_contubernium.starting_x_pos, z=battle_contubernium_in_turn. battle_contubernium.starting_z_pos + z_offset) def result(coords: Coordinates): return euclidean_distance(coords, target) return result if order.what in (Order.RANGED_AND_CHARGE, Order.RANGED_AND_FLEE, Order.RANGED_AND_STAND ) and battle_contubernium_in_turn.ammo_remaining > 0: def result(coords: Coordinates): closest, distance = closest_in_set(coords, enemy_contubernia) shot_range = battle_contubernium_in_turn.battle_unit_in_turn.battle_unit.world_unit.shot_range( ) if distance > shot_range: return distance - shot_range if distance < shot_range - 1: return shot_range - distance return 0 return result if order.what == Order.STAND_AND_DISTANCE: def result(coords: Coordinates): closest, enemy_distance = closest_in_set( coords, enemy_contubernia) shot_range = battle_contubernium_in_turn.battle_unit_in_turn.battle_unit.world_unit.shot_range( ) if shot_range == 0: min_enemy_distance = 7 else: min_enemy_distance = shot_range - 1 if enemy_distance > min_enemy_distance: return 0 else: return min_enemy_distance - enemy_distance return result