Dat1710's picture
Upload folder using huggingface_hub
00db46c verified
import random
import re
from dataclasses import dataclass
from enum import Enum
def check_valid_arithmetic_expression(expression: str, result: int) -> bool:
"""
Check if a string is a valid arithmetic expression.
With format
num1 op1 num2 op2 num3 op3 num4
with operators +, -, *, /
Args:
expression: The expression to check
Returns:
bool: True if valid arithmetic expression, False otherwise
"""
# Regex pattern for: number operator number operator number operator number
# Number can only be positive integer (no negative numbers)
# Operators are +, -, *, /
# Spacing between numbers and operators is optional
pattern = (
r"^\s*(\d+)\s*([+\-*/])\s*(\d+)\s*([+\-*/])\s*(\d+)\s*([+\-*/])\s*(\d+)\s*$"
)
if bool(re.match(pattern, expression)):
return eval(expression) == result
return False
@dataclass
class ArithmeticProblem:
num_1: int
num_2: int
num_3: int
num_4: int
op1: str
op2: str
op3: str
expression: str
result: int
class Mode(Enum):
ALL = "all" # All operators are allowed
MUL_DIV = "mul_div" # Only multiplication and division are allowed
class ArithmeticProblemGenerator:
def __init__(
self,
min_num: int = 1,
max_num: int = 100,
result_min: int = 1,
result_max: int = 1000,
max_attempts: int = 100,
operators: tuple[str] = ("+", "-", "*", "/"),
mode: Mode = Mode.ALL,
):
"""
Initialize the arithmetic problem generator.
Args:
min_num: The minimum number to use in the arithmetic problem
max_num: The maximum number to use in the arithmetic problem
operators: The operators to use in the arithmetic problem
mode: The mode of the arithmetic problem
"""
self.min_num = min_num
self.max_num = max_num
self.result_min = result_min
self.result_max = result_max
self.operators = operators
self.max_attempts = max_attempts
self.mode = mode
def _generate_random_number(self) -> int:
return random.randint(self.min_num, self.max_num)
def _generate_random_operator(self) -> str:
return random.choice(self.operators)
def generate_problem(self) -> ArithmeticProblem:
"""
Generate an countdown arithmetic problem.
Generate four numbers, num_1, num_2, num_3, num_4,
and operators between them, and apply the operators to the numbers to get the result.
Make sure that the result must exactly be an integer, and
match the result of the arithmetic problem.
Returns:
ArithmeticProblem: The generated arithmetic problem
"""
max_attempts = 1_000
for _ in range(max_attempts):
# Generate four random numbers
num_1 = self._generate_random_number()
num_2 = self._generate_random_number()
num_3 = self._generate_random_number()
num_4 = self._generate_random_number()
# Generate three random operators for the expression: num_1 op1 num_2 op2 num_3 op3 num_4
op1 = self._generate_random_operator()
op2 = self._generate_random_operator()
op3 = self._generate_random_operator()
if (
self.mode == Mode.MUL_DIV
and op1 not in ("*", "/")
and op2 not in ("*", "/")
and op3 not in ("*", "/")
):
continue
# Try to evaluate the expression and ensure it results in an integer
result = self._evaluate_expression(
num_1, op1, num_2, op2, num_3, op3, num_4
)
# Check if result is an integer (no floating point remainder)
if (
isinstance(result, (int, float))
and result == int(result)
and self.result_min <= result <= self.result_max
):
result = int(result)
return ArithmeticProblem(
num_1=num_1,
num_2=num_2,
num_3=num_3,
num_4=num_4,
op1=op1,
op2=op2,
op3=op3,
expression=f"{num_1} {op1} {num_2} {op2} {num_3} {op3} {num_4}",
result=result,
)
return None
def _evaluate_expression(
self,
num_1: int,
op1: str,
num_2: int,
op2: str,
num_3: int,
op3: str,
num_4: int,
) -> float:
"""
Evaluate the arithmetic expression following standard order of operations.
Args:
num_1: First number
op1: First operator
num_2: Second number
op2: Second operator
num_3: Third number
op3: Third operator
num_4: Fourth number
Returns:
float: The result of the arithmetic expression
"""
# Build expression string: num_1 op1 num_2 op2 num_3 op3 num_4
expression = f"{num_1} {op1} {num_2} {op2} {num_3} {op3} {num_4}"
# Use eval to calculate the result (following Python's order of operations)
# This handles operator precedence correctly (* and / before + and -)
return eval(expression)
class ArithmeticProblemDescriptionGenerator:
def __init__(self):
"""Initialize the description generator with various problem templates."""
self.problem_templates = [
# Direct challenge templates
"Using the numbers {num_1}, {num_2}, {num_3}, and {num_4}, create an expression that equals {result}. You can only use +, -, x, and / operators.",
"Can you make {result} using {num_1}, {num_2}, {num_3}, and {num_4}? Use only +, -, x, and / operators.",
"Find a way to combine {num_1}, {num_2}, {num_3}, and {num_4} to get {result} using only +, -, x, and / operators.",
"Use all four numbers ({num_1}, {num_2}, {num_3}, {num_4}) to make {result}. Only +, -, x, and / operators are allowed.",
# Instructional templates
"Given the numbers {num_1}, {num_2}, {num_3}, and {num_4}, arrange them with +, -, x, and / operators to achieve {result}.",
"Your task: Use {num_1}, {num_2}, {num_3}, and {num_4} exactly once each with only +, -, x, and / operators to create an expression equal to {result}.",
"Problem: How can you use the four numbers {num_1}, {num_2}, {num_3}, {num_4} with +, -, x, and / operators to get {result}?",
# Additional templates with operator emphasis
"Create a mathematical expression using {num_1}, {num_2}, {num_3}, and {num_4} that equals {result}. Only basic arithmetic operators (+, -, x, /) are permitted.",
"Arrange {num_1}, {num_2}, {num_3}, and {num_4} with +, -, x, and / to make {result}.",
"Using only addition (+), subtraction (-), multiplication (x), and division (/), combine {num_1}, {num_2}, {num_3}, and {num_4} to equal {result}.",
]
def generate_description(self, problem: ArithmeticProblem) -> tuple[str, int]:
"""
Generate a problem description for the given arithmetic problem.
Args:
problem: The ArithmeticProblem to generate description for
Returns:
tuple[str, int]: A tuple containing (problem_description, result)
"""
# Select random template
problem_template = random.choice(self.problem_templates)
# Generate the problem description
problem_description = problem_template.format(
num_1=problem.num_1,
num_2=problem.num_2,
num_3=problem.num_3,
num_4=problem.num_4,
result=problem.result,
)
return problem_description, problem.result