Spaces:
Sleeping
Sleeping
File size: 5,189 Bytes
3aba62e b255e59 2342c10 5b6725d b255e59 5b6725d 2342c10 3aba62e 5b6725d 3aba62e b255e59 3aba62e b255e59 3aba62e b255e59 3aba62e b255e59 2342c10 3aba62e 2342c10 b255e59 5b6725d b255e59 3aba62e b255e59 3aba62e b255e59 5b6725d 3aba62e 5b6725d 3aba62e b255e59 5b6725d 2342c10 3aba62e b255e59 3aba62e b255e59 5b6725d 3aba62e b255e59 5b6725d 3aba62e b255e59 3aba62e b255e59 3aba62e b255e59 3aba62e b255e59 5b6725d b255e59 | 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | """
MycoController — autonomous AI agent that drives background game actions.
The controller uses engine._llm() to decide what to do, then delegates
execution to the same engine functions the player uses.
"""
from . import engine
import json
class MycoController:
def __init__(self):
self.position = [1, 1]
self.history = []
# ------------------------------------------------------------------
# Decision layer — ask Gemma what to do next
# ------------------------------------------------------------------
def get_agent_decision(self, current_mushroom, collection) -> dict:
# Build a full context so Gemma has score/health/mystery data.
ctx = engine._ctx(current_mushroom, list(collection or []))
mushroom_line = (
f"There is a {current_mushroom.get('rarity','?')} mushroom called "
f"{current_mushroom.get('name','?')} in the clearing."
if current_mushroom
else "The clearing is empty."
)
prompt = (
f"You are Myco, an autonomous forest agent. "
f"Current position: {self.position}. {mushroom_line} "
f"Collection count: {ctx.get('collection_count', 0)}. "
f"Score: {ctx.get('score', 0)} spores. Health: {ctx.get('health', 3)}/3. "
f"Mystery chapter: {ctx.get('mystery_title', 'The Wrong Memory')}. "
"Decide the single best next action from: move, search, study, collect, wait. "
"If the clearing is empty, prefer 'search'. "
"If a mushroom is present and unstudied, prefer 'study'. "
"If studied and safe, prefer 'collect'. "
"If move, pick an adjacent grid coordinate within [0,2] x [0,2]. "
'Respond ONLY in valid JSON: {"action":"...","target":[x,y],"thought":"..."}'
)
response = engine._llm(prompt, ctx)
if not response:
print("[Myco Controller] LLM returned no data — defaulting to wait.")
return {"action": "wait", "target": None, "thought": "Engine returned no data."}
try:
parsed = json.loads(response)
action = parsed.get("action", "wait")
if action not in {"move", "search", "study", "collect", "wait"}:
raise ValueError(f"Unknown action: {action!r}")
return parsed
except Exception as exc:
print(f"[Myco Controller] Parse error: {exc} — raw: {response!r}")
return {"action": "wait", "target": None, "thought": "Failed to parse response."}
# ------------------------------------------------------------------
# Execution layer — carry out the decision using engine functions
# ------------------------------------------------------------------
def run_tick(self, current_mushroom, collection) -> dict:
"""
Run one autonomous tick. Returns a result dict with:
action_taken, thought, and optionally data (new current/collection).
This is the ONLY entry point the UI should call — it keeps controller
state (self.history, self.position) in sync.
"""
decision = self.get_agent_decision(current_mushroom, collection)
action = decision.get("action", "wait")
result = {
"action_taken": action,
"thought": decision.get("thought", ""),
"data": {},
}
if action == "move":
target = decision.get("target")
if isinstance(target, list) and len(target) == 2:
x = max(0, min(2, int(target[0])))
y = max(0, min(2, int(target[1])))
self.position = [x, y]
result["data"]["position"] = self.position
else:
result["thought"] = "Move had no valid target — staying put."
elif action == "search":
mushroom, current, history = engine.discover_mushroom(list(collection or []))
self.history = history
result["data"] = {
"mushroom": mushroom,
"current": current,
"history": history,
}
elif action == "study":
if current_mushroom:
reply, history = engine.study_current(current_mushroom, self.history)
self.history = history
result["data"] = {
"study_reply": reply,
"history": history,
}
else:
result["thought"] = "Nothing to study — clearing is empty."
elif action == "collect":
if current_mushroom:
coll, history = engine.collect_current(
current_mushroom, list(collection or []), self.history
)
self.history = history
result["data"] = {
"collection": coll,
"history": history,
}
else:
result["thought"] = "Nothing to collect — clearing is empty."
# action == "wait" needs no execution
return result
|