def repair_fleet(input_fleet): """ Have logistics ships do their job and repair other ships in the fleet """ logistics = logi_subfleet(input_fleet) logi_shield = logistics[0] logi_armor = logistics[1] if (logi_shield == []) and (logi_armor == []): return input_fleet damaged_shield = [] # this has a slight bug that ships can rep themselves # and a ship might get over repped, but that's actually intended # shield first for x in range(len(input_fleet)): if input_fleet[x].attributes.shield != input_fleet[x].schema.shield: damaged_shield.append(x) if damaged_shield != []: for ship in logi_shield: rep_target = random.choice(damaged_shield) input_fleet[rep_target] = Ship( input_fleet[rep_target].schema, ShipAttributes( min(input_fleet[rep_target].schema.shield, (input_fleet[rep_target].attributes.shield + ship.schema.buffs.remote_shield_repair)), input_fleet[rep_target].attributes.armor, input_fleet[rep_target].attributes.hull, ), input_fleet[rep_target].debuffs, ) damaged_armor = [] #armor second for x in range(len(input_fleet)): if input_fleet[x].attributes.armor != input_fleet[x].schema.armor: damaged_armor.append(int(x)) if damaged_armor != []: for ship in logi_armor: rep_target = random.choice(damaged_armor) input_fleet[rep_target] = Ship( input_fleet[rep_target].schema, ShipAttributes( input_fleet[rep_target].attributes.shield, min(input_fleet[rep_target].schema.armor, (input_fleet[rep_target].attributes.armor + ship.schema.buffs.remote_armor_repair)), input_fleet[rep_target].attributes.hull, ), input_fleet[rep_target].debuffs, ) return input_fleet
def ship_attack(attacker_weapon, attacker_debuffs, victim_ship): """ Do a ship attack. Apply the attacker's schema onto the victim_ship as an attack and return a new Ship object as the result. """ if not is_ship_alive(victim_ship): # save us some time, it could be the same dead ship. return victim_ship if victim_ship.schema.is_structure: # strucutres should not be attacked during fleet fights # structure damage and destruction is another mechanic outside of fleet engagements raise ValueError( "Battle.ship_attack() encountered a structure as a victim_ship") debuffs = grab_debuffs(attacker_weapon, victim_ship) if attacker_weapon['firepower'] <= 0: # no weapons: damage doesn't need to be calculated, but debuffs do return Ship( victim_ship.schema, ShipAttributes( victim_ship.attributes.shield, victim_ship.attributes.armor, victim_ship.attributes.hull, ), debuffs, ) damage = true_damage(attacker_weapon['firepower'], attacker_weapon['weapon_size'], victim_ship.schema.size, attacker_debuffs, victim_ship.debuffs) if damage <= 0: #if damage modfiers change damage to 0 then victim takes no damage return Ship( victim_ship.schema, ShipAttributes( victim_ship.attributes.shield, victim_ship.attributes.armor, victim_ship.attributes.hull, ), debuffs, ) shield = victim_ship.attributes.shield - damage armor = victim_ship.attributes.armor + min(shield, 0) hull = victim_ship.attributes.hull + min(armor, 0) return Ship( victim_ship.schema, ShipAttributes(max(0, shield), max(0, armor), max(0, hull)), debuffs, )
def test_is_alive(self): library = ShipLibraryMock() schema1 = library.get_ship_schemata('ship1') ship1 = Ship(schema1, ShipAttributes(10, 10, 100)) ship2 = Ship(schema1, ShipAttributes(10, 10, 0)) ship3 = Ship(schema1, ShipAttributes(10, 10, -1)) # lolwut self.assertTrue(battle.is_ship_alive(ship1)) self.assertFalse(battle.is_ship_alive(ship2)) self.assertFalse(battle.is_ship_alive(ship3))
def test_expand_fleet(self): library = ShipLibraryMock() schema = library.get_ship_schemata('ship1') ship_count = {"ship1": 5} answer = [ Ship(schema, ShipAttributes(10, 10, 100)), Ship(schema, ShipAttributes(10, 10, 100)), Ship(schema, ShipAttributes(10, 10, 100)), Ship(schema, ShipAttributes(10, 10, 100)), Ship(schema, ShipAttributes(10, 10, 100)), ] result = battle.expand_fleet(ship_count, library) self.assertEqual(answer, result.ships)
def prune_fleet(attack_result): """ Prune an AttackResult of dead ships and restore shields/armor. Returns the pruned fleet and a count of ships. """ fleet = [] count = {} damage_taken = 0 for ship in attack_result.damaged_fleet: if not ship.attributes.hull > 0: continue fleet.append( Ship( ship.schema, ShipAttributes( min(ship.schema.shield, (ship.attributes.shield + ship.schema.buffs.local_shield_repair)), min(ship.schema.armor, (ship.attributes.armor + ship.schema.buffs.local_armor_repair)), ship.attributes.hull, ), ship.debuffs, )) count[ship.schema.name] = count.get(ship.schema.name, 0) + 1 return Fleet(fleet, count)
def test_local_rep(self): random.seed(0) attacker = { "local_rep_test": 10, } defender = { "local_rep_test": 20, } rounds = 6 library = ShipLibraryMock() battle_instance = Battle(attacker, defender, rounds, library) schema_rep = library.get_ship_schemata('local_rep_test') attack_result = AttackResult([], [ Ship(schema_rep, ShipAttributes(100, 100, 100)), Ship(schema_rep, ShipAttributes(0, 0, 100)), Ship(schema_rep, ShipAttributes(10, 10, 0)), Ship(schema_rep, ShipAttributes(10, 10, 100)), ], 0, 0) expected_fleet = [ Ship(schema_rep, ShipAttributes(100, 100, 100)), Ship(schema_rep, ShipAttributes(5, 5, 100)), Ship(schema_rep, ShipAttributes(15, 15, 100)), ] result = battle.prune_fleet(attack_result) self.assertEqual(result.ships, expected_fleet)
def test_repair_fleet_does_not_scramble(self): random.seed(0) library = ShipLibraryMock() schema_remote = library.get_ship_schemata('remote_rep_test') tattered_fleet = [ Ship(schema_remote, ShipAttributes(0, 0, 100)), Ship(schema_remote, ShipAttributes(10, 10, 10)), Ship(schema_remote, ShipAttributes(100, 100, 100)), Ship(schema_remote, ShipAttributes(10, 10, 0)), Ship(schema_remote, ShipAttributes(99, 99, 100)), ] expected_fleet = [ Ship(schema_remote, ShipAttributes(10, 0, 100)), Ship(schema_remote, ShipAttributes(10, 20, 10)), Ship(schema_remote, ShipAttributes(100, 100, 100)), Ship(schema_remote, ShipAttributes(20, 30, 0)), Ship(schema_remote, ShipAttributes(100, 100, 100)), ] result = battle.repair_fleet(tattered_fleet) self.assertEqual(result, expected_fleet)
def test_fleet_attack(self): # Test the result of the attacking fleet firing once on the defenders. # Check that the damage on the defenders is accurate to only being # fired on once. attacker = { "ship1": 5, } defender = { "ship1": 4, } rounds = 6 library = ShipLibraryMock() battle_instance = Battle(attacker, defender, rounds, library, calculate=False) random.seed(1) result = battle.fleet_attack( battle_instance.attacker_fleet, battle_instance.defender_fleet, 0 # round number ) self.assertEqual(result.hits_taken, 5) self.assertEqual(result.damage_taken, 250) schema1 = library.get_ship_schemata('ship1') result = battle.prune_fleet(result) self.assertEqual(result.ships, [ Ship(schema1, ShipAttributes(10, 0, 20)), Ship(schema1, ShipAttributes(10, 0, 70)), Ship(schema1, ShipAttributes(10, 0, 70)), Ship(schema1, ShipAttributes(10, 0, 70)), ]) self.assertEqual(result.ship_count, { 'ship1': 4, })
def expand_fleet(ship_count, library): # for the listing of numbers of ship we need to expand to each ship # having it's own value for shield, armor, and hull # TO DO: Make sure fleet when expanded is ordered by size # From smallest to largest to make explode chance and # shield bounce effects work out properly. ships = [] for ship_type in ship_count: schema = library.get_ship_schemata(ship_type) ships.extend([ Ship( schema, # it's just a pointer... ShipAttributes(schema.shield, schema.armor, schema.hull), ) for i in range(ship_count[ship_type]) ]) return Fleet(ships, ship_count)
def test_fleet_attack_empty(self): library = ShipLibraryMock() schema1 = library.get_ship_schemata('ship1') empty_fleet = battle.expand_fleet({}, library) solo_fleet = battle.expand_fleet({'ship1': 1}, library) # attacking nothing will have no rounds. result = battle.fleet_attack(empty_fleet, empty_fleet, 0) # 0 is the round number self.assertEqual(result.damaged_fleet, empty_fleet.ships) # attacking nothing will have no rounds (for now?). result = battle.fleet_attack(solo_fleet, empty_fleet, 1) # 0 is the round number self.assertEqual(result.damaged_fleet, empty_fleet.ships) # nothing attacking a fleet will still result in a round (for now?). result = battle.fleet_attack(empty_fleet, solo_fleet, 2) # 0 is the round number self.assertEqual(result.damaged_fleet, [ Ship(schema1, ShipAttributes(10, 10, 100)), ])
def test_ship_attack(self): library = ShipLibraryMock() schema1 = library.get_ship_schemata('ship1') schema2 = library.get_ship_schemata('ship2') schema3 = library.get_ship_schemata('ship3') ship1 = Ship(schema1, ShipAttributes(10, 10, 100)) ship2 = Ship(schema2, ShipAttributes(100, 100, 100)) ship3 = Ship(schema3, ShipAttributes(100, 100, 100)) random.seed(1) #ship_attack(attacker, victim) ship1_1 = battle.ship_attack(ship2.schema.weapons[0], ship2.debuffs, ship1) self.assertEqual(ship1_1, Ship(schema1, ShipAttributes(0, 0, 0))) ship2_1 = battle.ship_attack(ship1.schema.weapons[0], ship1.debuffs, ship2) self.assertEqual(ship2_1, Ship(schema2, ShipAttributes(50, 100, 100))) ship2_2 = battle.ship_attack(ship1.schema.weapons[0], ship1.debuffs, ship2_1) self.assertEqual(ship2_2, Ship(schema2, ShipAttributes(0, 100, 100)))
def test_prune_fleet(self): library = ShipLibraryMock() schema1 = library.get_ship_schemata('ship1') schema2 = library.get_ship_schemata('ship2') schema3 = library.get_ship_schemata('ship3') attack_result = AttackResult( [], [ Ship(schema1, ShipAttributes(10, 5, 100)), Ship(schema1, ShipAttributes(0, 10, 100)), Ship(schema2, ShipAttributes(10, 10, 0)), # how is this possible? Ship(schema2, ShipAttributes(10, 10, 100)), Ship(schema2, ShipAttributes(0, 0, 10)), Ship(schema3, ShipAttributes(10, 0, 10)), ], 0, 0) expected_fleet = [ Ship(schema1, ShipAttributes(10, 5, 100)), Ship(schema1, ShipAttributes(10, 10, 100)), Ship(schema2, ShipAttributes(100, 10, 100)), Ship(schema2, ShipAttributes(100, 0, 10)), Ship(schema3, ShipAttributes(0, 0, 10)), # balance is restored. ] expected_count = { 'ship1': 2, 'ship2': 2, 'ship3': 1, } result = battle.prune_fleet(attack_result) self.assertEqual(result.ships, expected_fleet) self.assertEqual(result.ship_count, expected_count)