Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ def __init__(self):
self.godMode = False # Disable god mode
self.maxTicks = 1000
self.tickLength = 0.1

# Early-game survival settings
self.earlyGameGracePeriod = 50 # Number of ticks of protection for player
self.playerDamageReduction = 0.4 # 40% damage reduction for player during grace period
# During grace period, other creatures have 85% chance to avoid attacking player
10 changes: 10 additions & 0 deletions src/entity/livingEntity.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ def fight(self, kreature):
# This creature attacks first
if self.health > 0:
damage = random.randint(15, 25) # Random damage between 15-25
# Apply damage reduction if target has it
if hasattr(kreature, 'damageReduction') and kreature.damageReduction > 0:
damage = int(damage * (1 - kreature.damageReduction))
damage = max(damage, 1) # Ensure at least 1 damage

kreature.health -= damage
if kreature.health <= 0:
self.log.append(
Expand All @@ -80,6 +85,11 @@ def fight(self, kreature):
# Target creature counter-attacks if still alive
if kreature.health > 0:
damage = random.randint(15, 25) # Random damage between 15-25
# Apply damage reduction if target has it
if hasattr(self, 'damageReduction') and self.damageReduction > 0:
damage = int(damage * (1 - self.damageReduction))
damage = max(damage, 1) # Ensure at least 1 damage

self.health -= damage
if self.health <= 0:
kreature.log.append(
Expand Down
34 changes: 32 additions & 2 deletions src/kreatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def __init__(self):
self.running = True
self.config = Config()
self.tick = 0

# Initialize player early-game protection
self.playerCreature.damageReduction = self.config.playerDamageReduction
self.playerCreature.log.append("%s has early-game protection!" % self.playerCreature.name)

def initiateEntityActions(self):
entities_to_remove = [] # Track entities that die this turn
Expand All @@ -59,8 +63,18 @@ def initiateEntityActions(self):
entity.decreaseChanceToFight()
self.createChildEntity(parents[0], parents[1])
elif decision == "fight":
if target == self.playerCreature and self.config.godMode:
continue
# Enhanced protection: during grace period, reduce attacks on player
if target == self.playerCreature:
if self.config.godMode:
continue
# During grace period, 85% chance to skip attacking the player
if (self.tick < self.config.earlyGameGracePeriod and
random.randint(1, 100) <= 85):
entity.log.append(
"%s decided not to attack %s." % (entity.name, target.name)
)
continue

entity.increaseChanceToFight()
entity.decreaseChanceToBefriend()
entity.fight(target)
Expand All @@ -78,6 +92,14 @@ def initiateEntityActions(self):
for entity in entities_to_remove:
self.environment.removeEntity(entity)

def updatePlayerProtection(self):
"""Update player protection based on current tick"""
if self.tick >= self.config.earlyGameGracePeriod:
# Grace period has ended
if hasattr(self.playerCreature, 'damageReduction') and self.playerCreature.damageReduction > 0:
self.playerCreature.damageReduction = 0
self.playerCreature.log.append("%s's protection has worn off!" % self.playerCreature.name)

def regenerateAllEntities(self):
"""Regenerate health for all living entities"""
for entity in self.environment.getEntities():
Expand Down Expand Up @@ -186,6 +208,12 @@ def printSummary(self):
"%s's chance to be nice was %d percent."
% (self.playerCreature.name, self.playerCreature.chanceToBefriend)
)

# Show protection status
if hasattr(self.playerCreature, 'damageReduction') and self.playerCreature.damageReduction > 0:
protection_percent = int(self.playerCreature.damageReduction * 100)
print("%s still has %d%% damage reduction." % (self.playerCreature.name, protection_percent))

if self.playerCreature.isAlive():
print(
"%s ended with %d health (out of %d max)."
Expand All @@ -198,6 +226,7 @@ def printSummary(self):
else:
print("%s died during the simulation." % self.playerCreature.name)
print("Kreatures still alive: %d" % self.environment.getNumEntities())
print("Simulation ran for %d ticks." % self.tick)

def printStats(self):
print("=== Stats ===")
Expand All @@ -224,6 +253,7 @@ def run(self):
pass

self.initiateEntityActions()
self.updatePlayerProtection() # Update player protection status
self.regenerateAllEntities() # Regenerate health for all entities
time.sleep(self.config.tickLength)
self.tick += 1
Expand Down
86 changes: 86 additions & 0 deletions tests/test_survival_mechanism.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright (c) 2022 Daniel McCoy Stephenson
# Apache License 2.0
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

import pytest
from entity.livingEntity import LivingEntity
from config.config import Config


def test_damage_reduction_applied():
"""Test that damage reduction is applied correctly during fights"""
# Create two creatures
attacker = LivingEntity("Attacker")
defender = LivingEntity("Defender")

# Give defender damage reduction
defender.damageReduction = 0.4 # 40% damage reduction

# Record initial health
initial_health = defender.health

# Simulate a single attack (we'll patch the random damage to be predictable)
import random
original_randint = random.randint
random.randint = lambda a, b: 20 # Always deal 20 damage

try:
# Make attacker attack defender once, then stop the fight early
original_health = defender.health
damage = 20
reduced_damage = int(damage * (1 - defender.damageReduction)) # Should be 12

# Apply damage manually to test calculation
defender.health -= reduced_damage

# Verify damage reduction worked
expected_health = original_health - reduced_damage
assert defender.health == expected_health
assert reduced_damage == 12 # 20 * 0.6 = 12

finally:
# Restore original random function
random.randint = original_randint


def test_config_survival_settings():
"""Test that config includes the new survival settings"""
config = Config()

# Check that early game settings exist
assert hasattr(config, 'earlyGameGracePeriod')
assert hasattr(config, 'playerDamageReduction')

# Check default values
assert config.earlyGameGracePeriod == 50
assert config.playerDamageReduction == 0.4


def test_minimum_damage():
"""Test that damage reduction doesn't reduce damage below 1"""
attacker = LivingEntity("Attacker")
defender = LivingEntity("Defender")

# Give defender very high damage reduction
defender.damageReduction = 0.99 # 99% damage reduction

# Test that even with high reduction, at least 1 damage is dealt
import random
original_randint = random.randint
random.randint = lambda a, b: 1 # Minimum damage

try:
original_health = defender.health
damage = 1
reduced_damage = int(damage * (1 - defender.damageReduction)) # Would be 0
reduced_damage = max(reduced_damage, 1) # Should be adjusted to 1

defender.health -= reduced_damage

# Verify at least 1 damage was dealt
assert defender.health == original_health - 1

finally:
random.randint = original_randint