Created
March 26, 2017 19:15
-
-
Save prakol16/f0a306efd63a977a95c034b4b6e00ef1 to your computer and use it in GitHub Desktop.
For codegolf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<module type="PYTHON_MODULE" version="4"> | |
<component name="NewModuleRootManager"> | |
<content url="file://$MODULE_DIR$" /> | |
<orderEntry type="inheritedJdk" /> | |
<orderEntry type="sourceFolder" forTests="false" /> | |
</component> | |
<component name="TestRunnerService"> | |
<option name="PROJECT_TEST_RUNNER" value="Unittests" /> | |
</component> | |
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.5.1+ (/usr/bin/python3.5)" project-jdk-type="Python SDK" /> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectModuleManager"> | |
<modules> | |
<module fileurl="file://$PROJECT_DIR$/.idea/Darwin.iml" filepath="$PROJECT_DIR$/.idea/Darwin.iml" /> | |
</modules> | |
</component> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
DEFENSE_CONSTANT = 25 | |
MIN_DAMAGE = 0.25 | |
# Just for fun | |
# Generated at http://listofrandomnames.com/index.cfm | |
names = ["Ashanti","Jeanett","Charlyn","Micki","Dell","Dacia","Jenni","Cristi","Ethelene","Geraldine","Johnny", | |
"Arianna","Orville","Jarrett","Ute","Candis","Rhona","Beth","Hilaria","Ava","Kamala","Louetta","Twila", | |
"Kristle","Adrianne","Ouida","Gregorio","Kenna","David","Nona","Marshall","Melany","Berna","Vivienne", | |
"Genevieve","Jewel","Jennell","Sharell","Shelba","Darrell","Deedra","Belia","Betsey","Kimberely","Eveline", | |
"Erin","Allyson","Angele","Cordelia","Kacie"] | |
class Damager: | |
def __init__(self, starting_health=1.0, defense=0.0, attack=0.0): | |
self.health = starting_health | |
self.starting_health = self.health | |
self.defense = defense | |
self.attack = attack | |
def get_stats(self): | |
return [self.health, self.defense, self.attack] | |
def get_hit(self, damage): | |
self.health -= max(damage * (1 - self.defense / DEFENSE_CONSTANT), MIN_DAMAGE * damage) | |
return self.health <= 0 | |
def hit(self, other): | |
return other.get_hit(self.attack) | |
class Enemy(Damager): | |
ids = 0 | |
def __init__(self, blur=0, reproduction=1, **kwargs): | |
mutated_kwargs = {} | |
self.mutation_variation = 0.3 | |
caps = {"starting_health": 1.0, "attack": 0, "defense": 0} | |
for key in caps: | |
mutated_kwargs[key] = self.mutate_stat(kwargs[key] if key in kwargs else caps[key], cap=caps[key]) | |
assert "attack" in mutated_kwargs | |
super().__init__(**mutated_kwargs) | |
self.reproduction = self.mutate_stat(reproduction, cap=1.0) | |
self.blur = self.mutate_stat(blur, cap=0.0) | |
self.name = random.choice(names) | |
self.enemy_id = Enemy.ids | |
self.steps_survived = 0 | |
Enemy.ids += 1 | |
def blur_stat(self, x): | |
return max(x - random.random() * self.blur, 0) | |
def mutate_stat(self, x, cap): | |
return max(x + random.normalvariate(0, self.mutation_variation), cap) | |
def combine_stat(self, x, y): | |
return (x + y) / 2 | |
def get_stats(self): | |
stats = super().get_stats() | |
stats.append(self.reproduction) | |
return list(map(self.blur_stat, stats)) | |
def can_reproduce(self, num_enemies): | |
return random.random() < self.reproduction / num_enemies | |
def choose_mate(self, all_enemies, total_attack): | |
r = random.random() * total_attack | |
for e in all_enemies: | |
r -= e.attack | |
if r <= 0: | |
return e | |
return random.choice(all_enemies) | |
def reproduce_with(self, other): | |
return Enemy(starting_health=self.combine_stat(self.starting_health, other.starting_health), | |
defense=self.combine_stat(self.defense, other.defense), | |
attack=self.combine_stat(self.attack, other.attack), | |
blur=self.combine_stat(self.blur, other.blur), | |
reproduction=self.combine_stat(self.blur, other.blur)) | |
def __str__(self): | |
return "Enemy {id} {name} stats=[health={:.2f}, defense={:.2f}," \ | |
"attack={:.2f}, blur={blur:.2f}, repr={reproduction:.2f}]".format( | |
*super().get_stats(), id=self.enemy_id, name=self.name, blur=self.blur, reproduction=self.reproduction) | |
INITIAL_STATS = 10 | |
STARTING_ENEMIES = 5 | |
class Player(Damager): | |
def __init__(self, attack=10, defense=0, regeneration=0): | |
super().__init__(starting_health=100, attack=attack, defense=defense) | |
self.min_health = 100 | |
self.regeneration = regeneration | |
if attack + defense + regeneration > INITIAL_STATS: | |
raise ValueError("Sum of stats is bigger than {}".format(INITIAL_STATS)) | |
def get_attack(self, enemies, round_num, nuke_recharge): | |
print("\nRound num: {}; Nuke recharge {}\n".format(round_num, nuke_recharge)) | |
for ind, stat in enumerate(enemies): | |
print("Enemy {} with h {:.2f} d {:.2f} a {:.2f} r {:.2f}".format(ind, *stat)) | |
return list(map(int, input("What indices to attack? ").split())) | |
def restore(self): | |
if self.health < self.min_health: | |
self.min_health = self.health | |
self.health = self.starting_health | |
def get_health(self): | |
return self.health | |
def regenerate(self): | |
self.health += self.regeneration | |
if self.health > self.starting_health: | |
self.health = self.starting_health | |
NUKE_RECHARGE_TIME = 10 | |
ENEMIES_HIT_PER_TURN = 3 | |
class Controller: | |
def __init__(self, logger=print, pclass=Player): | |
self.player = pclass() | |
self.logger = logger | |
self.enemies_defeated = 0 | |
self.rounds = 0 | |
self.nuke_recharge = 0 | |
self.round_stats = {"starting_health": 0, "attack": 0, "defense": 0, "reproduction": 0, "blur": 0} | |
self.enemies = [Enemy() for _ in range(STARTING_ENEMIES)] | |
self.round_num_enemies = 0 | |
def kill_enemy(self, e): | |
self.round_stats["starting_health"] += e.starting_health * e.steps_survived | |
self.round_stats["attack"] += e.attack * e.steps_survived | |
self.round_stats["defense"] += e.defense * e.steps_survived | |
self.round_stats["reproduction"] += e.reproduction * e.steps_survived | |
self.round_stats["blur"] += e.blur * e.steps_survived | |
self.round_num_enemies += e.steps_survived | |
def attack_enemies(self, enemy_indices): | |
to_die = [] | |
for enemy_ind in enemy_indices: | |
enemy_ind %= len(self.enemies) | |
self.logger("Player chose to attack " + str(self.enemies[enemy_ind])) | |
if self.player.hit(self.enemies[enemy_ind]): | |
# Enemy killed, remove it | |
self.enemies_defeated += 1 | |
self.logger("Enemy killed! Total # of enemies defeated: " + str(self.enemies_defeated)) | |
to_die.append(enemy_ind) | |
self.kill_enemy(self.enemies[enemy_ind]) | |
self.enemies[:] = [enemy for (ind, enemy) in enumerate(self.enemies) if ind not in to_die] | |
def run_step(self): | |
for enemy in self.enemies: | |
self.logger(str(enemy) + " hit player") | |
enemy.steps_survived += 1 | |
if enemy.hit(self.player): | |
self.logger("Player died!") | |
return True | |
self.logger("Player has {:.2f} health left".format(self.player.get_health())) | |
enemy_stats = list(map(Enemy.get_stats, self.enemies)) | |
enemy_indices = self.player.get_attack(enemy_stats, self.rounds, self.nuke_recharge) | |
if len(enemy_indices) == ENEMIES_HIT_PER_TURN: | |
self.attack_enemies(enemy_indices) | |
elif self.nuke_recharge <= 0: | |
self.logger("Player chose to NUKE!") | |
self.attack_enemies(list(range(len(self.enemies)))) | |
self.nuke_recharge = NUKE_RECHARGE_TIME | |
else: | |
self.logger("Player tried to nuke, but still needs to wait {} turn(s)".format(self.nuke_recharge)) | |
if len(self.enemies) == 0: | |
self.rounds += 1 | |
self.logger("All enemies killed. Total # of rounds completed: " + str(self.rounds)) | |
self.player.restore() | |
self.create_enemies() | |
else: | |
self.reproduce_all() | |
random.shuffle(self.enemies) | |
self.nuke_recharge = max(0, self.nuke_recharge - 1) | |
self.player.regenerate() | |
self.logger("Regenerating, player has {} health left".format(self.player.health)) | |
self.logger("\n") | |
return False | |
def create_enemies(self): | |
average_round_stats = {} | |
for key in self.round_stats: | |
average_round_stats[key] = self.round_stats[key] / self.round_num_enemies | |
self.round_stats[key] = 0 | |
self.round_num_enemies = 0 | |
for _ in range(self.get_round_enemies()): | |
self.enemies.append(Enemy(**average_round_stats)) | |
def get_round_enemies(self): | |
return STARTING_ENEMIES * (self.rounds + 1) | |
def reproduce_all(self): | |
babies = [] | |
total_attack = sum(map(lambda x: x.attack, self.enemies)) | |
for enemy in self.enemies: | |
if enemy.can_reproduce(len(self.enemies)): | |
other = enemy.choose_mate(self.enemies, total_attack) | |
baby = enemy.reproduce_with(other) | |
if enemy == other: | |
self.logger(str(enemy) + " reproduced to create " + str(baby)) | |
else: | |
self.logger(str(enemy) + " and " + str(other) + " reproduced to create " + str(baby)) | |
babies.append(baby) | |
self.enemies.extend(babies) | |
class ExamplePlayer(Player): | |
def __init__(self): | |
super().__init__(attack=3, defense=0, regeneration=7) | |
def get_attack(self, enemies, round_num, nuke_recharge): | |
if self.get_health() < 50 and nuke_recharge == 0: | |
return [0] # Nuke it! | |
return [0, 1, 2] | |
def noop(*args, **kwargs): | |
pass | |
if __name__ == "__main__": | |
c = Controller(logger=print, pclass=ExamplePlayer) # Change Player to ExamplePlayer to run example player | |
while True: | |
if c.run_step(): | |
break | |
print("\n\n\n") | |
print("Survived {} rounds".format(c.rounds)) | |
print("Minimum health {}".format(c.player.min_health)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment