Something went wrong. Try again.
A 5e storytelling engine with an LLM DM
Something went wrong. Try again.
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195"""Tests for dice notation parsing and rolling."""
import pytest
from storied.dice import RollResult, parse_notation, roll
class TestParseNotation: """Tests for parse_notation function."""
def test_simple_die(self): result = parse_notation("1d20") assert result.count == 1 assert result.sides == 20 assert result.modifier == 0 assert result.keep_highest is None assert result.keep_lowest is None
def test_multiple_dice(self): result = parse_notation("3d6") assert result.count == 3 assert result.sides == 6 assert result.modifier == 0
def test_positive_modifier(self): result = parse_notation("2d6+3") assert result.count == 2 assert result.sides == 6 assert result.modifier == 3
def test_negative_modifier(self): result = parse_notation("1d20-2") assert result.count == 1 assert result.sides == 20 assert result.modifier == -2
def test_keep_highest(self): result = parse_notation("4d6kh3") assert result.count == 4 assert result.sides == 6 assert result.keep_highest == 3 assert result.keep_lowest is None
def test_keep_lowest(self): result = parse_notation("2d20kl1") assert result.count == 2 assert result.sides == 20 assert result.keep_lowest == 1 assert result.keep_highest is None
def test_advantage(self): result = parse_notation("2d20kh1") assert result.count == 2 assert result.sides == 20 assert result.keep_highest == 1
def test_disadvantage(self): result = parse_notation("2d20kl1") assert result.count == 2 assert result.sides == 20 assert result.keep_lowest == 1
def test_keep_with_modifier(self): result = parse_notation("4d6kh3+2") assert result.count == 4 assert result.sides == 6 assert result.keep_highest == 3 assert result.modifier == 2
def test_case_insensitive(self): result = parse_notation("2D20KH1") assert result.count == 2 assert result.sides == 20 assert result.keep_highest == 1
def test_whitespace_tolerance(self): result = parse_notation(" 2d6 + 3 ") assert result.count == 2 assert result.sides == 6 assert result.modifier == 3
def test_invalid_notation_raises(self): with pytest.raises(ValueError, match="Invalid dice notation"): parse_notation("not a dice roll")
def test_empty_string_raises(self): with pytest.raises(ValueError, match="Invalid dice notation"): parse_notation("")
class TestRoll: """Tests for roll function."""
def test_roll_returns_result(self): result = roll("1d20") assert isinstance(result, RollResult) assert result.notation == "1d20" assert len(result.rolls) == 1 assert 1 <= result.rolls[0] <= 20 assert result.total == result.rolls[0]
def test_roll_multiple_dice(self): result = roll("3d6") assert len(result.rolls) == 3 assert all(1 <= r <= 6 for r in result.rolls) assert result.total == sum(result.rolls)
def test_roll_with_modifier(self): result = roll("1d20+5") assert result.modifier == 5 assert result.total == result.rolls[0] + 5
def test_roll_with_negative_modifier(self): result = roll("1d20-3") assert result.modifier == -3 assert result.total == result.rolls[0] - 3
def test_roll_keep_highest(self): result = roll("4d6kh3") assert len(result.rolls) == 4 assert len(result.kept) == 3 # Kept should be the 3 highest sorted_rolls = sorted(result.rolls, reverse=True) assert sorted(result.kept, reverse=True) == sorted_rolls[:3] assert result.total == sum(result.kept)
def test_roll_keep_lowest(self): result = roll("2d20kl1") assert len(result.rolls) == 2 assert len(result.kept) == 1 assert result.kept[0] == min(result.rolls) assert result.total == result.kept[0]
def test_roll_keep_with_modifier(self): result = roll("4d6kh3+2") assert len(result.kept) == 3 assert result.modifier == 2 assert result.total == sum(result.kept) + 2
def test_roll_result_dict(self): result = roll("2d6+3") d = result.to_dict() assert d["notation"] == "2d6+3" assert "rolls" in d assert d["modifier"] == 3 assert "total" in d
def test_deterministic_with_seed(self): # Roll with same seed should give same results result1 = roll("3d6", seed=42) result2 = roll("3d6", seed=42) assert result1.rolls == result2.rolls assert result1.total == result2.total
class TestCompoundNotation: """Tests for compound dice notation (e.g., 2d8+1d6)."""
def test_two_dice_groups(self): result = roll("2d8+1d6", seed=42) assert len(result.rolls) == 3 # 2 d8s + 1 d6 assert result.total == sum(result.rolls)
def test_three_dice_groups(self): result = roll("1d8+1d6+1d4", seed=42) assert len(result.rolls) == 3 assert result.total == sum(result.rolls)
def test_compound_with_modifier(self): result = roll("2d6+1d4+2", seed=42) assert len(result.rolls) == 3 # 2 d6s + 1 d4 assert result.modifier == 2 assert result.total == sum(result.rolls) + 2
def test_subtraction_dice_group(self): result = roll("2d6-1d4", seed=42) # First two are d6s, last is d4 d6_total = sum(result.rolls[:2]) d4_total = result.rolls[2] assert result.total == d6_total - d4_total
def test_chaos_bolt_notation(self): # Chaos Bolt does 2d8 + 1d6 damage result = roll("2d8+1d6", seed=123) assert len(result.rolls) == 3 assert all(1 <= r <= 8 for r in result.rolls[:2]) assert 1 <= result.rolls[2] <= 6
def test_complex_compound(self): # e.g., 1d6+1d4+1d8+3 result = roll("1d6+1d4+1d8+3", seed=42) assert len(result.rolls) == 3 assert result.modifier == 3 assert result.total == sum(result.rolls) + 3