cypher dice: some cleanups from my fork that I'm merging back
Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
+12
-5
@@ -1,10 +1,16 @@
|
||||
"""Dice rolling operations (outside of the lex/yacc roller)."""
|
||||
"""Dice rolling operations (outside of the lex/yacc roller).
|
||||
|
||||
SPDX-FileCopyrightText: © 2026 Brian S. Stephan <bss@incorporeal.org>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
import random
|
||||
from typing import Optional, Tuple
|
||||
|
||||
rand = random.SystemRandom()
|
||||
|
||||
|
||||
def cypher_roll(difficulty: int = None, mod: int = 0, is_attack: bool = False):
|
||||
def cypher_roll(difficulty: Optional[int] = None, mod: int = 0, is_attack: bool = False) \
|
||||
-> Tuple[int, Optional[int], Optional[bool], Optional[str], Optional[str], int, Optional[int]]:
|
||||
"""Make a Cypher System roll.
|
||||
|
||||
Args:
|
||||
@@ -16,8 +22,8 @@ def cypher_roll(difficulty: int = None, mod: int = 0, is_attack: bool = False):
|
||||
- the result on the d20
|
||||
- the highest difficulty beaten
|
||||
- if the difficulty is known, if the target was beat
|
||||
- miscellaneous effects
|
||||
- miscellaneous negative effects
|
||||
- miscellaneous effects, if any
|
||||
- miscellaneous negative effects, if any
|
||||
- the level of the result on the d20
|
||||
- the net difficulty level, if known
|
||||
"""
|
||||
@@ -57,6 +63,7 @@ def cypher_result(difficulty: int, modifier: int, is_attack: bool, stat: str, co
|
||||
else:
|
||||
display_comment = "attack" if is_attack else "task"
|
||||
|
||||
# roll it
|
||||
result, beats, success, effect, neg_effect, result_lvl, diff_level = cypher_roll(
|
||||
difficulty=difficulty, mod=modifier, is_attack=is_attack
|
||||
)
|
||||
@@ -94,7 +101,7 @@ def cypher_result(difficulty: int, modifier: int, is_attack: bool, stat: str, co
|
||||
elif modifier < 0:
|
||||
modded_diff = f"{difficulty}{modifier}"
|
||||
else:
|
||||
modded_diff = difficulty
|
||||
modded_diff = str(difficulty)
|
||||
|
||||
if ircify:
|
||||
detail_str = f"14(res. {result_lvl} (d20={result}) vs. {stat} difficulty {diff_level} ({modded_diff}))"
|
||||
|
||||
+44
-22
@@ -1,4 +1,8 @@
|
||||
"""Tests for dice operations."""
|
||||
"""Tests for dice operations.
|
||||
|
||||
SPDX-FileCopyrightText: © 2026 Brian S. Stephan <bss@incorporeal.org>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase
|
||||
@@ -6,57 +10,65 @@ from django.test import TestCase
|
||||
import dice.lib
|
||||
|
||||
|
||||
class DiceLibTestCase(TestCase):
|
||||
"""Test that a variety of dice rolls work as expected."""
|
||||
class CypherRollTestCase(TestCase):
|
||||
"""Test that a variety of Cypher System dice rolls work as expected."""
|
||||
|
||||
def test_cypher_rolls(self):
|
||||
"""Roll a variety of Cypher System rolls."""
|
||||
# simple task, simple check
|
||||
def test_simple_check(self):
|
||||
"""Test a simple task, simple check."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=5):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (5, 1, True, None, None, 1, 1))
|
||||
|
||||
# simple failure
|
||||
def test_simple_check_failure(self):
|
||||
"""Test a simple failure."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=2):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (2, 0, False, None, None, 0, 1))
|
||||
|
||||
# rolled a 1
|
||||
def test_gm_intrusion(self):
|
||||
"""Test rolling a 1."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=1):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (1, None, False, None, 'a GM intrusion', 0, 1))
|
||||
|
||||
# rolled a 17 on an attack
|
||||
def test_attack_17(self):
|
||||
"""Test an attack with bonus damage."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=17):
|
||||
result = dice.lib.cypher_roll(difficulty=1, is_attack=True)
|
||||
self.assertEqual(result, (17, 5, True, '+1 damage', None, 5, 1))
|
||||
|
||||
# rolled a 18 on an attack
|
||||
def test_attack_18(self):
|
||||
"""Test an attack with more bonus damage."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=18):
|
||||
result = dice.lib.cypher_roll(difficulty=1, is_attack=True)
|
||||
self.assertEqual(result, (18, 6, True, '+2 damage', None, 6, 1))
|
||||
|
||||
# rolled a 17
|
||||
def test_task_17(self):
|
||||
"""Test a task 17 has no bonus damage."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=17):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (17, 5, True, None, None, 5, 1))
|
||||
|
||||
# rolled a 18
|
||||
def test_task_18(self):
|
||||
"""Test a task 18 has no bonus damage."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=18):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (18, 6, True, None, None, 6, 1))
|
||||
|
||||
# rolled a 19
|
||||
def test_19(self):
|
||||
"""Test a 19 has a minor effect."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=19):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (19, 6, True, 'a minor effect', None, 6, 1))
|
||||
|
||||
# rolled a 20
|
||||
def test_20(self):
|
||||
"""Test a 20 has a minor effect."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=20):
|
||||
result = dice.lib.cypher_roll(difficulty=1)
|
||||
self.assertEqual(result, (20, 6, True, 'a MAJOR EFFECT', None, 6, 1))
|
||||
|
||||
# mods affect the result of what the roll beats
|
||||
def test_modifiers_beat_rolls(self):
|
||||
"""Test that mods affect the result of what the roll beats."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=2):
|
||||
result = dice.lib.cypher_roll(difficulty=1, mod=-5)
|
||||
self.assertEqual(result, (2, 5, True, None, None, 0, -4))
|
||||
@@ -65,7 +77,8 @@ class DiceLibTestCase(TestCase):
|
||||
result = dice.lib.cypher_roll(difficulty=3, mod=-3)
|
||||
self.assertEqual(result, (2, 3, True, None, None, 0, 0))
|
||||
|
||||
# ...even without a difficulty known
|
||||
def test_modifiers_beat_rolls_unknown_difficulty(self):
|
||||
"""Test that mods affect the result of what the roll beats even without a difficulty known."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=2):
|
||||
result = dice.lib.cypher_roll(mod=-5)
|
||||
self.assertEqual(result, (2, 5, None, None, None, 0, None))
|
||||
@@ -78,7 +91,8 @@ class DiceLibTestCase(TestCase):
|
||||
result = dice.lib.cypher_roll()
|
||||
self.assertEqual(result, (1, None, None, None, 'a GM intrusion', 0, None))
|
||||
|
||||
# general "don't know the difficulty" kind of check
|
||||
def test_unknown_difficulty(self):
|
||||
"""Test general "don't know the difficulty" kind of checks."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=10):
|
||||
result = dice.lib.cypher_roll(mod=-2)
|
||||
self.assertEqual(result, (10, 5, None, None, None, 3, None))
|
||||
@@ -88,11 +102,9 @@ class DiceLibTestCase(TestCase):
|
||||
result = dice.lib.cypher_roll(mod=2)
|
||||
self.assertEqual(result, (10, 1, None, None, None, 3, None))
|
||||
|
||||
def test_cypher_result(self):
|
||||
"""Test the Cypher result string generation."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=10):
|
||||
result = dice.lib.cypher_result(3, 0, False, "Might", "")
|
||||
self.assertEqual(result, "task succeeded! (res. 3 (d20=10) vs. Might difficulty 3 (3))")
|
||||
|
||||
class DiceLibTestCase(TestCase):
|
||||
"""Test that a variety of dice rolls work as expected."""
|
||||
|
||||
def test_reaction_roll(self):
|
||||
"""Roll possible reactions."""
|
||||
@@ -112,3 +124,13 @@ class DiceLibTestCase(TestCase):
|
||||
self.assertEqual(dice.lib.reaction_roll(), (19, 'positive', '+'))
|
||||
with mock.patch('random.SystemRandom.randint', return_value=20):
|
||||
self.assertEqual(dice.lib.reaction_roll(), (20, 'VERY positive', '++'))
|
||||
|
||||
|
||||
class CypherRollStringTestCase(TestCase):
|
||||
"""Test that Cypher System roll results are in the expected string representation."""
|
||||
|
||||
def test_simple_result(self):
|
||||
"""Test the basics of string output."""
|
||||
with mock.patch('random.SystemRandom.randint', return_value=10):
|
||||
result = dice.lib.cypher_result(3, 0, False, "Might", "")
|
||||
self.assertEqual(result, "task succeeded! (res. 3 (d20=10) vs. Might difficulty 3 (3))")
|
||||
|
||||
Reference in New Issue
Block a user