File size: 2,637 Bytes
8a69b92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"""2. Reader + router: what is asked, and how the extracted values fit together.

PLACEHOLDER: keyword rules standing in for the TinyLLM, which will read the same marked text and return the same
choices once it is trained.

    read("A car at <speed1> brakes at <acceleration>. How long until it stops?")
    -> {"target": "t", "order": ("u",), "sign": -1, "u0": False, "v0": True, "a_source": None}

Returns None when the question isn't one it can answer.
"""

import re

# what is asked, by priority: the first cue found picks the target
TARGET_CUES = {
    "s": r"how far|distance|how much (slip )?road",
    "t": r"how long|\btime\b",
    "v": r"how fast|\bspeed\b",
    "F": r"\bforce\b",
    "a": r"how hard|acceleration|deceleration",
}
OFF_TOPIC = r"\b(fuel|engine|legal|tyres?|insurance|poem|gear)\b"
REST = r"from (rest|a standstill|stationary)|pulls? away"
STOP = r"\bstop|stationary|\bahead\b"  # "a dog 30 m ahead": stop before it
BRAKE = r"\bbrak|\bstop|decel"
CONSTANT = r"constant|cruis|steady"
FINAL = r"(\bto|reach\w*) <speed1>"  # a lone speed that is where the car ends up
SURFACE_DECEL = {"icy": 2.0, "wet": 5.0, "dry": 7.5}  # typical braking on each road, m/s²
SURFACE_CUES = {"icy": r"\bic[ey]|snow", "wet": r"rain|\bwet\b"}


def read(marked):
    text = marked.lower()
    words = re.sub(r"<\w+>", "#", text)  # cues are words, never the value markers ("<time>" isn't "time")
    target = next((t for t, cue in TARGET_CUES.items() if re.search(cue, words)), None)
    if target is None or re.search(OFF_TOPIC, words):
        return None
    speeds = text.count("<speed")
    order = {0: (), 1: ("v",) if re.search(FINAL, text) else ("u",), 2: ("u", "v")}.get(speeds)
    if order is None:
        return None
    braking = bool(re.search(BRAKE, text))
    v0 = target != "v" and "v" not in order and bool(re.search(STOP, text))
    u0 = "u" not in order and bool(re.search(REST, text))
    # with no acceleration or force given: cruising means a = 0, braking means the road's typical deceleration,
    # unless the speeds, time and distance already fix it
    a_source = None
    kinematics = len(order) + u0 + v0 + ("<time>" in text) + ("<distance>" in text)
    if "<acceleration>" not in text and "<force>" not in text and target != "a" and kinematics < 3:
        if re.search(CONSTANT, text):
            a_source = 0.0
        elif braking:
            surface = next((s for s, cue in SURFACE_CUES.items() if re.search(cue, text)), "dry")
            a_source = SURFACE_DECEL[surface]
    return {"target": target, "order": order, "sign": -1 if braking else 1, "u0": u0, "v0": v0, "a_source": a_source}