Compare commits
2 Commits
18224b1ea7
...
1c54f860fa
Author | SHA1 | Date |
---|---|---|
mat ess | 1c54f860fa | |
mat ess | 5f877c7d68 |
|
@ -6,3 +6,4 @@ __pycache__
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
.vscode
|
.vscode
|
||||||
.coverage
|
.coverage
|
||||||
|
.hypothesis
|
||||||
|
|
|
@ -27,6 +27,5 @@ total | 1
|
||||||
## todo
|
## todo
|
||||||
|
|
||||||
- [x] roll with (dis)advantage
|
- [x] roll with (dis)advantage
|
||||||
- [ ] interactive rolling mode
|
|
||||||
- [x] print criticals
|
- [x] print criticals
|
||||||
- [ ] use property testing
|
- [x] use property testing
|
||||||
|
|
8
justfile
8
justfile
|
@ -23,8 +23,8 @@ env:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
hatch clean
|
hatch clean
|
||||||
hatch env purge
|
hatch env prune
|
||||||
rm -r .{mypy,pytest,ruff}_cache
|
-rm -r .{mypy,pytest,ruff}_cache
|
||||||
rm -r dist
|
-rm -r dist
|
||||||
rm .coverage
|
-rm .coverage
|
||||||
fd __pycache__ --no-ignore --exec rm -r
|
fd __pycache__ --no-ignore --exec rm -r
|
||||||
|
|
|
@ -38,6 +38,7 @@ path = "roll/__about__.py"
|
||||||
[tool.hatch.envs.default]
|
[tool.hatch.envs.default]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"coverage[toml]>=6.5",
|
"coverage[toml]>=6.5",
|
||||||
|
"hypothesis",
|
||||||
"pytest",
|
"pytest",
|
||||||
]
|
]
|
||||||
[tool.hatch.envs.default.scripts]
|
[tool.hatch.envs.default.scripts]
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Roll:
|
||||||
|
|
||||||
dice_count: int = 1
|
dice_count: int = 1
|
||||||
sides: int = 20
|
sides: int = 20
|
||||||
modifier: int | None = None
|
modifier: int = 0
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
if self.dice_count < 1:
|
if self.dice_count < 1:
|
||||||
|
@ -34,14 +34,13 @@ class Roll:
|
||||||
msg = f"expected {value!r} to match pattern {ROLL_PATTERN.pattern!r}"
|
msg = f"expected {value!r} to match pattern {ROLL_PATTERN.pattern!r}"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
dice_count, sides, modifier = match.groups()
|
dice_count, sides, modifier = match.groups()
|
||||||
return cls(int(dice_count), int(sides), int(modifier) if modifier else None)
|
return cls(int(dice_count), int(sides), int(modifier) if modifier else 0)
|
||||||
|
|
||||||
def modifier_str(self) -> str:
|
def modifier_str(self) -> str:
|
||||||
"""return the modifier as a string"""
|
"""return the modifier as a string"""
|
||||||
if self.modifier is None:
|
if self.modifier == 0:
|
||||||
return ""
|
return ""
|
||||||
sign = "+" if self.modifier > 0 else ""
|
return f"{self.modifier:+}"
|
||||||
return f"{sign}{self.modifier}"
|
|
||||||
|
|
||||||
def to_str(self) -> str:
|
def to_str(self) -> str:
|
||||||
"""return the short representation of a roll, e.g. 3d4 or 2d20+3"""
|
"""return the short representation of a roll, e.g. 3d4 or 2d20+3"""
|
||||||
|
|
|
@ -7,12 +7,12 @@ D20_MAX = 20
|
||||||
class Throw:
|
class Throw:
|
||||||
results: list[int]
|
results: list[int]
|
||||||
sides: int
|
sides: int
|
||||||
modifier: int | None = None
|
modifier: int = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self) -> int:
|
def total(self) -> int:
|
||||||
"""calculate the total of the throw, accounting for the modifier"""
|
"""calculate the total of the throw, accounting for the modifier"""
|
||||||
return sum(self.results) + (self.modifier or 0)
|
return sum(self.results) + self.modifier
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_critical_hit(self) -> bool:
|
def is_critical_hit(self) -> bool:
|
||||||
|
|
|
@ -1,13 +1,33 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
from hypothesis import assume, given
|
||||||
|
from hypothesis import strategies as st
|
||||||
|
|
||||||
from roll.roll import Roll
|
from roll.roll import Roll
|
||||||
|
|
||||||
|
|
||||||
def test_roll_validation():
|
@given(st.integers(max_value=0))
|
||||||
|
def test_bad_dice_count(n: int):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
Roll(0, 20)
|
Roll(n, 20)
|
||||||
|
|
||||||
|
|
||||||
|
@given(st.integers(max_value=1))
|
||||||
|
def test_bad_sides(sides: int):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
Roll(1, 1)
|
Roll(1, sides)
|
||||||
|
|
||||||
|
|
||||||
|
@given(
|
||||||
|
st.integers(min_value=1),
|
||||||
|
st.integers(min_value=2),
|
||||||
|
st.integers(),
|
||||||
|
)
|
||||||
|
def test_valid_dice(n: int, sides: int, modifier: int):
|
||||||
|
string = f"{n}d{sides}{modifier:+}" if modifier else f"{n}d{sides}"
|
||||||
|
roll = Roll(n, sides, modifier)
|
||||||
|
roll_from_str = Roll.from_str(string)
|
||||||
|
assert roll == roll_from_str
|
||||||
|
assert string == roll.to_str() and string == roll_from_str.to_str()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -25,16 +45,21 @@ def test_from_bad_str(roll: str):
|
||||||
Roll.from_str(roll)
|
Roll.from_str(roll)
|
||||||
|
|
||||||
|
|
||||||
def test_modify():
|
@given(
|
||||||
roll = Roll(2, 20)
|
st.integers(min_value=1),
|
||||||
modified_roll = roll.modify(3)
|
st.integers(min_value=2),
|
||||||
assert modified_roll == Roll(2, 20, 3)
|
st.integers(),
|
||||||
assert roll == Roll(2, 20)
|
st.integers(),
|
||||||
|
)
|
||||||
|
def test_modify(n: int, sides: int, modifier: int, new_modifier: int):
|
||||||
|
assume(modifier != new_modifier)
|
||||||
|
roll = Roll(n, sides, modifier)
|
||||||
|
modified_roll = roll.modify(new_modifier)
|
||||||
|
assert modified_roll == Roll(n, sides, new_modifier)
|
||||||
assert modified_roll is not roll and modified_roll != roll
|
assert modified_roll is not roll and modified_roll != roll
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("n", list(range(1, 5)))
|
@given(st.integers(min_value=1, max_value=50_000), st.integers(min_value=2))
|
||||||
@pytest.mark.parametrize("sides", list(range(2, 100)))
|
|
||||||
def test_throw(n: int, sides: int):
|
def test_throw(n: int, sides: int):
|
||||||
roll = Roll(n, sides)
|
roll = Roll(n, sides)
|
||||||
throw = roll.throw()
|
throw = roll.throw()
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
|
from hypothesis import given
|
||||||
|
from hypothesis import strategies as st
|
||||||
|
|
||||||
from roll.throw import Throw
|
from roll.throw import Throw
|
||||||
|
|
||||||
|
|
||||||
def test_throw():
|
@given(st.lists(st.integers(min_value=1), min_size=1), st.integers(min_value=2))
|
||||||
throw = Throw([1, 2, 3], sides=4)
|
def test_throw(results: list[int], sides: int):
|
||||||
assert throw.total == 6
|
throw = Throw(results, sides=sides)
|
||||||
|
assert throw.total == sum(results)
|
||||||
assert not throw.is_critical_hit
|
assert not throw.is_critical_hit
|
||||||
assert not throw.is_critical_miss
|
assert not throw.is_critical_miss
|
||||||
|
|
||||||
|
|
||||||
def test_throw_with_modifier():
|
@given(st.lists(st.integers(min_value=1), min_size=1), st.integers(min_value=2), st.integers())
|
||||||
throw = Throw([1, 2, 3], sides=4, modifier=5)
|
def test_throw_with_modifier(results: list[int], sides: int, modifier: int):
|
||||||
assert throw.total == 11
|
throw = Throw(results, sides=sides, modifier=modifier)
|
||||||
|
assert throw.total == (sum(results) + modifier)
|
||||||
assert not throw.is_critical_hit
|
assert not throw.is_critical_hit
|
||||||
assert not throw.is_critical_miss
|
assert not throw.is_critical_miss
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue