Rain, storm, snow, and fog now have atmospheric ambient messages. Clear and cloudy conditions return empty list. Messages are evocative and lowercase, ready to be mixed with zone-specific ambience.
260 lines
8.7 KiB
Python
260 lines
8.7 KiB
Python
"""Tests for weather system."""
|
|
|
|
import random
|
|
|
|
from mudlib.weather import (
|
|
WeatherCondition,
|
|
WeatherState,
|
|
advance_weather,
|
|
get_weather_description,
|
|
)
|
|
|
|
|
|
def test_weather_condition_enum():
|
|
assert WeatherCondition.clear.value == "clear"
|
|
assert WeatherCondition.cloudy.value == "cloudy"
|
|
assert WeatherCondition.rain.value == "rain"
|
|
assert WeatherCondition.storm.value == "storm"
|
|
assert WeatherCondition.snow.value == "snow"
|
|
assert WeatherCondition.fog.value == "fog"
|
|
|
|
|
|
def test_weather_state_dataclass():
|
|
state = WeatherState(condition=WeatherCondition.rain, intensity=0.5)
|
|
assert state.condition == WeatherCondition.rain
|
|
assert state.intensity == 0.5
|
|
|
|
|
|
def test_get_weather_description_returns_non_empty():
|
|
state = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
description = get_weather_description(state)
|
|
assert isinstance(description, str)
|
|
assert len(description) > 0
|
|
|
|
|
|
def test_get_weather_description_varies_by_condition():
|
|
clear = get_weather_description(
|
|
WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
)
|
|
rain = get_weather_description(
|
|
WeatherState(condition=WeatherCondition.rain, intensity=0.5)
|
|
)
|
|
snow = get_weather_description(
|
|
WeatherState(condition=WeatherCondition.snow, intensity=0.5)
|
|
)
|
|
|
|
# Different conditions should produce different descriptions
|
|
assert clear != rain
|
|
assert rain != snow
|
|
assert clear != snow
|
|
|
|
|
|
def test_get_weather_description_varies_by_intensity():
|
|
light_rain = get_weather_description(
|
|
WeatherState(condition=WeatherCondition.rain, intensity=0.2)
|
|
)
|
|
heavy_rain = get_weather_description(
|
|
WeatherState(condition=WeatherCondition.rain, intensity=0.9)
|
|
)
|
|
|
|
# Different intensities should produce different descriptions
|
|
assert light_rain != heavy_rain
|
|
|
|
|
|
def test_advance_weather_returns_new_state():
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
rng = random.Random(42)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
|
|
assert isinstance(new_state, WeatherState)
|
|
assert isinstance(new_state.condition, WeatherCondition)
|
|
assert 0.0 <= new_state.intensity <= 1.0
|
|
|
|
|
|
def test_advance_weather_is_deterministic_with_seed():
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
|
|
rng1 = random.Random(42)
|
|
state1 = advance_weather(current, season="summer", rng=rng1)
|
|
|
|
rng2 = random.Random(42)
|
|
state2 = advance_weather(current, season="summer", rng=rng2)
|
|
|
|
assert state1.condition == state2.condition
|
|
assert state1.intensity == state2.intensity
|
|
|
|
|
|
def test_advance_weather_transitions_naturally():
|
|
# Clear can become cloudy
|
|
rng = random.Random(42)
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
if new_state.condition == WeatherCondition.cloudy:
|
|
break
|
|
else:
|
|
raise AssertionError("clear never transitioned to cloudy in 100 iterations")
|
|
|
|
# Cloudy can become rain or clear
|
|
rng = random.Random(43)
|
|
found_rain = False
|
|
found_clear = False
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
if new_state.condition == WeatherCondition.rain:
|
|
found_rain = True
|
|
if new_state.condition == WeatherCondition.clear:
|
|
found_clear = True
|
|
if found_rain and found_clear:
|
|
break
|
|
assert found_rain or found_clear
|
|
|
|
|
|
def test_storm_transitions_to_rain_or_cloudy():
|
|
# Storm should always transition away (doesn't last)
|
|
rng = random.Random(44)
|
|
found_non_storm = False
|
|
for _ in range(50):
|
|
current = WeatherState(condition=WeatherCondition.storm, intensity=0.8)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
if new_state.condition in (WeatherCondition.rain, WeatherCondition.cloudy):
|
|
found_non_storm = True
|
|
break
|
|
assert found_non_storm, "storm should transition to rain or cloudy"
|
|
|
|
|
|
def test_snow_only_in_winter_autumn():
|
|
# Snow in winter
|
|
rng = random.Random(45)
|
|
found_snow = False
|
|
for _ in range(200):
|
|
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
|
|
new_state = advance_weather(current, season="winter", rng=rng)
|
|
if new_state.condition == WeatherCondition.snow:
|
|
found_snow = True
|
|
break
|
|
assert found_snow, "snow should be possible in winter"
|
|
|
|
# Snow should be rare or impossible in summer
|
|
rng = random.Random(46)
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
# Should not produce snow in summer
|
|
assert new_state.condition != WeatherCondition.snow
|
|
|
|
|
|
def test_climate_temperate_default():
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
rng = random.Random(47)
|
|
|
|
# Should work without climate parameter (defaults to temperate)
|
|
new_state = advance_weather(current, season="summer", rng=rng)
|
|
assert isinstance(new_state, WeatherState)
|
|
|
|
|
|
def test_climate_arid_favors_clear():
|
|
# Arid should heavily favor clear weather
|
|
rng = random.Random(48)
|
|
clear_count = 0
|
|
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
new_state = advance_weather(current, season="summer", rng=rng, climate="arid")
|
|
if new_state.condition == WeatherCondition.clear:
|
|
clear_count += 1
|
|
|
|
# Arid should stay clear most of the time
|
|
assert clear_count > 70, f"arid should favor clear, got {clear_count}/100"
|
|
|
|
|
|
def test_climate_arid_no_snow():
|
|
# Arid should never produce snow
|
|
rng = random.Random(49)
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
|
|
new_state = advance_weather(current, season="winter", rng=rng, climate="arid")
|
|
assert new_state.condition != WeatherCondition.snow
|
|
|
|
|
|
def test_climate_arctic_favors_snow_fog_cloudy():
|
|
# Arctic should produce snow, fog, or cloudy frequently
|
|
rng = random.Random(50)
|
|
arctic_conditions = 0
|
|
|
|
for _ in range(100):
|
|
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
|
|
new_state = advance_weather(current, season="winter", rng=rng, climate="arctic")
|
|
if new_state.condition in (
|
|
WeatherCondition.snow,
|
|
WeatherCondition.fog,
|
|
WeatherCondition.cloudy,
|
|
):
|
|
arctic_conditions += 1
|
|
|
|
# Arctic should heavily favor snow/fog/cloudy
|
|
assert arctic_conditions > 70, (
|
|
f"arctic should favor snow/fog/cloudy, got {arctic_conditions}/100"
|
|
)
|
|
|
|
|
|
def test_advance_weather_accepts_all_seasons():
|
|
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
|
|
rng = random.Random(51)
|
|
|
|
for season in ["spring", "summer", "autumn", "winter"]:
|
|
new_state = advance_weather(current, season=season, rng=rng)
|
|
assert isinstance(new_state, WeatherState)
|
|
|
|
|
|
def test_get_weather_ambience_rain():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.rain)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) > 0
|
|
assert all(isinstance(msg, str) for msg in messages)
|
|
|
|
|
|
def test_get_weather_ambience_storm():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.storm)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) > 0
|
|
assert all(isinstance(msg, str) for msg in messages)
|
|
|
|
|
|
def test_get_weather_ambience_snow():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.snow)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) > 0
|
|
assert all(isinstance(msg, str) for msg in messages)
|
|
|
|
|
|
def test_get_weather_ambience_fog():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.fog)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) > 0
|
|
assert all(isinstance(msg, str) for msg in messages)
|
|
|
|
|
|
def test_get_weather_ambience_clear():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.clear)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) == 0
|
|
|
|
|
|
def test_get_weather_ambience_cloudy():
|
|
from mudlib.weather import get_weather_ambience
|
|
|
|
messages = get_weather_ambience(WeatherCondition.cloudy)
|
|
assert isinstance(messages, list)
|
|
assert len(messages) == 0
|