CLAUDE: Implement /scout command with weighted dice rolling
Added weighted dice rolling system for scouting cards based on card type. Features: - New /scout command with card_type parameter (batter or pitcher) - Weighted first d6 roll: - Batter: Always rolls 1-3 on first d6 - Pitcher: Always rolls 4-6 on first d6 - Remaining dice (2d6 and 1d20) roll normally - Uses same embed formatting as /ab command - Comprehensive test coverage (4 new tests) Implementation: - Added _roll_weighted_scout_dice() helper method - Reuses existing dice rolling and embed creation logic - Follows established command patterns with @logged_command decorator Tests: - test_weighted_scout_dice_batter - Verifies batter weighting (20 iterations) - test_weighted_scout_dice_pitcher - Verifies pitcher weighting (20 iterations) - test_scout_command_batter - Tests batter slash command - test_scout_command_pitcher - Tests pitcher slash command All 34 dice command tests pass.
This commit is contained in:
parent
c5fecc878f
commit
4cab227109
@ -108,6 +108,37 @@ class DiceRollCommands(commands.Cog):
|
||||
embed.title = f'At bat roll for {ctx.author.display_name}'
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@discord.app_commands.command(
|
||||
name="scout",
|
||||
description="Roll weighted scouting dice (1d6;2d6;1d20) based on card type"
|
||||
)
|
||||
@discord.app_commands.describe(
|
||||
card_type="Type of card being scouted"
|
||||
)
|
||||
@discord.app_commands.choices(card_type=[
|
||||
discord.app_commands.Choice(name="Batter", value="batter"),
|
||||
discord.app_commands.Choice(name="Pitcher", value="pitcher")
|
||||
])
|
||||
@logged_command("/scout")
|
||||
async def scout_dice(
|
||||
self,
|
||||
interaction: discord.Interaction,
|
||||
card_type: discord.app_commands.Choice[str]
|
||||
):
|
||||
"""Roll weighted scouting dice based on card type (batter or pitcher)."""
|
||||
await interaction.response.defer()
|
||||
|
||||
# Get the card type value
|
||||
card_type_value = card_type.value
|
||||
|
||||
# Roll weighted scouting dice
|
||||
roll_results = self._roll_weighted_scout_dice(card_type_value)
|
||||
|
||||
# Create embed for the roll results
|
||||
embed = self._create_multi_roll_embed("1d6;2d6;1d20", roll_results, interaction.user)
|
||||
embed.title = f'Scouting roll for {interaction.user.display_name} ({card_type.name})'
|
||||
await interaction.followup.send(embed=embed)
|
||||
|
||||
@discord.app_commands.command(
|
||||
name="fielding",
|
||||
description="Roll Super Advanced fielding dice for a defensive position"
|
||||
@ -563,6 +594,38 @@ class DiceRollCommands(commands.Cog):
|
||||
'total': total
|
||||
}
|
||||
|
||||
def _roll_weighted_scout_dice(self, card_type: str) -> list[dict]:
|
||||
"""
|
||||
Roll scouting dice with weighted first d6 based on card type.
|
||||
|
||||
Args:
|
||||
card_type: Either "batter" (1-3) or "pitcher" (4-6) for first d6
|
||||
|
||||
Returns:
|
||||
List of 3 roll result dicts: weighted 1d6, normal 2d6, normal 1d20
|
||||
"""
|
||||
# First die (1d6) - weighted based on card type
|
||||
if card_type == "batter":
|
||||
first_roll = random.randint(1, 3)
|
||||
else: # pitcher
|
||||
first_roll = random.randint(4, 6)
|
||||
|
||||
first_d6_result = {
|
||||
'dice_notation': '1d6',
|
||||
'num_dice': 1,
|
||||
'die_sides': 6,
|
||||
'rolls': [first_roll],
|
||||
'total': first_roll
|
||||
}
|
||||
|
||||
# Second roll (2d6) - normal
|
||||
second_result = self._parse_and_roll_single_dice("2d6")
|
||||
|
||||
# Third roll (1d20) - normal
|
||||
third_result = self._parse_and_roll_single_dice("1d20")
|
||||
|
||||
return [first_d6_result, second_result, third_result]
|
||||
|
||||
def _create_multi_roll_embed(self, dice_notation: str, roll_results: list[dict], user: discord.User | discord.Member) -> discord.Embed:
|
||||
"""Create an embed for multiple dice roll results."""
|
||||
embed = EmbedTemplate.create_base_embed(
|
||||
|
||||
@ -551,4 +551,100 @@ class TestDiceRollCommands:
|
||||
# Check 3d6
|
||||
assert results[1]['dice_notation'] == '3d6'
|
||||
assert results[1]['num_dice'] == 3
|
||||
assert results[1]['die_sides'] == 6
|
||||
assert results[1]['die_sides'] == 6
|
||||
|
||||
def test_weighted_scout_dice_batter(self, dice_cog):
|
||||
"""Test that batter scout dice always rolls 1-3 for first d6."""
|
||||
# Roll 20 times to ensure consistency
|
||||
for _ in range(20):
|
||||
results = dice_cog._roll_weighted_scout_dice("batter")
|
||||
|
||||
# Should have 3 dice groups (1d6, 2d6, 1d20)
|
||||
assert len(results) == 3
|
||||
|
||||
# First d6 should ALWAYS be 1-3 for batter
|
||||
first_d6 = results[0]['rolls'][0]
|
||||
assert 1 <= first_d6 <= 3, f"Batter first d6 was {first_d6}, expected 1-3"
|
||||
|
||||
# Second roll (2d6) should be normal
|
||||
assert results[1]['num_dice'] == 2
|
||||
assert results[1]['die_sides'] == 6
|
||||
assert all(1 <= roll <= 6 for roll in results[1]['rolls'])
|
||||
|
||||
# Third roll (1d20) should be normal
|
||||
assert results[2]['num_dice'] == 1
|
||||
assert results[2]['die_sides'] == 20
|
||||
assert 1 <= results[2]['rolls'][0] <= 20
|
||||
|
||||
def test_weighted_scout_dice_pitcher(self, dice_cog):
|
||||
"""Test that pitcher scout dice always rolls 4-6 for first d6."""
|
||||
# Roll 20 times to ensure consistency
|
||||
for _ in range(20):
|
||||
results = dice_cog._roll_weighted_scout_dice("pitcher")
|
||||
|
||||
# Should have 3 dice groups (1d6, 2d6, 1d20)
|
||||
assert len(results) == 3
|
||||
|
||||
# First d6 should ALWAYS be 4-6 for pitcher
|
||||
first_d6 = results[0]['rolls'][0]
|
||||
assert 4 <= first_d6 <= 6, f"Pitcher first d6 was {first_d6}, expected 4-6"
|
||||
|
||||
# Second roll (2d6) should be normal
|
||||
assert results[1]['num_dice'] == 2
|
||||
assert results[1]['die_sides'] == 6
|
||||
assert all(1 <= roll <= 6 for roll in results[1]['rolls'])
|
||||
|
||||
# Third roll (1d20) should be normal
|
||||
assert results[2]['num_dice'] == 1
|
||||
assert results[2]['die_sides'] == 20
|
||||
assert 1 <= results[2]['rolls'][0] <= 20
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scout_command_batter(self, dice_cog, mock_interaction):
|
||||
"""Test scout slash command with batter card type."""
|
||||
# Mock a card_type choice
|
||||
card_type_choice = MagicMock()
|
||||
card_type_choice.value = 'batter'
|
||||
card_type_choice.name = 'Batter'
|
||||
|
||||
await dice_cog.scout_dice.callback(dice_cog, mock_interaction, card_type_choice)
|
||||
|
||||
# Verify response was deferred
|
||||
mock_interaction.response.defer.assert_called_once()
|
||||
|
||||
# Verify followup was sent with embed
|
||||
mock_interaction.followup.send.assert_called_once()
|
||||
call_args = mock_interaction.followup.send.call_args
|
||||
assert 'embed' in call_args.kwargs
|
||||
|
||||
# Verify embed has the correct format
|
||||
embed = call_args.kwargs['embed']
|
||||
assert isinstance(embed, discord.Embed)
|
||||
assert embed.title == "Scouting roll for TestUser (Batter)"
|
||||
assert len(embed.fields) == 1
|
||||
assert "Details:[1d6;2d6;1d20" in embed.fields[0].value
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scout_command_pitcher(self, dice_cog, mock_interaction):
|
||||
"""Test scout slash command with pitcher card type."""
|
||||
# Mock a card_type choice
|
||||
card_type_choice = MagicMock()
|
||||
card_type_choice.value = 'pitcher'
|
||||
card_type_choice.name = 'Pitcher'
|
||||
|
||||
await dice_cog.scout_dice.callback(dice_cog, mock_interaction, card_type_choice)
|
||||
|
||||
# Verify response was deferred
|
||||
mock_interaction.response.defer.assert_called_once()
|
||||
|
||||
# Verify followup was sent with embed
|
||||
mock_interaction.followup.send.assert_called_once()
|
||||
call_args = mock_interaction.followup.send.call_args
|
||||
assert 'embed' in call_args.kwargs
|
||||
|
||||
# Verify embed has the correct format
|
||||
embed = call_args.kwargs['embed']
|
||||
assert isinstance(embed, discord.Embed)
|
||||
assert embed.title == "Scouting roll for TestUser (Pitcher)"
|
||||
assert len(embed.fields) == 1
|
||||
assert "Details:[1d6;2d6;1d20" in embed.fields[0].value
|
||||
Loading…
Reference in New Issue
Block a user