Create simple calculator
This commit is contained in:
@ -1,79 +1,34 @@
|
||||
# Agenten Plattform
|
||||
#
|
||||
# (c) 2024 Magnus Bender
|
||||
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
|
||||
# Universitaet Hamburg
|
||||
# https://www.chai.uni-hamburg.de/~bender
|
||||
#
|
||||
# (c) 2025 Magnus Bender
|
||||
#
|
||||
# source code released under the terms of GNU Public License Version 3
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
import re
|
||||
|
||||
from typing import Callable
|
||||
from ums.agent import ExtractTextAgent
|
||||
from ums.utils import RiddleData, ExtractedData, ExtractedContent
|
||||
|
||||
from ums.agent import ExtractAudioAgent, ExtractImageAgent, ExtractTextAgent
|
||||
from ums.utils import RiddleData, AgentMessage, ExtractedData, ExtractedContent, ExtractedPositions
|
||||
|
||||
class SimpleExtractAudioAgent(ExtractAudioAgent):
|
||||
# here we do not have an implementation for extracting audio,
|
||||
# normally, we would not have this class, but here as example
|
||||
|
||||
def handle(self, data: RiddleData) -> RiddleData:
|
||||
print("Audio Process:", data.file_plain)
|
||||
return data
|
||||
|
||||
class SimpleExtractImageAgent(ExtractImageAgent):
|
||||
# equally, we would not have this class without implementation
|
||||
|
||||
def before_response(self, response: AgentMessage, send_it: Callable[[], None]) -> bool:
|
||||
# agents are able to prevent sending response messages to the management
|
||||
# or send this messages later via `send_it()``
|
||||
|
||||
print("The response would be:", response)
|
||||
|
||||
# just stop the response from being sent
|
||||
return False
|
||||
|
||||
def handle(self, data: RiddleData) -> RiddleData:
|
||||
print("Image Process:", data.file_plain)
|
||||
return data
|
||||
|
||||
class SimpleExtractTextAgent(ExtractTextAgent):
|
||||
class CalculatorExtractVariables(ExtractTextAgent):
|
||||
|
||||
def handle(self, data: RiddleData) -> RiddleData:
|
||||
print("Text Process:", data.file_plain)
|
||||
|
||||
# here we extract the variables assigned with numbers
|
||||
found = False
|
||||
# here we extract the variables assigned with numbers in the .txt files
|
||||
with open(data.file_plain) as f:
|
||||
for i, line in enumerate(f):
|
||||
if "=" in line:
|
||||
match = re.match(r"([a-z]{1})\s*=\s*(\d+)", line.strip())
|
||||
if not match is None:
|
||||
variable = match.group(1)
|
||||
value = int(match.group(2))
|
||||
found = True
|
||||
line_no = i
|
||||
if found:
|
||||
extracted = ExtractedData(
|
||||
contents=[
|
||||
ExtractedContent(type="variable",content=variable),
|
||||
ExtractedContent(type="value",content=value)
|
||||
],
|
||||
positions=[
|
||||
ExtractedPositions(type="line",position=line_no),
|
||||
ExtractedPositions(type="line",position=line_no)
|
||||
],
|
||||
other={
|
||||
"variable" : variable,
|
||||
"value" : value
|
||||
}
|
||||
)
|
||||
data.file_extracted = self.store_extracted(data, extracted)
|
||||
|
||||
for line in f:
|
||||
match = re.match(r"([a-z]{1})\s*=\s*(\d+)", line.strip())
|
||||
if not match is None:
|
||||
variable, value = match.group(1), int(match.group(2))
|
||||
|
||||
data.file_extracted = self.store_extracted(data, ExtractedData(
|
||||
contents=[
|
||||
ExtractedContent(type="variable", content=variable),
|
||||
ExtractedContent(type="value", content=value)
|
||||
]
|
||||
))
|
||||
return data
|
||||
|
||||
|
||||
AGENT_CLASSES = [
|
||||
SimpleExtractAudioAgent, SimpleExtractImageAgent, SimpleExtractTextAgent
|
||||
CalculatorExtractVariables
|
||||
]
|
@ -1,101 +1,57 @@
|
||||
# Agenten Plattform
|
||||
#
|
||||
# (c) 2024 Magnus Bender
|
||||
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
|
||||
# Universitaet Hamburg
|
||||
# https://www.chai.uni-hamburg.de/~bender
|
||||
# (c) 2025 Magnus Bender
|
||||
#
|
||||
# source code released under the terms of GNU Public License Version 3
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
import re
|
||||
|
||||
import re, random
|
||||
|
||||
from typing import Callable
|
||||
from typing import List
|
||||
|
||||
from ums.agent import SolveAgent
|
||||
from ums.utils import Riddle, RiddleData, RiddleSolution, RiddleDataType, AgentMessage
|
||||
from ums.utils import Riddle, RiddleData, RiddleSolution
|
||||
|
||||
class SimpleSolveAgent(SolveAgent):
|
||||
class CalculatorSolveExpression(SolveAgent):
|
||||
|
||||
def before_response(self, response: AgentMessage, send_it: Callable[[], None]) -> bool:
|
||||
# do not send a response, if this is not a calculator riddle!
|
||||
return not self.stop_response
|
||||
|
||||
def handle(self, riddle: Riddle, data: RiddleData) -> RiddleSolution:
|
||||
def handle(self, riddle: Riddle, data: List[RiddleData]) -> RiddleSolution:
|
||||
# remove whitespace
|
||||
expression = riddle.question.strip()
|
||||
|
||||
# this is a very simple calculator, if the riddle it not for the calculator
|
||||
# just do not try to solve it and do not answer management!
|
||||
if "[Taschenrechner]" in riddle.context:
|
||||
self.stop_response = False
|
||||
# get all the extracted values
|
||||
var_vals = {}
|
||||
for d in data:
|
||||
e = self.get_extracted(d)
|
||||
if not e is None \
|
||||
and "variable" in e.other and "value" in e.other \
|
||||
and e.other["variable"] in expression:
|
||||
var_vals[e.other["variable"]] = e.other["value"]
|
||||
|
||||
# get all the extracted values
|
||||
var_vals = {}
|
||||
used_data = []
|
||||
for d in data:
|
||||
e = self.get_extracted(d)
|
||||
if not e is None \
|
||||
and "variable" in e.other and "value" in e.other \
|
||||
and e.other["variable"] in expression:
|
||||
used_data.append(d)
|
||||
var_vals[e.other["variable"]] = e.other["value"]
|
||||
# require "=" at the end
|
||||
if not expression[-1] == "=":
|
||||
return RiddleSolution(solution="Error", explanation="No = at the end of the expression!")
|
||||
|
||||
# solve the expression
|
||||
# remove the = and whitespace
|
||||
expression = expression[:-1].strip()
|
||||
|
||||
# require "=" at the end
|
||||
if not expression[-1] == "=":
|
||||
return RiddleSolution(solution="Error", explanation="No = at the end of the expression!")
|
||||
|
||||
# solve the expression
|
||||
# remove the = and whitespace
|
||||
expression = expression[:-1].strip()
|
||||
for var, val in var_vals.items():
|
||||
# replace the variables by values
|
||||
expression = expression.replace(var, str(val))
|
||||
|
||||
for var, val in var_vals.items():
|
||||
# replace the variables by values
|
||||
expression = expression.replace(var, str(val))
|
||||
# check the expression
|
||||
if re.match(r"^[0-9+\-*\/ ]+$", expression) is None:
|
||||
return RiddleSolution(solution="Error", explanation="Missing data or faulty expression")
|
||||
|
||||
# check the expression
|
||||
if re.match(r"^[0-9+\-*\/ ]+$", expression) is None:
|
||||
return RiddleSolution(solution="Error", explanation="Missing data or faulty expression")
|
||||
|
||||
try:
|
||||
# using eval is a bad idea, but this is only for demonstration
|
||||
# and expression may only contain "0-9 +-*/"
|
||||
result = eval(expression)
|
||||
except:
|
||||
return RiddleSolution(solution="Error", explanation="Unable to calculate value of expression")
|
||||
|
||||
# add some noise and invalidate result (for gatekeeper to check)
|
||||
if random.random() > 0.5:
|
||||
print("UPPS UPPS")
|
||||
result += 1 + int(random.random()*9)
|
||||
|
||||
return RiddleSolution(
|
||||
solution=str(result),
|
||||
explanation="{} = {}".format(expression, result),
|
||||
used_data=used_data
|
||||
)
|
||||
|
||||
else:
|
||||
self.stop_response = True
|
||||
|
||||
# but we will start a nice riddle, we can solve :D
|
||||
self.sub_riddle(
|
||||
riddle=Riddle(
|
||||
context="[Taschenrechner]",
|
||||
question="x * x ="
|
||||
),
|
||||
data=[
|
||||
RiddleData(
|
||||
type=RiddleDataType.TEXT,
|
||||
file_plain="./example/x.txt"
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
return RiddleSolution(solution="Error", explanation="No context [Taschenrechner]!")
|
||||
|
||||
try:
|
||||
# using eval is a usually bad idea, but this is only for demonstration
|
||||
# and expression may only contain "0-9 +-*/"
|
||||
result = eval(expression)
|
||||
except:
|
||||
return RiddleSolution(solution="Error", explanation="Unable to calculate value of expression")
|
||||
|
||||
return RiddleSolution(solution=str(result), explanation="{} = {}".format(expression, result))
|
||||
|
||||
AGENT_CLASSES = [
|
||||
SimpleSolveAgent
|
||||
CalculatorSolveExpression
|
||||
]
|
@ -1,71 +1,50 @@
|
||||
# Agenten Plattform
|
||||
#
|
||||
# (c) 2024 Magnus Bender
|
||||
# Institute of Humanities-Centered Artificial Intelligence (CHAI)
|
||||
# Universitaet Hamburg
|
||||
# https://www.chai.uni-hamburg.de/~bender
|
||||
# (c) 2025 Magnus Bender
|
||||
#
|
||||
# source code released under the terms of GNU Public License Version 3
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
import re
|
||||
from typing import Callable, List
|
||||
from typing import List
|
||||
|
||||
from ums.agent import GatekeeperAgent
|
||||
from ums.utils import Riddle, RiddleSolution, AgentMessage
|
||||
from ums.utils import Riddle, RiddleSolution
|
||||
|
||||
|
||||
class SimpleSolveAgent(GatekeeperAgent):
|
||||
|
||||
def before_response(self, response: AgentMessage, send_it: Callable[[], None]) -> bool:
|
||||
# do not send a response, if this is not a calculator riddle!
|
||||
return not self.stop_response
|
||||
class CalculatorCheckResult(GatekeeperAgent):
|
||||
|
||||
def handle(self, solution: List[RiddleSolution], riddle: Riddle) -> List[RiddleSolution]:
|
||||
self.stop_response = False
|
||||
# iterate over all poss. solutions
|
||||
return [self._check_single_solution(s) for s in solution]
|
||||
|
||||
|
||||
def _check_single_solution(self, solution:RiddleSolution) -> RiddleSolution:
|
||||
# first check for errors
|
||||
if solution.solution == "Error":
|
||||
solution.accepted = True
|
||||
solution.review = "An error of the riddle can not be fixed!"
|
||||
return solution
|
||||
|
||||
# this is just a simple check, we check if solution and explanation match to the expression
|
||||
|
||||
match = re.match(r"^([0-9+\-*\/ ]+)\s*=\s*(\d+)$", solution.explanation)
|
||||
if match is None:
|
||||
self.stop_response = True
|
||||
return solution
|
||||
return self._update_solution(solution, False, "Invalid math expression")
|
||||
|
||||
expression, result = match.group(1), match.group(2)
|
||||
|
||||
if result != solution.solution:
|
||||
solution.accepted = False
|
||||
solution.review = "Inconsistent values"
|
||||
return solution
|
||||
|
||||
return self._update_solution(solution, False, "Inconsistent values")
|
||||
|
||||
try:
|
||||
# using eval is a bad idea, but this is only for demonstration
|
||||
# and expression may only contain "0-9 +-*/"
|
||||
own_result = eval(expression)
|
||||
except:
|
||||
solution.accepted = False
|
||||
solution.review = "Unsolvable expression"
|
||||
return solution
|
||||
return self._update_solution(solution, False, "Unsolvable expression")
|
||||
|
||||
# check the values
|
||||
if str(own_result) != solution.solution:
|
||||
solution.accepted = False
|
||||
solution.review = "Value of expression does not match solution!"
|
||||
return self._update_solution(solution, False, "Value of expression does not match solution!")
|
||||
else:
|
||||
solution.accepted = True
|
||||
solution.review = "Yes, {}".format(solution.explanation)
|
||||
|
||||
return solution
|
||||
return self._update_solution(solution, True, "Yes, {}".format(solution.explanation))
|
||||
|
||||
def _update_solution(self, solution:RiddleSolution, accepted:bool, review:str="") -> RiddleSolution:
|
||||
solution.accepted = accepted
|
||||
solution.review = review
|
||||
return solution
|
||||
|
||||
AGENT_CLASSES = [
|
||||
SimpleSolveAgent
|
||||
CalculatorCheckResult
|
||||
]
|
Reference in New Issue
Block a user