aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/source/config.py24
-rw-r--r--src/oneroll/__init__.py19
-rw-r--r--src/oneroll/__main__.py198
-rw-r--r--src/oneroll/_core.pyi303
-rw-r--r--src/oneroll/tui.py95
5 files changed, 317 insertions, 322 deletions
diff --git a/docs/source/config.py b/docs/source/config.py
index fa4794e..83c0158 100644
--- a/docs/source/config.py
+++ b/docs/source/config.py
@@ -10,9 +10,11 @@ if sys.version_info >= (3, 11):
else:
import tomli as tomllib
+
def setup(app):
- app.add_config_value('releaselevel', '', 'env')
-
+ app.add_config_value("releaselevel", "", "env")
+
+
DATA = None
PYPROJECT = os.path.join("..", "..", "Cargo.toml")
with open(PYPROJECT, "r", encoding="utf8") as f:
@@ -55,17 +57,17 @@ extensions = [
"myst_parser",
]
-doctest_global_setup = '''
+doctest_global_setup = """
try:
import hydro_roll as hr
import hydro_roll_core as hrc
except ImportError:
hr = None
hrc = None
-'''
+"""
todo_include_todos = True
todo_emit_warnings = True
-intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
+intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
extlinks = {
@@ -90,13 +92,13 @@ rst_epilog = """
locale_dirs = ["../locales/"] # path is example but recommended.
gettext_compact = False # optional.
gettext_uuid = True # optional.
-numfig = True # Figures, tables and code blocks are automatically numbered if they have a title
-pygments_style = "rrt" # default sphinx, change the style of code block
-math_number_all = True # Number all equations, figures, tables and code blocks
+numfig = True # Figures, tables and code blocks are automatically numbered if they have a title
+pygments_style = "rrt" # default sphinx, change the style of code block
+math_number_all = True # Number all equations, figures, tables and code blocks
html_additional_pages = {
- 'copy': 'copying.html',
+ "copy": "copying.html",
}
-html_split_index = True # Split the index page by each alphabet
+html_split_index = True # Split the index page by each alphabet
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
@@ -146,4 +148,4 @@ html_theme_options = {
# html_sidebars = {
# '**': ['globaltoc.html', 'sourcelink.html', 'searchbox.html', 'relations.html'],
# 'using/windows': ['windowssidebar.html', 'searchbox.html'],
-# } \ No newline at end of file
+# }
diff --git a/src/oneroll/__init__.py b/src/oneroll/__init__.py
index 5dddfad..cd42d60 100644
--- a/src/oneroll/__init__.py
+++ b/src/oneroll/__init__.py
@@ -11,17 +11,17 @@ Main functions:
- Bracket support
- Complete error handling
-Example of usage:
-# Basic use
-import oneroll
-result = oneroll.roll("3d6 + 2")
-print(result.total) # output total points
+Example of usage:
+# Basic use
+import oneroll
+result = oneroll.roll("3d6 + 2")
+print(result.total) # output total points
-# Use the OneRoll class
-roller = oneroll.OneRoll()
-result = roller.roll("4d6kh3")
+# Use the OneRoll class
+roller = oneroll.OneRoll()
+result = roller.roll("4d6kh3")
-# Simple throw
+# Simple throw
total = oneroll.roll_simple(3, 6)
"""
@@ -36,6 +36,7 @@ __version__ = "1.3.2"
__author__ = "HsiangNianian"
__description__ = "高性能骰子表达式解析器"
+
# 重新导出主要类和函数,提供更友好的接口
class OneRoll:
"""
diff --git a/src/oneroll/__main__.py b/src/oneroll/__main__.py
index 7ac50f4..04bfff9 100644
--- a/src/oneroll/__main__.py
+++ b/src/oneroll/__main__.py
@@ -4,14 +4,14 @@ OneRoll interactive dice roll program
Supports command line parameters and interactive mode.
-Example of usage:
-# Direct throw
-python -m oneroll "3d6 + 2"
+Example of usage:
+# Direct throw
+python -m oneroll "3d6 + 2"
-# Interactive mode
-python -m oneroll
+# Interactive mode
+python -m oneroll
-# Statistical Mode
+# Statistical Mode
python -m oneroll --stats "3d6" --times 100
"""
@@ -30,23 +30,24 @@ import oneroll
console = Console()
+
class OneRollCLI:
"""OneRoll command line interface"""
-
+
def __init__(self):
self.roller = OneRoll()
self.history: List[Dict[str, Any]] = []
-
+
def print_result(self, result: Dict[str, Any], expression: str = None):
"""Pretty print the dice roll result"""
if expression is None:
- expression = result.get('expression', 'Unknown')
-
+ expression = result.get("expression", "Unknown")
+
# Create result panel
- total = result['total']
- details = result['details']
- rolls = result['rolls']
-
+ total = result["total"]
+ details = result["details"]
+ rolls = result["rolls"]
+
# Select color based on result value
if total >= 15:
color = "green"
@@ -54,65 +55,65 @@ class OneRollCLI:
color = "yellow"
else:
color = "red"
-
+
# Build display text
text = Text()
text.append(f"🎲 {expression}\n", style="bold blue")
text.append(f"总点数: ", style="bold")
text.append(f"{total}", style=f"bold {color}")
text.append(f"\n详情: {details}", style="white")
-
+
if rolls:
text.append(f"\n投掷结果: ", style="bold")
text.append(f"{rolls}", style="cyan")
-
+
# Display comment
comment = result.get("comment", "")
if comment:
text.append(f"\n注释: ", style="bold")
text.append(f"{comment}", style="italic blue")
-
+
panel = Panel(text, title="Dice Roll Result", border_style=color)
console.print(panel)
-
+
def print_statistics(self, stats: Dict[str, Any], expression: str):
"""Print statistics information"""
table = Table(title=f"统计结果: {expression} (投掷 {stats['count']} 次)")
table.add_column("统计项", style="cyan")
table.add_column("数值", style="green")
-
- table.add_row("最小值", str(stats['min']))
- table.add_row("最大值", str(stats['max']))
+
+ table.add_row("最小值", str(stats["min"]))
+ table.add_row("最大值", str(stats["max"]))
table.add_row("平均值", f"{stats['mean']:.2f}")
- table.add_row("总和", str(stats['total']))
- table.add_row("投掷次数", str(stats['count']))
-
+ table.add_row("总和", str(stats["total"]))
+ table.add_row("投掷次数", str(stats["count"]))
+
console.print(table)
-
+
def print_history(self):
"""Print dice roll history"""
if not self.history:
console.print("暂无投掷历史", style="yellow")
return
-
+
table = Table(title="投掷历史")
table.add_column("序号", style="cyan")
table.add_column("表达式", style="green")
table.add_column("总点数", style="yellow")
table.add_column("注释", style="blue")
table.add_column("详情", style="white")
-
+
for i, result in enumerate(self.history[-10:], 1): # only show last 30 times
table.add_row(
str(i),
- result.get('expression', 'Unknown'),
- str(result['total']),
- result.get('comment', ''),
- result['details']
+ result.get("expression", "Unknown"),
+ str(result["total"]),
+ result.get("comment", ""),
+ result["details"],
)
-
+
console.print(table)
-
+
def show_help(self):
"""show help information"""
help_text = """
@@ -144,41 +145,41 @@ class OneRollCLI:
• disadvantage - 2d20kl1 (劣势)
• attribute - 4d6kh3 (属性投掷)
"""
-
+
console.print(Panel(help_text, title="帮助信息", border_style="blue"))
-
+
def interactive_mode(self):
"""Interactive Mode"""
console.print(Panel.fit("🎲 OneRoll 交互式掷骰程序", style="bold blue"))
console.print("输入 'help' 查看帮助,输入 'quit' 退出程序\n")
-
+
while True:
try:
user_input = Prompt.ask("🎲 请输入骰子表达式").strip()
-
+
if not user_input:
continue
-
+
# handle special command
- if user_input.lower() in ['quit', 'exit', 'q']:
+ if user_input.lower() in ["quit", "exit", "q"]:
if Confirm.ask("确定要退出吗?"):
break
continue
-
- if user_input.lower() == 'help':
+
+ if user_input.lower() == "help":
self.show_help()
continue
-
- if user_input.lower() == 'history':
+
+ if user_input.lower() == "history":
self.print_history()
continue
-
- if user_input.lower() == 'clear':
+
+ if user_input.lower() == "clear":
self.history.clear()
console.print("历史已清空", style="green")
continue
-
- if user_input.lower().startswith('stats '):
+
+ if user_input.lower().startswith("stats "):
parts = user_input.split()
if len(parts) >= 3:
expression = parts[1]
@@ -187,16 +188,18 @@ class OneRollCLI:
if times > 1000:
console.print("统计次数不能超过1000次", style="red")
continue
-
+
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
- console=console
+ console=console,
) as progress:
- task = progress.add_task(f"正在统计 {expression}...", total=None)
+ task = progress.add_task(
+ f"正在统计 {expression}...", total=None
+ )
stats = roll_statistics(expression, times)
progress.stop()
-
+
self.print_statistics(stats, expression)
except ValueError:
console.print("统计次数必须是数字", style="red")
@@ -205,10 +208,10 @@ class OneRollCLI:
else:
console.print("用法: stats <表达式> <次数>", style="red")
continue
-
+
# resolve regular expression's alias
expression = self._resolve_expression_alias(user_input)
-
+
# execute roll
try:
result = roll(expression)
@@ -216,39 +219,42 @@ class OneRollCLI:
self.print_result(result, user_input)
except Exception as e:
console.print(f"错误: {e}", style="red")
-
+
except KeyboardInterrupt:
if Confirm.ask("\n确定要退出吗?"):
break
except EOFError:
break
-
+
def _resolve_expression_alias(self, user_input: str) -> str:
"""resolve regular expression's alias"""
aliases = {
- 'd20': CommonRolls.D20,
- 'advantage': CommonRolls.D20_ADVANTAGE,
- 'disadvantage': CommonRolls.D20_DISADVANTAGE,
- 'attr': CommonRolls.ATTRIBUTE_ROLL,
- 'attribute': CommonRolls.ATTRIBUTE_ROLL,
+ "d20": CommonRolls.D20,
+ "advantage": CommonRolls.D20_ADVANTAGE,
+ "disadvantage": CommonRolls.D20_DISADVANTAGE,
+ "attr": CommonRolls.ATTRIBUTE_ROLL,
+ "attribute": CommonRolls.ATTRIBUTE_ROLL,
}
-
+
return aliases.get(user_input.lower(), user_input)
-
+
def run(self, args):
"""run tui mode"""
if args.tui:
# start tui
try:
from .tui import run_tui
+
run_tui()
except ImportError:
- console.print("TUI 模式需要安装 textual: pip install textual", style="red")
+ console.print(
+ "TUI 模式需要安装 textual: pip install textual", style="red"
+ )
sys.exit(1)
except Exception as e:
console.print(f"TUI 启动失败: {e}", style="red")
sys.exit(1)
-
+
elif args.expression:
# single roll mode
try:
@@ -257,7 +263,7 @@ class OneRollCLI:
except Exception as e:
console.print(f"错误: {e}", style="red")
sys.exit(1)
-
+
elif args.stats:
# stats mode
try:
@@ -265,25 +271,26 @@ class OneRollCLI:
if times > 10000:
console.print("统计次数不能超过10000次", style="red")
sys.exit(1)
-
+
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
- console=console
+ console=console,
) as progress:
task = progress.add_task(f"正在统计 {args.stats}...", total=None)
stats = roll_statistics(args.stats, times)
progress.stop()
-
+
self.print_statistics(stats, args.stats)
except Exception as e:
console.print(f"错误: {e}", style="red")
sys.exit(1)
-
+
else:
# interactive mode
self.interactive_mode()
+
def main():
parser = argparse.ArgumentParser(
description="OneRoll 骰子表达式解析器",
@@ -294,43 +301,24 @@ def main():
%(prog)s --stats "3d6" --times 100 # 统计模式
%(prog)s --tui # 终端用户界面
%(prog)s # 交互式模式
- """
+ """,
)
-
- parser.add_argument(
- 'expression',
- nargs='?',
- help='骰子表达式,如 "3d6 + 2"'
- )
-
- parser.add_argument(
- '--stats',
- help='统计模式,指定要统计的表达式'
- )
-
- parser.add_argument(
- '--times',
- type=int,
- default=100,
- help='统计次数,默认100次'
- )
-
- parser.add_argument(
- '--version',
- action='version',
- version=oneroll.__version__
- )
-
- parser.add_argument(
- '--tui',
- action='store_true',
- help='启动终端用户界面 (TUI)'
- )
-
+
+ parser.add_argument("expression", nargs="?", help='骰子表达式,如 "3d6 + 2"')
+
+ parser.add_argument("--stats", help="统计模式,指定要统计的表达式")
+
+ parser.add_argument("--times", type=int, default=100, help="统计次数,默认100次")
+
+ parser.add_argument("--version", action="version", version=oneroll.__version__)
+
+ parser.add_argument("--tui", action="store_true", help="启动终端用户界面 (TUI)")
+
args = parser.parse_args()
-
+
cli = OneRollCLI()
cli.run(args)
-if __name__ == '__main__':
- main() \ No newline at end of file
+
+if __name__ == "__main__":
+ main()
diff --git a/src/oneroll/_core.pyi b/src/oneroll/_core.pyi
index 7916261..d81297d 100644
--- a/src/oneroll/_core.pyi
+++ b/src/oneroll/_core.pyi
@@ -5,185 +5,183 @@ RollHistory = List[DiceResult]
ModifierList = List[str]
def roll_dice(expression: str) -> DiceResult:
- """
- Analyze and calculate dice expressions
-
- Args:
- expression: Dice expression string, supports the following formats:
- - Basic dice: "3d6", "1d20", "2d10"
- - Mathematical operations: "3d6 + 2", "2d6 * 3", "(2d6 + 3) * 2"
- - Modifiers: "2d6!", "4d6kh3", "5d6dl1", "3d6r1", "4d6ro1"
-
- Returns:
- A dictionary containing the following keys:
- - expression: str - expression string
- - total: int - Total points
- - rolls: List[List[int]] - List of throw results
- - details: str - details
- - comment: str - User comment
-
- Raises:
- ValueError: When the expression is invalid
-
- Example:
- result = roll_dice("3d6 + 2")
- print(result["total"]) # output total points
+ """
+ Analyze and calculate dice expressions
+
+ Args:
+ expression: Dice expression string, supports the following formats:
+ - Basic dice: "3d6", "1d20", "2d10"
+ - Mathematical operations: "3d6 + 2", "2d6 * 3", "(2d6 + 3) * 2"
+ - Modifiers: "2d6!", "4d6kh3", "5d6dl1", "3d6r1", "4d6ro1"
+
+ Returns:
+ A dictionary containing the following keys:
+ - expression: str - expression string
+ - total: int - Total points
+ - rolls: List[List[int]] - List of throw results
+ - details: str - details
+ - comment: str - User comment
+
+ Raises:
+ ValueError: When the expression is invalid
+
+ Example:
+ result = roll_dice("3d6 + 2")
+ print(result["total"]) # output total points
"""
...
def roll_simple(dice_count: int, dice_sides: int) -> int:
- """
- Simple dice throw
+ """
+ Simple dice throw
- Args:
- dice_count: The number of dice must be greater than 0
- dice_sides: The number of dice faces must be greater than 0
+ Args:
+ dice_count: The number of dice must be greater than 0
+ dice_sides: The number of dice faces must be greater than 0
- Returns:
- Total points
+ Returns:
+ Total points
- Raises:
- ValueError: When the parameter is invalid
+ Raises:
+ ValueError: When the parameter is invalid
- Example:
- total = roll_simple(3, 6) # Roll 3 6-sided dice
+ Example:
+ total = roll_simple(3, 6) # Roll 3 6-sided dice
"""
...
class OneRoll:
- """
- OneRoll dice thrower category
+ """
+ OneRoll dice thrower category
- Provides an object-oriented dice throwing interface, supporting complex expressions and various modifiers.
+ Provides an object-oriented dice throwing interface, supporting complex expressions and various modifiers.
"""
-
+
def __init__(self) -> None:
"""
Initialize OneRoll Object
-
+
Example:
roller = OneRoll()
"""
...
-
+
def roll(self, expression: str) -> DiceResult:
- """
- Analyze and calculate dice expressions
-
- Args:
- expression: Dice expression string, supports the following formats:
- - Basic dice: "3d6", "1d20", "2d10"
- - Mathematical operations: "3d6 + 2", "2d6 * 3", "(2d6 + 3) * 2"
- - Modifiers: "2d6!", "4d6kh3", "5d6dl1", "3d6r1", "4d6ro1"
-
- Returns:
- A dictionary containing the following keys:
- - expression: str - expression string
- - total: int - Total points
- - rolls: List[List[int]] - List of throw results
- - details: str - details
- - comment: str - User comment
-
- Raises:
- ValueError: When the expression is invalid
-
- Example:
- roller = OneRoll()
- result = roller.roll("3d6 + 2")
- print(f"Total points: {result['total']}")
+ """
+ Analyze and calculate dice expressions
+
+ Args:
+ expression: Dice expression string, supports the following formats:
+ - Basic dice: "3d6", "1d20", "2d10"
+ - Mathematical operations: "3d6 + 2", "2d6 * 3", "(2d6 + 3) * 2"
+ - Modifiers: "2d6!", "4d6kh3", "5d6dl1", "3d6r1", "4d6ro1"
+
+ Returns:
+ A dictionary containing the following keys:
+ - expression: str - expression string
+ - total: int - Total points
+ - rolls: List[List[int]] - List of throw results
+ - details: str - details
+ - comment: str - User comment
+
+ Raises:
+ ValueError: When the expression is invalid
+
+ Example:
+ roller = OneRoll()
+ result = roller.roll("3d6 + 2")
+ print(f"Total points: {result['total']}")
"""
...
-
+
def roll_simple(self, dice_count: int, dice_sides: int) -> int:
- """
- Simple dice throw
+ """
+ Simple dice throw
- Args:
- dice_count: The number of dice must be greater than 0
- dice_sides: The number of dice faces must be greater than 0
+ Args:
+ dice_count: The number of dice must be greater than 0
+ dice_sides: The number of dice faces must be greater than 0
- Returns:
- Total points
+ Returns:
+ Total points
- Raises:
- ValueError: When the parameter is invalid
+ Raises:
+ ValueError: When the parameter is invalid
- Example:
- roller = OneRoll()
- total = roller.roll_simple(3, 6) # Throw 3 6-sided dice
+ Example:
+ roller = OneRoll()
+ total = roller.roll_simple(3, 6) # Throw 3 6-sided dice
"""
...
-
+
def roll_with_modifiers(
- self,
- dice_count: int,
- dice_sides: int,
- modifiers: ModifierList
+ self, dice_count: int, dice_sides: int, modifiers: ModifierList
) -> DiceResult:
- """
- Dice throw with modifier
-
- Args:
- dice_count: The number of dice must be greater than 0
- dice_sides: The number of dice faces must be greater than 0
- modifiers: modifier list, supports the following formats:
- - "!" - Explosion dice
- - "r<number>" - Re-submit, such as "r1"
- - "ro<number>" - Conditional re-submission, such as "ro1"
- - "kh<number>" - Take the height, such as "kh3"
- - "kl<number>" - Take the low, such as "kl2"
- - "dh<number>" - discard the height, such as "dh1"
- - "dl<number>" - discard low, such as "dl1"
-
- Returns:
- A dictionary containing the following keys:
- - total: int - Total points
- - rolls: List[List[int]] - List of throw results
- - details: str - details
-
- Raises:
- ValueError: When the parameter or modifier is invalid
-
- Example:
- roller = OneRoll()
- result = roller.roll_with_modifiers(4, 6, ["kh3"]) # 4d6kh3
- print(f"Total points: {result['total']}")
+ """
+ Dice throw with modifier
+
+ Args:
+ dice_count: The number of dice must be greater than 0
+ dice_sides: The number of dice faces must be greater than 0
+ modifiers: modifier list, supports the following formats:
+ - "!" - Explosion dice
+ - "r<number>" - Re-submit, such as "r1"
+ - "ro<number>" - Conditional re-submission, such as "ro1"
+ - "kh<number>" - Take the height, such as "kh3"
+ - "kl<number>" - Take the low, such as "kl2"
+ - "dh<number>" - discard the height, such as "dh1"
+ - "dl<number>" - discard low, such as "dl1"
+
+ Returns:
+ A dictionary containing the following keys:
+ - total: int - Total points
+ - rolls: List[List[int]] - List of throw results
+ - details: str - details
+
+ Raises:
+ ValueError: When the parameter or modifier is invalid
+
+ Example:
+ roller = OneRoll()
+ result = roller.roll_with_modifiers(4, 6, ["kh3"]) # 4d6kh3
+ print(f"Total points: {result['total']}")
"""
...
def is_valid_expression(expression: str) -> bool:
- """
- Check if the expression is valid
+ """
+ Check if the expression is valid
- Args:
- expression: The expression string to check
+ Args:
+ expression: The expression string to check
- Returns:
- Return True if the expression is valid, otherwise return False
+ Returns:
+ Return True if the expression is valid, otherwise return False
- Example:
- if is_valid_expression("3d6 + 2"):
- result = roll_dice("3d6 + 2")
+ Example:
+ if is_valid_expression("3d6 + 2"):
+ result = roll_dice("3d6 + 2")
"""
...
def parse_expression(expression: str) -> Dict[str, Any]:
- """
- Parses expressions but not throwing
+ """
+ Parses expressions but not throwing
- Args:
- expression: The expression string to parse
+ Args:
+ expression: The expression string to parse
- Returns:
- Analytical results dictionary
+ Returns:
+ Analytical results dictionary
- Raises:
- ValueError: When the expression is invalid
+ Raises:
+ ValueError: When the expression is invalid
"""
...
class RollStatistics:
"""Throw statistics"""
+
min: int
max: int
mean: float
@@ -192,47 +190,48 @@ class RollStatistics:
results: List[int]
def roll_multiple(expression: str, times: int) -> RollHistory:
- """
- Throw the same expression multiple times
+ """
+ Throw the same expression multiple times
- Args:
- expression: dice expression string
- times: The number of throws must be greater than 0
+ Args:
+ expression: dice expression string
+ times: The number of throws must be greater than 0
- Returns:
- Throw result list
+ Returns:
+ Throw result list
- Raises:
- ValueError: When the parameter is invalid
+ Raises:
+ ValueError: When the parameter is invalid
- Example:
- results = roll_multiple("3d6", 10)
- totals = [r["total"] for r in results]
+ Example:
+ results = roll_multiple("3d6", 10)
+ totals = [r["total"] for r in results]
"""
...
def roll_statistics(expression: str, times: int) -> RollStatistics:
- """
- Statistics of multiple throws
+ """
+ Statistics of multiple throws
- Args:
- expression: dice expression string
- times: The number of throws must be greater than 0
+ Args:
+ expression: dice expression string
+ times: The number of throws must be greater than 0
- Returns:
- Object containing statistics
+ Returns:
+ Object containing statistics
- Raises:
- ValueError: When the parameter is invalid
+ Raises:
+ ValueError: When the parameter is invalid
- Example:
- stats = roll_statistics("3d6", 100)
- print(f"Average: {stats.mean:.2f}")
+ Example:
+ stats = roll_statistics("3d6", 100)
+ print(f"Average: {stats.mean:.2f}")
"""
...
class CommonRolls:
"""Commonly used dice expression constants"""
+
D20: str
D20_ADVANTAGE: str
D20_DISADVANTAGE: str
@@ -244,4 +243,4 @@ class CommonRolls:
HIT_POINTS_D6: str
HIT_POINTS_D8: str
HIT_POINTS_D10: str
- HIT_POINTS_D12: str \ No newline at end of file
+ HIT_POINTS_D12: str
diff --git a/src/oneroll/tui.py b/src/oneroll/tui.py
index 9e4e4c9..fa0eb09 100644
--- a/src/oneroll/tui.py
+++ b/src/oneroll/tui.py
@@ -16,91 +16,91 @@ from datetime import datetime
from . import OneRoll, roll, roll_statistics, CommonRolls
+
class RollResult(Message):
"""Throw result message"""
-
+
def __init__(self, result: Dict[str, Any], expression: str) -> None:
self.result = result
self.expression = expression
super().__init__()
+
class ExpressionInput(Input):
"""Expression input box"""
-
+
def __init__(self) -> None:
super().__init__(
- placeholder="输入骰子表达式,如 3d6 + 2",
- id="expression_input"
+ placeholder="输入骰子表达式,如 3d6 + 2", id="expression_input"
)
-
+
def on_key(self, event) -> None:
if event.key == "enter":
self.post_message(RollResult(roll(self.value), self.value))
self.value = ""
+
class QuickRollButton(Button):
"""Quick Throw Button"""
-
+
def __init__(self, label: str, expression: str) -> None:
self.expression = expression
super().__init__(label, id=f"quick_{expression}")
-
+
def on_button_pressed(self) -> None:
self.post_message(RollResult(roll(self.expression), self.expression))
+
class RollHistory(DataTable):
"""Throw History Table"""
-
+
def __init__(self) -> None:
super().__init__()
self.add_columns("时间", "表达式", "总点数", "详情")
-
+
def add_roll(self, result: Dict[str, Any], expression: str) -> None:
"""Add throw record"""
time_str = datetime.now().strftime("%H:%M:%S")
- self.add_row(
- time_str,
- expression,
- str(result["total"]),
- result["details"]
- )
+ self.add_row(time_str, expression, str(result["total"]), result["details"])
# keep table at bottom
self.scroll_end()
+
class StatisticsPanel(Static):
"""Statistics Panel"""
-
+
def __init__(self) -> None:
super().__init__("统计功能", id="stats_panel")
-
+
def show_statistics(self, expression: str, times: int = 100) -> None:
"""Show statistics information"""
try:
stats = roll_statistics(expression, times)
stats_text = f"""
-统计结果: {expression} (投掷 {stats['count']} 次)
+统计结果: {expression} (投掷 {stats["count"]} 次)
-最小值: {stats['min']}
-最大值: {stats['max']}
-平均值: {stats['mean']:.2f}
-总和: {stats['total']}
+最小值: {stats["min"]}
+最大值: {stats["max"]}
+平均值: {stats["mean"]:.2f}
+总和: {stats["total"]}
"""
self.update(stats_text)
except Exception as e:
self.update(f"统计错误: {e}")
+
class RollDisplay(Static):
- """The throwing result shows """
-
+ """The throwing result shows"""
+
def __init__(self) -> None:
super().__init__("等待投掷...", id="roll_display")
-
+
def show_result(self, result: Dict[str, Any], expression: str) -> None:
"""Display throwing result"""
total = result["total"]
details = result["details"]
rolls = result["rolls"]
-
+
# a kind of color selection based on result
if total >= 15:
color = "green"
@@ -108,7 +108,7 @@ class RollDisplay(Static):
color = "yellow"
else:
color = "red"
-
+
display_text = f"""[bold blue]🎲 {expression}[/bold blue]
[bold]总点数:[/bold] [bold {color}]{total}[/bold {color}]
@@ -119,13 +119,16 @@ class RollDisplay(Static):
# show the comment
comment = result.get("comment", "")
if comment:
- display_text += f"\n\n[bold]注释:[/bold] [italic blue]{comment}[/italic blue]"
-
+ display_text += (
+ f"\n\n[bold]注释:[/bold] [italic blue]{comment}[/italic blue]"
+ )
+
self.update(display_text)
+
class OneRollTUI(App):
"""OneRoll 终端用户界面"""
-
+
CSS = """
Screen {
layout: vertical;
@@ -163,14 +166,14 @@ class OneRollTUI(App):
margin: 1;
}
"""
-
+
def compose(self) -> ComposeResult:
"""UI components"""
yield Header()
-
+
with Container():
yield ExpressionInput()
-
+
with Horizontal(classes="quick_buttons"):
yield QuickRollButton("D20", CommonRolls.D20)
yield QuickRollButton("优势", CommonRolls.D20_ADVANTAGE)
@@ -178,35 +181,35 @@ class OneRollTUI(App):
yield QuickRollButton("属性", CommonRolls.ATTRIBUTE_ROLL)
yield QuickRollButton("3D6", "3d6")
yield QuickRollButton("2D6", "2d6")
-
+
yield RollDisplay()
-
+
with Tabs():
with Tab("历史记录", id="history_tab"):
yield RollHistory(id="history_table")
-
+
with Tab("统计", id="stats_tab"):
yield StatisticsPanel()
-
+
yield Footer()
-
+
def on_mount(self) -> None:
"""Initialization during interface mount"""
self.title = "OneRoll 骰子投掷器"
self.sub_title = "高性能骰子表达式解析器"
-
+
# Set focus to the input box
self.query_one(ExpressionInput).focus()
-
+
def on_roll_result(self, message: RollResult) -> None:
# display result
roll_display = self.query_one(RollDisplay)
roll_display.show_result(message.result, message.expression)
-
+
# add history
history_table = self.query_one(RollHistory)
history_table.add_roll(message.result, message.expression)
-
+
def on_key(self, event) -> None:
if event.key == "ctrl+q":
self.exit()
@@ -214,7 +217,7 @@ class OneRollTUI(App):
self.show_help()
elif event.key == "ctrl+s":
self.show_statistics()
-
+
def show_help(self) -> None:
help_text = """
OneRoll 骰子投掷器
@@ -237,15 +240,17 @@ OneRoll 骰子投掷器
• Ctrl+S - 显示统计
• Enter - 执行投掷
"""
-
+
self.notify(help_text, title="帮助信息", timeout=10)
-
+
def show_statistics(self) -> None:
self.notify("统计功能开发中...", title="统计")
+
def run_tui():
app = OneRollTUI()
app.run()
+
if __name__ == "__main__":
run_tui()