Spaces:
No application file
No application file
Upload 39 files
Browse files- .gitattributes +3 -0
- v2/utils/__pycache__/block_relation_builder.cpython-312.pyc +3 -0
- v2/utils/__pycache__/plan_generator_13.cpython-312.pyc +3 -0
- v2/utils/action_node.py +452 -0
- v2/utils/action_plan_sample.txt +1577 -0
- v2/utils/agent.py +1647 -0
- v2/utils/agent2.py +259 -0
- v2/utils/all_analysis.txt +0 -0
- v2/utils/all_analysis_trace.txt +10 -0
- v2/utils/all_generated_blocks.json +78 -0
- v2/utils/block_builder_main.py +446 -0
- v2/utils/block_correcter.py +143 -0
- v2/utils/block_function.py +1147 -0
- v2/utils/block_function_v1.py +759 -0
- v2/utils/block_naming.py +62 -0
- v2/utils/block_relation_builder.py +0 -0
- v2/utils/block_relation_builder_v2.py +0 -0
- v2/utils/block_var_setter.py +290 -0
- v2/utils/generated_output_json.json +87 -0
- v2/utils/half_working_plan.py +0 -0
- v2/utils/helper_function.py +0 -0
- v2/utils/logs.txt +6 -0
- v2/utils/opcode_counter.py +228 -0
- v2/utils/opcode_occurrence.py +981 -0
- v2/utils/plan_generator.py +0 -0
- v2/utils/plan_generator_10.py +0 -0
- v2/utils/plan_generator_11.py +0 -0
- v2/utils/plan_generator_12.py +0 -0
- v2/utils/plan_generator_13.py +0 -0
- v2/utils/plan_generator_2.py +0 -0
- v2/utils/plan_generator_3.py +0 -0
- v2/utils/plan_generator_4.py +0 -0
- v2/utils/plan_generator_5.py +0 -0
- v2/utils/plan_generator_6.py +0 -0
- v2/utils/plan_generator_7.py +0 -0
- v2/utils/plan_generator_8.py +0 -0
- v2/utils/plan_generator_9.py +0 -0
- v2/utils/pseudo_exampls.txt +618 -0
- v2/utils/script_plan.py +1449 -0
- v2/utils/testing.ipynb +3 -0
.gitattributes
CHANGED
@@ -42,3 +42,6 @@ scratch_VLM/game_samples/Pong[[:space:]]Game.sb3 filter=lfs diff=lfs merge=lfs -
|
|
42 |
scratch_VLM/scratch_agent/uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text
|
43 |
v2/scratch_agent/static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png filter=lfs diff=lfs merge=lfs -text
|
44 |
v2/scratch_agent/uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
42 |
scratch_VLM/scratch_agent/uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text
|
43 |
v2/scratch_agent/static/assets/backdrops/ef9973bcff6d4cbc558e946028ec7d23.png filter=lfs diff=lfs merge=lfs -text
|
44 |
v2/scratch_agent/uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text
|
45 |
+
v2/utils/__pycache__/block_relation_builder.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
46 |
+
v2/utils/__pycache__/plan_generator_13.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
47 |
+
v2/utils/testing.ipynb filter=lfs diff=lfs merge=lfs -text
|
v2/utils/__pycache__/block_relation_builder.cpython-312.pyc
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9c686438dd391592343c2837c7eb952141313d9a15e99bc26f3783b1385a9f91
|
3 |
+
size 111900
|
v2/utils/__pycache__/plan_generator_13.cpython-312.pyc
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:05eb37cdab6c26c54f57ea851420ddb9b3bdb4656ce47fd43d4942fba6488b76
|
3 |
+
size 114434
|
v2/utils/action_node.py
ADDED
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import uuid
|
3 |
+
import logging
|
4 |
+
from typing import Dict, Any, List
|
5 |
+
|
6 |
+
# Assume GameState is a TypedDict or similar for clarity
|
7 |
+
# from typing import TypedDict
|
8 |
+
# class GameState(TypedDict):
|
9 |
+
# description: str
|
10 |
+
# project_json: Dict[str, Any]
|
11 |
+
# action_plan: Dict[str, Any]
|
12 |
+
# sprite_initial_positions: Dict[str, Any]
|
13 |
+
|
14 |
+
# Placeholder for actual GameState in your application
|
15 |
+
GameState = Dict[str, Any]
|
16 |
+
|
17 |
+
logger = logging.getLogger(__name__)
|
18 |
+
|
19 |
+
# --- Mock Agent for demonstration ---
|
20 |
+
class MockAgent:
|
21 |
+
def invoke(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
22 |
+
"""
|
23 |
+
Mocks an LLM agent invocation. In a real scenario, this would call your
|
24 |
+
actual LLM API (e.g., through LangChain, LlamaIndex, etc.).
|
25 |
+
"""
|
26 |
+
user_message = payload["messages"][-1]["content"]
|
27 |
+
print(f"\n--- Mock Agent Received Prompt (partial) ---\n{user_message[:500]}...\n------------------------------------------")
|
28 |
+
|
29 |
+
# Simplified mock responses for demonstration purposes
|
30 |
+
# In a real scenario, the LLM would generate actual Scratch block JSON
|
31 |
+
if "Propose a high-level action flow" in user_message:
|
32 |
+
return {
|
33 |
+
"messages": [{
|
34 |
+
"content": json.dumps({
|
35 |
+
"action_overall_flow": {
|
36 |
+
"Sprite1": {
|
37 |
+
"description": "Basic movement and interaction",
|
38 |
+
"plans": [
|
39 |
+
{
|
40 |
+
"event": "when flag clicked",
|
41 |
+
"logic": "forever loop: move 10 steps, if touching Edge then turn 15 degrees"
|
42 |
+
},
|
43 |
+
{
|
44 |
+
"event": "when space key pressed",
|
45 |
+
"logic": "say Hello! for 2 seconds"
|
46 |
+
}
|
47 |
+
]
|
48 |
+
},
|
49 |
+
"Ball": {
|
50 |
+
"description": "Simple bouncing behavior",
|
51 |
+
"plans": [
|
52 |
+
{
|
53 |
+
"event": "when flag clicked",
|
54 |
+
"logic": "move 5 steps, if on edge bounce"
|
55 |
+
}
|
56 |
+
]
|
57 |
+
}
|
58 |
+
}
|
59 |
+
})
|
60 |
+
}]
|
61 |
+
}
|
62 |
+
elif "You are an AI assistant generating Scratch 3.0 block JSON" in user_message:
|
63 |
+
# This mock response is highly simplified. A real LLM would generate
|
64 |
+
# valid Scratch blocks based on the provided relevant catalog and plan.
|
65 |
+
# We're just demonstrating the *mechanism* of filtering the catalog.
|
66 |
+
if "Sprite1" in user_message:
|
67 |
+
return {
|
68 |
+
"messages": [{
|
69 |
+
"content": json.dumps({
|
70 |
+
f"block_id_{generate_block_id()}": {
|
71 |
+
"opcode": "event_whenflagclicked",
|
72 |
+
"next": f"block_id_{generate_block_id()}_forever",
|
73 |
+
"parent": None,
|
74 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 100, "y": 100
|
75 |
+
},
|
76 |
+
f"block_id_{generate_block_id()}_forever": {
|
77 |
+
"opcode": "control_forever",
|
78 |
+
"next": None,
|
79 |
+
"parent": f"block_id_{generate_block_id()}",
|
80 |
+
"inputs": {
|
81 |
+
"SUBSTACK": [2, f"block_id_{generate_block_id()}_move"]
|
82 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
83 |
+
},
|
84 |
+
f"block_id_{generate_block_id()}_move": {
|
85 |
+
"opcode": "motion_movesteps",
|
86 |
+
"next": f"block_id_{generate_block_id()}_if",
|
87 |
+
"parent": f"block_id_{generate_block_id()}_forever",
|
88 |
+
"inputs": {
|
89 |
+
"STEPS": [1, [4, "10"]]
|
90 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
91 |
+
},
|
92 |
+
f"block_id_{generate_block_id()}_if": {
|
93 |
+
"opcode": "control_if",
|
94 |
+
"next": None,
|
95 |
+
"parent": f"block_id_{generate_block_id()}_forever",
|
96 |
+
"inputs": {
|
97 |
+
"CONDITION": [2, f"block_id_{generate_block_id()}_touching"],
|
98 |
+
"SUBSTACK": [2, f"block_id_{generate_block_id()}_turn"]
|
99 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
100 |
+
},
|
101 |
+
f"block_id_{generate_block_id()}_touching": {
|
102 |
+
"opcode": "sensing_touchingobject",
|
103 |
+
"next": None,
|
104 |
+
"parent": f"block_id_{generate_block_id()}_if",
|
105 |
+
"inputs": {
|
106 |
+
"TOUCHINGOBJECTMENU": [1, f"block_id_{generate_block_id()}_touching_menu"]
|
107 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
108 |
+
},
|
109 |
+
f"block_id_{generate_block_id()}_touching_menu": {
|
110 |
+
"opcode": "sensing_touchingobjectmenu",
|
111 |
+
"next": None,
|
112 |
+
"parent": f"block_id_{generate_block_id()}_touching",
|
113 |
+
"inputs": {},
|
114 |
+
"fields": {"TOUCHINGOBJECTMENU": ["_edge_", None]},
|
115 |
+
"shadow": True, "topLevel": False
|
116 |
+
},
|
117 |
+
f"block_id_{generate_block_id()}_turn": {
|
118 |
+
"opcode": "motion_turnright",
|
119 |
+
"next": None,
|
120 |
+
"parent": f"block_id_{generate_block_id()}_if",
|
121 |
+
"inputs": {
|
122 |
+
"DEGREES": [1, [4, "15"]]
|
123 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
124 |
+
},
|
125 |
+
f"block_id_{generate_block_id()}_say": {
|
126 |
+
"opcode": "looks_sayforsecs",
|
127 |
+
"next": None,
|
128 |
+
"parent": None, # This block would typically be part of a separate script
|
129 |
+
"inputs": {
|
130 |
+
"MESSAGE": [1, [10, "Hello!"]],
|
131 |
+
"SECS": [1, [4, "2"]]
|
132 |
+
}, "fields": {}, "shadow": False, "topLevel": True, "x": 300, "y": 100
|
133 |
+
}
|
134 |
+
})
|
135 |
+
}]
|
136 |
+
}
|
137 |
+
elif "Ball" in user_message:
|
138 |
+
return {
|
139 |
+
"messages": [{
|
140 |
+
"content": json.dumps({
|
141 |
+
f"block_id_{generate_block_id()}": {
|
142 |
+
"opcode": "event_whenflagclicked",
|
143 |
+
"next": f"block_id_{generate_block_id()}_moveball",
|
144 |
+
"parent": None,
|
145 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 100, "y": 100
|
146 |
+
},
|
147 |
+
f"block_id_{generate_block_id()}_moveball": {
|
148 |
+
"opcode": "motion_movesteps",
|
149 |
+
"next": f"block_id_{generate_block_id()}_edgebounce",
|
150 |
+
"parent": f"block_id_{generate_block_id()}",
|
151 |
+
"inputs": {
|
152 |
+
"STEPS": [1, [4, "5"]]
|
153 |
+
}, "fields": {}, "shadow": False, "topLevel": False
|
154 |
+
},
|
155 |
+
f"block_id_{generate_block_id()}_edgebounce": {
|
156 |
+
"opcode": "motion_ifonedgebounce",
|
157 |
+
"next": None,
|
158 |
+
"parent": f"block_id_{generate_block_id()}_moveball",
|
159 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": False
|
160 |
+
}
|
161 |
+
})
|
162 |
+
}]
|
163 |
+
}
|
164 |
+
return {"messages": [{"content": "[]"}]} # Default empty response
|
165 |
+
|
166 |
+
agent = MockAgent()
|
167 |
+
|
168 |
+
# Helper function to generate a unique block ID
|
169 |
+
def generate_block_id():
|
170 |
+
return str(uuid.uuid4())[:10].replace('-', '') # Shorten for readability, ensure uniqueness
|
171 |
+
|
172 |
+
# Placeholder for your extract_json_from_llm_response function
|
173 |
+
def extract_json_from_llm_response(response_string):
|
174 |
+
try:
|
175 |
+
# Assuming the LLM response is ONLY the JSON string within triple backticks
|
176 |
+
json_match = response_string.strip().replace("```json", "").replace("```", "").strip()
|
177 |
+
return json.loads(json_match)
|
178 |
+
except json.JSONDecodeError as e:
|
179 |
+
logger.error(f"Failed to decode JSON from LLM response: {e}")
|
180 |
+
logger.error(f"Raw response: {response_string}")
|
181 |
+
raise ValueError("Invalid JSON response from LLM")
|
182 |
+
|
183 |
+
# --- GLOBAL CATALOG OF ALL SCRATCH BLOCKS ---
|
184 |
+
# This is where you would load your block_content.json
|
185 |
+
# For demonstration, I'm using your provided snippets and adding some common ones.
|
186 |
+
# In a real application, you'd load this once at startup.
|
187 |
+
ALL_SCRATCH_BLOCKS_CATALOG = {
|
188 |
+
"motion_movesteps": {
|
189 |
+
"opcode": "motion_movesteps", "next": None, "parent": None,
|
190 |
+
"inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 464, "y": -416
|
191 |
+
},
|
192 |
+
"motion_turnright": {
|
193 |
+
"opcode": "motion_turnright", "next": None, "parent": None,
|
194 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 467, "y": -316
|
195 |
+
},
|
196 |
+
"motion_ifonedgebounce": {
|
197 |
+
"opcode": "motion_ifonedgebounce", "next": None, "parent": None,
|
198 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 467, "y": -316
|
199 |
+
},
|
200 |
+
"event_whenflagclicked": {
|
201 |
+
"opcode": "event_whenflagclicked", "next": None, "parent": None,
|
202 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
203 |
+
},
|
204 |
+
"event_whenkeypressed": {
|
205 |
+
"opcode": "event_whenkeypressed", "next": None, "parent": None,
|
206 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
207 |
+
},
|
208 |
+
"control_forever": {
|
209 |
+
"opcode": "control_forever", "next": None, "parent": None,
|
210 |
+
"inputs": {"SUBSTACK": [2, "some_id"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
211 |
+
},
|
212 |
+
"control_if": {
|
213 |
+
"opcode": "control_if", "next": None, "parent": None,
|
214 |
+
"inputs": {"CONDITION": [2, "some_id"], "SUBSTACK": [2, "some_id_2"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
215 |
+
},
|
216 |
+
"looks_sayforsecs": {
|
217 |
+
"opcode": "looks_sayforsecs", "next": None, "parent": None,
|
218 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
219 |
+
},
|
220 |
+
"looks_say": {
|
221 |
+
"opcode": "looks_say", "next": None, "parent": None,
|
222 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
223 |
+
},
|
224 |
+
"sensing_touchingobject": {
|
225 |
+
"opcode": "sensing_touchingobject", "next": None, "parent": None,
|
226 |
+
"inputs": {"TOUCHINGOBJECTMENU": [1, "some_id"]}, "fields": {}, "shadow": False, "topLevel": True, "x": 10, "y": 10
|
227 |
+
},
|
228 |
+
"sensing_touchingobjectmenu": {
|
229 |
+
"opcode": "sensing_touchingobjectmenu", "next": None, "parent": None,
|
230 |
+
"inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": True, "x": 10, "y": 10
|
231 |
+
},
|
232 |
+
# Add more blocks from your block_content.json here...
|
233 |
+
}
|
234 |
+
|
235 |
+
# --- Heuristic-based block selection ---
|
236 |
+
def get_relevant_blocks_for_plan(action_plan: Dict[str, Any], all_blocks_catalog: Dict[str, Any]) -> Dict[str, Any]:
|
237 |
+
"""
|
238 |
+
Analyzes the natural language action plan and selects relevant Scratch blocks
|
239 |
+
from the comprehensive catalog. This is a heuristic approach and might need
|
240 |
+
to be refined based on your specific use cases and LLM capabilities.
|
241 |
+
"""
|
242 |
+
relevant_opcodes = set()
|
243 |
+
|
244 |
+
# Always include common event blocks
|
245 |
+
relevant_opcodes.add("event_whenflagclicked")
|
246 |
+
relevant_opcodes.add("event_whenkeypressed") # Could be more specific if key is mentioned
|
247 |
+
|
248 |
+
# Keyword to opcode mapping (can be expanded)
|
249 |
+
keyword_map = {
|
250 |
+
"move": "motion_movesteps",
|
251 |
+
"steps": "motion_movesteps",
|
252 |
+
"turn": "motion_turnright",
|
253 |
+
"rotate": "motion_turnright",
|
254 |
+
"bounce": "motion_ifonedgebounce",
|
255 |
+
"edge": "motion_ifonedgebounce",
|
256 |
+
"forever": "control_forever",
|
257 |
+
"loop": "control_forever",
|
258 |
+
"if": "control_if",
|
259 |
+
"condition": "control_if",
|
260 |
+
"say": "looks_say",
|
261 |
+
"hello": "looks_say", # Simple example, might need more context
|
262 |
+
"touching": "sensing_touchingobject",
|
263 |
+
"mouse pointer": "sensing_touchingobjectmenu",
|
264 |
+
"edge": "sensing_touchingobjectmenu", # For touching edge
|
265 |
+
}
|
266 |
+
|
267 |
+
# Iterate through the action plan to find keywords
|
268 |
+
for sprite_name, sprite_actions in action_plan.get("action_overall_flow", {}).items():
|
269 |
+
for plan in sprite_actions.get("plans", []):
|
270 |
+
event_logic = plan.get("event", "").lower() + " " + plan.get("logic", "").lower()
|
271 |
+
|
272 |
+
# Check for direct opcode matches (if the LLM somehow outputs opcodes in its plan)
|
273 |
+
for opcode in all_blocks_catalog.keys():
|
274 |
+
if opcode in event_logic:
|
275 |
+
relevant_opcodes.add(opcode)
|
276 |
+
|
277 |
+
# Check for keywords
|
278 |
+
for keyword, opcode in keyword_map.items():
|
279 |
+
if keyword in event_logic:
|
280 |
+
relevant_opcodes.add(opcode)
|
281 |
+
# Add associated shadow blocks if known
|
282 |
+
if opcode == "sensing_touchingobject":
|
283 |
+
relevant_opcodes.add("sensing_touchingobjectmenu")
|
284 |
+
if opcode == "event_whenkeypressed":
|
285 |
+
relevant_opcodes.add("event_whenkeypressed") # It's already there but good to be explicit
|
286 |
+
|
287 |
+
# Construct the filtered catalog
|
288 |
+
relevant_blocks_catalog = {
|
289 |
+
opcode: all_blocks_catalog[opcode]
|
290 |
+
for opcode in relevant_opcodes if opcode in all_blocks_catalog
|
291 |
+
}
|
292 |
+
return relevant_blocks_catalog
|
293 |
+
|
294 |
+
# --- New Action Planning Node ---
|
295 |
+
def plan_sprite_actions(state: GameState):
|
296 |
+
logger.info("--- Running PlanSpriteActionsNode ---")
|
297 |
+
|
298 |
+
planning_prompt = (
|
299 |
+
f"You are an AI assistant tasked with planning Scratch 3.0 block code for a game. "
|
300 |
+
f"The game description is: '{state['description']}'.\n\n"
|
301 |
+
f"Here are the sprites currently in the project: {', '.join(target['name'] for target in state['project_json']['targets'] if not target['isStage']) if len(state['project_json']['targets']) > 1 else 'None'}.\n"
|
302 |
+
f"Initial positions: {json.dumps(state.get('sprite_initial_positions', {}), indent=2)}\n\n"
|
303 |
+
f"Consider the main actions and interactions required for each sprite. "
|
304 |
+
f"Think step-by-step about what each sprite needs to *do*, *when* it needs to do it (events), "
|
305 |
+
f"and if any actions need to *repeat* or depend on *conditions*.\n\n"
|
306 |
+
f"Propose a high-level action flow for each sprite in the following JSON format. "
|
307 |
+
f"Do NOT generate Scratch block JSON yet. Only describe the logic using natural language or simplified pseudo-code.\n\n"
|
308 |
+
f"Example format:\n"
|
309 |
+
f"```json\n"
|
310 |
+
f"{{\n"
|
311 |
+
f" \"action_overall_flow\": {{\n"
|
312 |
+
f" \"Sprite1\": {{\n"
|
313 |
+
f" \"description\": \"Main character actions\",\n"
|
314 |
+
f" \"plans\": [\n"
|
315 |
+
f" {{\n"
|
316 |
+
f" \"event\": \"when flag clicked\",\n"
|
317 |
+
f" \"logic\": \"forever loop: move 10 steps, if on edge bounce\"\n"
|
318 |
+
f" }},\n"
|
319 |
+
f" {{\n"
|
320 |
+
f" \"event\": \"when space key pressed\",\n"
|
321 |
+
f" \"logic\": \"change y by 10, wait 0.1 seconds, change y by -10\"\n"
|
322 |
+
f" }}\n"
|
323 |
+
f" ]\n"
|
324 |
+
f" }},\n"
|
325 |
+
f" \"Ball\": {{\n"
|
326 |
+
f" \"description\": \"Projectile movement\",\n"
|
327 |
+
f" \"plans\": [\n"
|
328 |
+
f" {{\n"
|
329 |
+
f" \"event\": \"when I start as a clone\",\n"
|
330 |
+
f" \"logic\": \"glide 1 sec to random position, if touching Sprite1 then stop this script\"\n"
|
331 |
+
f" }}\n"
|
332 |
+
f" ]\n"
|
333 |
+
f" }}\n"
|
334 |
+
f" }}\n"
|
335 |
+
f"}}\n"
|
336 |
+
f"```\n\n"
|
337 |
+
f"Return ONLY the JSON object for the action overall flow."
|
338 |
+
)
|
339 |
+
|
340 |
+
try:
|
341 |
+
response = agent.invoke({"messages": [{"role": "user", "content": planning_prompt}]})
|
342 |
+
raw_response = response["messages"][-1].content
|
343 |
+
print("Raw response from LLM [PlanSpriteActionsNode]:", raw_response)
|
344 |
+
action_plan = extract_json_from_llm_response(raw_response)
|
345 |
+
logger.info("Sprite action plan generated by PlanSpriteActionsNode.")
|
346 |
+
return {"action_plan": action_plan}
|
347 |
+
except Exception as e:
|
348 |
+
logger.error(f"Error in PlanSpriteActionsNode: {e}")
|
349 |
+
raise
|
350 |
+
|
351 |
+
# --- Updated Action Node Builder (to consume the plan and build blocks) ---
|
352 |
+
def build_action_nodes(state: GameState):
|
353 |
+
logger.info("--- Running ActionNodeBuilder ---")
|
354 |
+
|
355 |
+
action_plan = state.get("action_plan", {})
|
356 |
+
if not action_plan:
|
357 |
+
raise ValueError("No action plan found in state. Run PlanSpriteActionsNode first.")
|
358 |
+
|
359 |
+
# Convert the Scratch project JSON to a mutable Python object
|
360 |
+
project_json = state["project_json"]
|
361 |
+
targets = project_json["targets"]
|
362 |
+
|
363 |
+
# We need a way to map sprite names to their actual target objects in project_json
|
364 |
+
sprite_map = {target["name"]: target for target in targets if not target["isStage"]}
|
365 |
+
|
366 |
+
# --- NEW: Get only the relevant blocks for the entire action plan ---
|
367 |
+
relevant_scratch_blocks_catalog = get_relevant_blocks_for_plan(action_plan, ALL_SCRATCH_BLOCKS_CATALOG)
|
368 |
+
logger.info(f"Filtered {len(relevant_scratch_blocks_catalog)} relevant blocks out of {len(ALL_SCRATCH_BLOCKS_CATALOG)} total.")
|
369 |
+
|
370 |
+
|
371 |
+
# Iterate through the planned actions for each sprite
|
372 |
+
for sprite_name, sprite_actions in action_plan.get("action_overall_flow", {}).items():
|
373 |
+
if sprite_name in sprite_map:
|
374 |
+
current_sprite_target = sprite_map[sprite_name]
|
375 |
+
# Ensure 'blocks' field exists for the sprite
|
376 |
+
if "blocks" not in current_sprite_target:
|
377 |
+
current_sprite_target["blocks"] = {}
|
378 |
+
|
379 |
+
# Generate block JSON based on the detailed action plan for this sprite
|
380 |
+
# This is where the LLM's role becomes crucial: translating logic to blocks
|
381 |
+
llm_block_generation_prompt = (
|
382 |
+
f"You are an AI assistant generating Scratch 3.0 block JSON based on a provided plan. "
|
383 |
+
f"The current sprite is '{sprite_name}'.\n"
|
384 |
+
f"Its planned actions are:\n"
|
385 |
+
f"```json\n{json.dumps(sprite_actions, indent=2)}\n```\n\n"
|
386 |
+
f"Here is a **curated catalog of only the most relevant Scratch 3.0 blocks** for this plan:\n"
|
387 |
+
f"```json\n{json.dumps(relevant_scratch_blocks_catalog, indent=2)}\n```\n\n"
|
388 |
+
f"Current Scratch project JSON (for context, specifically this sprite's existing blocks if any):\n"
|
389 |
+
f"```json\n{json.dumps(current_sprite_target, indent=2)}\n```\n\n"
|
390 |
+
f"**Instructions:**\n"
|
391 |
+
f"1. For each planned event and its associated logic, generate the corresponding Scratch 3.0 block JSON.\n"
|
392 |
+
f"2. **Generate unique block IDs** for every new block. Use a format like 'block_id_abcdef12'.\n" # Updated ID format hint
|
393 |
+
f"3. Properly link blocks using `next` and `parent` fields to form execution stacks. Hat blocks (`topLevel: true`, `parent: null`).\n"
|
394 |
+
f"4. Correctly fill `inputs` and `fields` based on the catalog and the plan's logic (e.g., specific values for motion, keys for events, conditions for controls).\n"
|
395 |
+
f"5. For C-blocks (like `control_repeat`, `control_forever`, `control_if`), use the `SUBSTACK` input to link to the first block inside its loop/conditional.\n"
|
396 |
+
f"6. If the plan involves operators (e.g., 'if touching Sprite1'), use the appropriate operator blocks from the catalog and link them correctly as `CONDITION` inputs.\n"
|
397 |
+
f"7. Ensure that any shadow blocks (e.g., for dropdowns like `motion_goto_menu`, `sensing_touchingobjectmenu`) are generated with `shadow: true` and linked correctly as inputs to their parent block.\n"
|
398 |
+
f"8. Return ONLY the **updated 'blocks' dictionary** for this specific sprite. Do NOT return the full project JSON. ONLY the `blocks` dictionary."
|
399 |
+
)
|
400 |
+
|
401 |
+
try:
|
402 |
+
response = agent.invoke({"messages": [{"role": "user", "content": llm_block_generation_prompt}]})
|
403 |
+
raw_response = response["messages"][-1].content
|
404 |
+
print(f"Raw response from LLM [ActionNodeBuilder - {sprite_name}]:", raw_response)
|
405 |
+
generated_blocks = extract_json_from_llm_response(raw_response)
|
406 |
+
current_sprite_target["blocks"].update(generated_blocks) # Merge new blocks
|
407 |
+
logger.info(f"Action blocks added for sprite '{sprite_name}' by ActionNodeBuilder.")
|
408 |
+
except Exception as e:
|
409 |
+
logger.error(f"Error generating blocks for sprite '{sprite_name}': {e}")
|
410 |
+
# Depending on robustness needed, you might continue or re-raise
|
411 |
+
raise
|
412 |
+
|
413 |
+
return {"project_json": project_json}
|
414 |
+
|
415 |
+
# --- Example Usage (to demonstrate the flow) ---
|
416 |
+
if __name__ == "__main__":
|
417 |
+
# Initialize a mock game state
|
418 |
+
initial_game_state = {
|
419 |
+
"description": "A simple game where a sprite moves and says hello.",
|
420 |
+
"project_json": {
|
421 |
+
"targets": [
|
422 |
+
{"isStage": True, "name": "Stage", "blocks": {}},
|
423 |
+
{"isStage": False, "name": "Sprite1", "blocks": {}},
|
424 |
+
{"isStage": False, "name": "Ball", "blocks": {}}
|
425 |
+
]
|
426 |
+
},
|
427 |
+
"sprite_initial_positions": {}
|
428 |
+
}
|
429 |
+
|
430 |
+
# Step 1: Plan Sprite Actions
|
431 |
+
try:
|
432 |
+
state_after_planning = plan_sprite_actions(initial_game_state)
|
433 |
+
initial_game_state.update(state_after_planning)
|
434 |
+
print("\n--- Game State After Planning ---")
|
435 |
+
print(json.dumps(initial_game_state, indent=2))
|
436 |
+
except Exception as e:
|
437 |
+
print(f"Planning failed: {e}")
|
438 |
+
exit()
|
439 |
+
|
440 |
+
# Step 2: Build Action Nodes (Generate Blocks)
|
441 |
+
try:
|
442 |
+
state_after_building = build_action_nodes(initial_game_state)
|
443 |
+
initial_game_state.update(state_after_building)
|
444 |
+
print("\n--- Game State After Building Blocks ---")
|
445 |
+
# Print only the blocks for a specific sprite to keep output manageable
|
446 |
+
for target in initial_game_state["project_json"]["targets"]:
|
447 |
+
if not target["isStage"]:
|
448 |
+
print(f"\nBlocks for {target['name']}:")
|
449 |
+
print(json.dumps(target.get('blocks', {}), indent=2))
|
450 |
+
|
451 |
+
except Exception as e:
|
452 |
+
print(f"Building blocks failed: {e}")
|
v2/utils/action_plan_sample.txt
ADDED
@@ -0,0 +1,1577 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
First sample:
|
2 |
+
|
3 |
+
{
|
4 |
+
'Stage': {
|
5 |
+
'description': 'Background and global game state management, including broadcasts, rewards, and score.',
|
6 |
+
'plans': [
|
7 |
+
{
|
8 |
+
'event': 'event_whenflagclicked',
|
9 |
+
'logic': 'when green flag clicked\n switch backdrop to [blue sky v]\n set [score v] to 0\n show variable [score v]\n broadcast [Game Start v]',
|
10 |
+
'motion': [
|
11 |
+
|
12 |
+
],
|
13 |
+
'control': [
|
14 |
+
|
15 |
+
],
|
16 |
+
'operator': [
|
17 |
+
|
18 |
+
],
|
19 |
+
'sensing': [
|
20 |
+
|
21 |
+
],
|
22 |
+
'looks': [
|
23 |
+
'looks_switchbackdropto'
|
24 |
+
],
|
25 |
+
'sounds': [
|
26 |
+
|
27 |
+
],
|
28 |
+
'events': [
|
29 |
+
'event_broadcast'
|
30 |
+
],
|
31 |
+
'data': [
|
32 |
+
'data_setvariableto',
|
33 |
+
'data_showvariable'
|
34 |
+
],
|
35 |
+
'opcode_counts': [
|
36 |
+
{
|
37 |
+
'opcode': 'event_whenflagclicked',
|
38 |
+
'count': 1
|
39 |
+
},
|
40 |
+
{
|
41 |
+
'opcode': 'looks_switchbackdropto',
|
42 |
+
'count': 1
|
43 |
+
},
|
44 |
+
{
|
45 |
+
'opcode': 'data_setvariableto',
|
46 |
+
'count': 1
|
47 |
+
},
|
48 |
+
{
|
49 |
+
'opcode': 'data_showvariable',
|
50 |
+
'count': 1
|
51 |
+
},
|
52 |
+
{
|
53 |
+
'opcode': 'event_broadcast',
|
54 |
+
'count': 1
|
55 |
+
}
|
56 |
+
]
|
57 |
+
},
|
58 |
+
{
|
59 |
+
'event': 'event_whenbroadcastreceived',
|
60 |
+
'logic': 'when I receive [Game Over v]\n if <(score) > (High Score)> then\n set [High Score v] to (score)\n end\n switch backdrop to [Game Over v]',
|
61 |
+
'motion': [
|
62 |
+
|
63 |
+
],
|
64 |
+
'control': [
|
65 |
+
'control_if'
|
66 |
+
],
|
67 |
+
'operator': [
|
68 |
+
'operator_gt'
|
69 |
+
],
|
70 |
+
'sensing': [
|
71 |
+
|
72 |
+
],
|
73 |
+
'looks': [
|
74 |
+
'looks_switchbackdropto'
|
75 |
+
],
|
76 |
+
'sounds': [
|
77 |
+
|
78 |
+
],
|
79 |
+
'events': [
|
80 |
+
|
81 |
+
],
|
82 |
+
'data': [
|
83 |
+
'data_setvariableto'
|
84 |
+
],
|
85 |
+
'opcode_counts': [
|
86 |
+
{
|
87 |
+
'opcode': 'event_whenbroadcastreceived',
|
88 |
+
'count': 1
|
89 |
+
},
|
90 |
+
{
|
91 |
+
'opcode': 'control_if',
|
92 |
+
'count': 1
|
93 |
+
},
|
94 |
+
{
|
95 |
+
'opcode': 'operator_gt',
|
96 |
+
'count': 1
|
97 |
+
},
|
98 |
+
{
|
99 |
+
'opcode': 'data_setvariableto',
|
100 |
+
'count': 1
|
101 |
+
},
|
102 |
+
{
|
103 |
+
'opcode': 'looks_switchbackdropto',
|
104 |
+
'count': 1
|
105 |
+
}
|
106 |
+
]
|
107 |
+
},
|
108 |
+
{
|
109 |
+
'event': 'event_whenbroadcastreceived',
|
110 |
+
'logic': 'when I receive [Level Up v]\n change [level v] by 1\n set [ballSpeed v] to (ballSpeed * 1.1)',
|
111 |
+
'motion': [
|
112 |
+
|
113 |
+
],
|
114 |
+
'control': [
|
115 |
+
|
116 |
+
],
|
117 |
+
'operator': [
|
118 |
+
'operator_multiply'
|
119 |
+
],
|
120 |
+
'sensing': [
|
121 |
+
|
122 |
+
],
|
123 |
+
'looks': [
|
124 |
+
|
125 |
+
],
|
126 |
+
'sounds': [
|
127 |
+
|
128 |
+
],
|
129 |
+
'events': [
|
130 |
+
|
131 |
+
],
|
132 |
+
'data': [
|
133 |
+
'data_changevariableby',
|
134 |
+
'data_setvariableto'
|
135 |
+
],
|
136 |
+
'opcode_counts': [
|
137 |
+
{
|
138 |
+
'opcode': 'event_whenbroadcastreceived',
|
139 |
+
'count': 1
|
140 |
+
},
|
141 |
+
{
|
142 |
+
'opcode': 'data_changevariableby',
|
143 |
+
'count': 1
|
144 |
+
},
|
145 |
+
{
|
146 |
+
'opcode': 'data_setvariableto',
|
147 |
+
'count': 1
|
148 |
+
},
|
149 |
+
{
|
150 |
+
'opcode': 'operator_multiply',
|
151 |
+
'count': 1
|
152 |
+
}
|
153 |
+
]
|
154 |
+
}
|
155 |
+
]
|
156 |
+
},
|
157 |
+
'Cat': {
|
158 |
+
'description': 'Main character (cat) actions',
|
159 |
+
'plans': [
|
160 |
+
{
|
161 |
+
'event': 'event_whenflagclicked',
|
162 |
+
'logic': 'when green flag clicked\n go to x: 0 y: -100\n forever\n if <key [space v] pressed?> then\n jump\n end\n move 5 steps\n if <touching [Ball v]?> then\n broadcast [Game Over v]\n end\n end',
|
163 |
+
'motion': [
|
164 |
+
'motion_movesteps',
|
165 |
+
'motion_gotoxy'
|
166 |
+
],
|
167 |
+
'control': [
|
168 |
+
'control_forever',
|
169 |
+
'control_if'
|
170 |
+
],
|
171 |
+
'operator': [
|
172 |
+
|
173 |
+
],
|
174 |
+
'sensing': [
|
175 |
+
'sensing_keypressed',
|
176 |
+
'sensing_touchingobject'
|
177 |
+
],
|
178 |
+
'looks': [
|
179 |
+
|
180 |
+
],
|
181 |
+
'sounds': [
|
182 |
+
|
183 |
+
],
|
184 |
+
'events': [
|
185 |
+
'event_broadcast'
|
186 |
+
],
|
187 |
+
'data': [
|
188 |
+
|
189 |
+
],
|
190 |
+
'opcode_counts': [
|
191 |
+
{
|
192 |
+
'opcode': 'event_whenflagclicked',
|
193 |
+
'count': 1
|
194 |
+
},
|
195 |
+
{
|
196 |
+
'opcode': 'motion_gotoxy',
|
197 |
+
'count': 1
|
198 |
+
},
|
199 |
+
{
|
200 |
+
'opcode': 'control_forever',
|
201 |
+
'count': 1
|
202 |
+
},
|
203 |
+
{
|
204 |
+
'opcode': 'control_if',
|
205 |
+
'count': 2
|
206 |
+
},
|
207 |
+
{
|
208 |
+
'opcode': 'sensing_keypressed',
|
209 |
+
'count': 1
|
210 |
+
},
|
211 |
+
{
|
212 |
+
'opcode': 'sensing_touchingobject',
|
213 |
+
'count': 1
|
214 |
+
},
|
215 |
+
{
|
216 |
+
'opcode': 'motion_movesteps',
|
217 |
+
'count': 1
|
218 |
+
},
|
219 |
+
{
|
220 |
+
'opcode': 'event_broadcast',
|
221 |
+
'count': 1
|
222 |
+
}
|
223 |
+
]
|
224 |
+
},
|
225 |
+
{
|
226 |
+
'event': 'event_whenkeypressed',
|
227 |
+
'logic': 'when [space v] key pressed\n change y by 10\n wait 0.2 seconds\n change y by -10',
|
228 |
+
'motion': [
|
229 |
+
'motion_changeyby'
|
230 |
+
],
|
231 |
+
'control': [
|
232 |
+
'control_wait'
|
233 |
+
],
|
234 |
+
'operator': [
|
235 |
+
|
236 |
+
],
|
237 |
+
'sensing': [
|
238 |
+
|
239 |
+
],
|
240 |
+
'looks': [
|
241 |
+
|
242 |
+
],
|
243 |
+
'sounds': [
|
244 |
+
|
245 |
+
],
|
246 |
+
'events': [
|
247 |
+
|
248 |
+
],
|
249 |
+
'data': [
|
250 |
+
|
251 |
+
],
|
252 |
+
'opcode_counts': [
|
253 |
+
{
|
254 |
+
'opcode': 'event_whenkeypressed',
|
255 |
+
'count': 1
|
256 |
+
},
|
257 |
+
{
|
258 |
+
'opcode': 'motion_changeyby',
|
259 |
+
'count': 2
|
260 |
+
},
|
261 |
+
{
|
262 |
+
'opcode': 'control_wait',
|
263 |
+
'count': 1
|
264 |
+
}
|
265 |
+
]
|
266 |
+
}
|
267 |
+
]
|
268 |
+
},
|
269 |
+
'Ball': {
|
270 |
+
'description': 'Obstacle movement and interaction',
|
271 |
+
'plans': [
|
272 |
+
{
|
273 |
+
'event': 'event_whenflagclicked',
|
274 |
+
'logic': 'when green flag clicked\n go to x: 0 y: 100\n forever\n glide 2 seconds to x: pick random (-240, 240) y: 100\n if <touching [Cat v]?> then\n broadcast [Game Over v]\n end\n end',
|
275 |
+
'motion': [
|
276 |
+
'motion_glidesecstoxy',
|
277 |
+
'motion_xposition'
|
278 |
+
],
|
279 |
+
'control': [
|
280 |
+
'control_forever'
|
281 |
+
],
|
282 |
+
'operator': [
|
283 |
+
'operator_random'
|
284 |
+
],
|
285 |
+
'sensing': [
|
286 |
+
'sensing_touchingobject'
|
287 |
+
],
|
288 |
+
'looks': [
|
289 |
+
|
290 |
+
],
|
291 |
+
'sounds': [
|
292 |
+
|
293 |
+
],
|
294 |
+
'events': [
|
295 |
+
'event_broadcast'
|
296 |
+
],
|
297 |
+
'data': [
|
298 |
+
|
299 |
+
],
|
300 |
+
'opcode_counts': [
|
301 |
+
{
|
302 |
+
'opcode': 'event_whenflagclicked',
|
303 |
+
'count': 1
|
304 |
+
},
|
305 |
+
{
|
306 |
+
'opcode': 'motion_gotoxy',
|
307 |
+
'count': 1
|
308 |
+
},
|
309 |
+
{
|
310 |
+
'opcode': 'motion_glidesecstoxy',
|
311 |
+
'count': 1
|
312 |
+
},
|
313 |
+
{
|
314 |
+
'opcode': 'operator_random',
|
315 |
+
'count': 1
|
316 |
+
},
|
317 |
+
{
|
318 |
+
'opcode': 'control_forever',
|
319 |
+
'count': 1
|
320 |
+
},
|
321 |
+
{
|
322 |
+
'opcode': 'sensing_touchingobject',
|
323 |
+
'count': 1
|
324 |
+
},
|
325 |
+
{
|
326 |
+
'opcode': 'event_broadcast',
|
327 |
+
'count': 1
|
328 |
+
}
|
329 |
+
]
|
330 |
+
}
|
331 |
+
]
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
second sample
|
336 |
+
|
337 |
+
[Refined Action Plan]: {
|
338 |
+
"Stage": {
|
339 |
+
"description": "Background and global game state management, including broadcasts, rewards, and score.",
|
340 |
+
"plans": [
|
341 |
+
{
|
342 |
+
"event": "event_whenflagclicked",
|
343 |
+
"logic": "when green flag clicked\n set [score v] to 0\n set [speed v] to 2\n set [lives v] to 1\n broadcast [Game Start v]",
|
344 |
+
"control": [],
|
345 |
+
"operator": [],
|
346 |
+
"sensing": [],
|
347 |
+
"looks": [],
|
348 |
+
"sounds": [],
|
349 |
+
"events": [
|
350 |
+
"event_broadcast"
|
351 |
+
],
|
352 |
+
"data": [
|
353 |
+
"data_setvariableto"
|
354 |
+
]
|
355 |
+
},
|
356 |
+
{
|
357 |
+
"event": "event_whenbroadcastreceived",
|
358 |
+
"logic": "when I receive [Game Over v]\n if <(score) > (High Score)> then\n set [High Score v] to (score)\n end\n broadcast [Reset Game v]",
|
359 |
+
"control": [
|
360 |
+
"control_if"
|
361 |
+
],
|
362 |
+
"operator": [
|
363 |
+
"operator_gt"
|
364 |
+
],
|
365 |
+
"sensing": [],
|
366 |
+
"looks": [],
|
367 |
+
"sounds": [],
|
368 |
+
"events": [
|
369 |
+
"event_broadcast"
|
370 |
+
],
|
371 |
+
"data": [
|
372 |
+
"data_setvariableto"
|
373 |
+
]
|
374 |
+
},
|
375 |
+
{
|
376 |
+
"event": "event_whenbroadcastreceived",
|
377 |
+
"logic": "when I receive [Reset Game v]\n set [score v] to 0\n set [speed v] to 2\n set [lives v] to 1",
|
378 |
+
"control": [],
|
379 |
+
"operator": [],
|
380 |
+
"sensing": [],
|
381 |
+
"looks": [],
|
382 |
+
"sounds": [],
|
383 |
+
"events": [
|
384 |
+
"event_broadcast"
|
385 |
+
],
|
386 |
+
"data": [
|
387 |
+
"data_setvariableto"
|
388 |
+
]
|
389 |
+
}
|
390 |
+
]
|
391 |
+
},
|
392 |
+
"Cat": {
|
393 |
+
"description": "Main character (cat) actions",
|
394 |
+
"plans": [
|
395 |
+
{
|
396 |
+
"event": "event_whenflagclicked",
|
397 |
+
"logic": "when green flag clicked\n go to x: 0 y: -130\n forever\n if <key [space v] pressed?> then\n jump\n end\n move 5 steps\n end",
|
398 |
+
"motion": [
|
399 |
+
"motion_movesteps",
|
400 |
+
"motion_setx",
|
401 |
+
"motion_sety"
|
402 |
+
],
|
403 |
+
"control": [
|
404 |
+
"control_forever",
|
405 |
+
"control_if"
|
406 |
+
],
|
407 |
+
"operator": [],
|
408 |
+
"sensing": [
|
409 |
+
"sensing_keypressed"
|
410 |
+
],
|
411 |
+
"looks": [],
|
412 |
+
"sounds": [],
|
413 |
+
"events": [],
|
414 |
+
"data": []
|
415 |
+
},
|
416 |
+
{
|
417 |
+
"event": "event_whenkeypressed",
|
418 |
+
"logic": "when [space v] key pressed\n if <(y position) = -130> then\n repeat (10)\n change y by (20)\n wait (0.1) seconds\n change y by (-20)\n end\n end",
|
419 |
+
"control": [
|
420 |
+
"control_repeat",
|
421 |
+
"control_if"
|
422 |
+
],
|
423 |
+
"operator": [],
|
424 |
+
"sensing": [],
|
425 |
+
"looks": [],
|
426 |
+
"sounds": [],
|
427 |
+
"events": [],
|
428 |
+
"data": []
|
429 |
+
}
|
430 |
+
]
|
431 |
+
},
|
432 |
+
"Ball": {
|
433 |
+
"description": "Obstacle movement and interaction",
|
434 |
+
"plans": [
|
435 |
+
{
|
436 |
+
"event": "event_whenflagclicked",
|
437 |
+
"logic": "when green flag clicked\n go to x: 240 y: 0\n forever\n glide (2) seconds to x: -240 y: 0\n if <(x position) < -235> then\n
|
438 |
+
set x to 240\n end\n if <touching [Cat v]?> then\n broadcast [Game Over v]\n end\n end",
|
439 |
+
"motion": [
|
440 |
+
"motion_gotoxy",
|
441 |
+
"motion_glidesecstoxy",
|
442 |
+
"motion_xposition",
|
443 |
+
"motion_setx"
|
444 |
+
],
|
445 |
+
"control": [
|
446 |
+
"control_forever",
|
447 |
+
"control_if"
|
448 |
+
],
|
449 |
+
"operator": [
|
450 |
+
"operator_lt"
|
451 |
+
],
|
452 |
+
"sensing": [
|
453 |
+
"sensing_istouching"
|
454 |
+
],
|
455 |
+
"looks": [],
|
456 |
+
"sounds": [],
|
457 |
+
"events": [
|
458 |
+
"event_broadcast"
|
459 |
+
],
|
460 |
+
"data": []
|
461 |
+
}
|
462 |
+
]
|
463 |
+
}
|
464 |
+
}
|
465 |
+
|
466 |
+
|
467 |
+
sample 3:
|
468 |
+
{
|
469 |
+
'Stage': {
|
470 |
+
'description': 'Background and global game state management, including broadcasts, rewards, and score.',
|
471 |
+
'plans': [
|
472 |
+
{
|
473 |
+
'event': 'event_whenflagclicked',
|
474 |
+
'logic': 'when green flag clicked\n set [score v] to 0\n set [health v] to 1\n set [speed v] to 1\n switch backdrop to [blue sky v]\n broadcast [Game Start v]',
|
475 |
+
'motion': [
|
476 |
+
|
477 |
+
],
|
478 |
+
'control': [
|
479 |
+
|
480 |
+
],
|
481 |
+
'operator': [
|
482 |
+
|
483 |
+
],
|
484 |
+
'sensing': [
|
485 |
+
|
486 |
+
],
|
487 |
+
'looks': [
|
488 |
+
'looks_switchbackdropto'
|
489 |
+
],
|
490 |
+
'sounds': [
|
491 |
+
|
492 |
+
],
|
493 |
+
'events': [
|
494 |
+
'event_broadcast'
|
495 |
+
],
|
496 |
+
'data': [
|
497 |
+
'data_setvariableto',
|
498 |
+
'data_showvariable'
|
499 |
+
],
|
500 |
+
'opcode_counts': [
|
501 |
+
{
|
502 |
+
'opcode': 'event_whenflagclicked',
|
503 |
+
'count': 1
|
504 |
+
},
|
505 |
+
{
|
506 |
+
'opcode': 'data_setvariableto',
|
507 |
+
'count': 3
|
508 |
+
},
|
509 |
+
{
|
510 |
+
'opcode': 'looks_switchbackdropto',
|
511 |
+
'count': 1
|
512 |
+
},
|
513 |
+
{
|
514 |
+
'opcode': 'event_broadcast',
|
515 |
+
'count': 1
|
516 |
+
}
|
517 |
+
]
|
518 |
+
},
|
519 |
+
{
|
520 |
+
'event': 'event_whenbroadcastreceived',
|
521 |
+
'logic': 'when I receive [Game Over v]\n if <(score) > (High Score)> then\n set [High Score v] to (score)\n end\n switch backdrop to [Game Over v]',
|
522 |
+
'motion': [
|
523 |
+
|
524 |
+
],
|
525 |
+
'control': [
|
526 |
+
'control_if'
|
527 |
+
],
|
528 |
+
'operator': [
|
529 |
+
'operator_gt'
|
530 |
+
],
|
531 |
+
'sensing': [
|
532 |
+
|
533 |
+
],
|
534 |
+
'looks': [
|
535 |
+
'looks_switchbackdropto'
|
536 |
+
],
|
537 |
+
'sounds': [
|
538 |
+
|
539 |
+
],
|
540 |
+
'events': [
|
541 |
+
|
542 |
+
],
|
543 |
+
'data': [
|
544 |
+
'data_setvariableto'
|
545 |
+
],
|
546 |
+
'opcode_counts': [
|
547 |
+
{
|
548 |
+
'opcode': 'event_whenbroadcastreceived',
|
549 |
+
'count': 1
|
550 |
+
},
|
551 |
+
{
|
552 |
+
'opcode': 'control_if',
|
553 |
+
'count': 1
|
554 |
+
},
|
555 |
+
{
|
556 |
+
'opcode': 'operator_gt',
|
557 |
+
'count': 1
|
558 |
+
},
|
559 |
+
{
|
560 |
+
'opcode': 'data_setvariableto',
|
561 |
+
'count': 1
|
562 |
+
},
|
563 |
+
{
|
564 |
+
'opcode': 'looks_switchbackdropto',
|
565 |
+
'count': 1
|
566 |
+
}
|
567 |
+
]
|
568 |
+
}
|
569 |
+
]
|
570 |
+
},
|
571 |
+
'Cat': {
|
572 |
+
'description': 'Main character (cat) actions',
|
573 |
+
'plans': [
|
574 |
+
{
|
575 |
+
'event': 'event_whenflagclicked',
|
576 |
+
'logic': 'when green flag clicked\n go to x: 0 y: -130\n set [shield v] to 0',
|
577 |
+
'motion': [
|
578 |
+
'motion_gotoxy'
|
579 |
+
],
|
580 |
+
'control': [
|
581 |
+
|
582 |
+
],
|
583 |
+
'operator': [
|
584 |
+
|
585 |
+
],
|
586 |
+
'sensing': [
|
587 |
+
|
588 |
+
],
|
589 |
+
'looks': [
|
590 |
+
|
591 |
+
],
|
592 |
+
'sounds': [
|
593 |
+
|
594 |
+
],
|
595 |
+
'events': [
|
596 |
+
|
597 |
+
],
|
598 |
+
'data': [
|
599 |
+
|
600 |
+
],
|
601 |
+
'opcode_counts': [
|
602 |
+
{
|
603 |
+
'opcode': 'event_whenflagclicked',
|
604 |
+
'count': 1
|
605 |
+
},
|
606 |
+
{
|
607 |
+
'opcode': 'motion_gotoxy',
|
608 |
+
'count': 1
|
609 |
+
},
|
610 |
+
{
|
611 |
+
'opcode': 'data_setvariableto',
|
612 |
+
'count': 1
|
613 |
+
}
|
614 |
+
]
|
615 |
+
},
|
616 |
+
{
|
617 |
+
'event': 'event_whenkeypressed',
|
618 |
+
'logic': 'when [space v] key pressed\n if <(y position) = -130> then\n repeat (10)\n change y by 20\n wait (0.1) seconds\n change y by -20\n end\n end',
|
619 |
+
'motion': [
|
620 |
+
'motion_changeyby'
|
621 |
+
],
|
622 |
+
'control': [
|
623 |
+
'control_repeat',
|
624 |
+
'control_wait',
|
625 |
+
'control_if'
|
626 |
+
],
|
627 |
+
'operator': [
|
628 |
+
|
629 |
+
],
|
630 |
+
'sensing': [
|
631 |
+
|
632 |
+
],
|
633 |
+
'looks': [
|
634 |
+
|
635 |
+
],
|
636 |
+
'sounds': [
|
637 |
+
|
638 |
+
],
|
639 |
+
'events': [
|
640 |
+
|
641 |
+
],
|
642 |
+
'data': [
|
643 |
+
|
644 |
+
],
|
645 |
+
'opcode_counts': [
|
646 |
+
{
|
647 |
+
'opcode': 'event_whenkeypressed',
|
648 |
+
'count': 1
|
649 |
+
},
|
650 |
+
{
|
651 |
+
'opcode': 'control_if',
|
652 |
+
'count': 1
|
653 |
+
},
|
654 |
+
{
|
655 |
+
'opcode': 'control_repeat',
|
656 |
+
'count': 1
|
657 |
+
},
|
658 |
+
{
|
659 |
+
'opcode': 'control_wait',
|
660 |
+
'count': 1
|
661 |
+
},
|
662 |
+
{
|
663 |
+
'opcode': 'motion_changeyby',
|
664 |
+
'count': 2
|
665 |
+
}
|
666 |
+
]
|
667 |
+
},
|
668 |
+
{
|
669 |
+
'event': 'event_whenbroadcastreceived',
|
670 |
+
'logic': 'when I receive [Power-Up v]\n if <(Power-Up) = [Shield v]> then\n set [shield v] to 1\n end',
|
671 |
+
'motion': [
|
672 |
+
|
673 |
+
],
|
674 |
+
'control': [
|
675 |
+
|
676 |
+
],
|
677 |
+
'operator': [
|
678 |
+
|
679 |
+
],
|
680 |
+
'sensing': [
|
681 |
+
|
682 |
+
],
|
683 |
+
'looks': [
|
684 |
+
|
685 |
+
],
|
686 |
+
'sounds': [
|
687 |
+
|
688 |
+
],
|
689 |
+
'events': [
|
690 |
+
|
691 |
+
],
|
692 |
+
'data': [
|
693 |
+
|
694 |
+
],
|
695 |
+
'opcode_counts': [
|
696 |
+
{
|
697 |
+
'opcode': 'event_whenbroadcastreceived',
|
698 |
+
'count': 1
|
699 |
+
},
|
700 |
+
{
|
701 |
+
'opcode': 'control_if',
|
702 |
+
'count': 1
|
703 |
+
},
|
704 |
+
{
|
705 |
+
'opcode': 'operator_eq',
|
706 |
+
'count': 1
|
707 |
+
},
|
708 |
+
{
|
709 |
+
'opcode': 'data_setvariableto',
|
710 |
+
'count': 1
|
711 |
+
}
|
712 |
+
]
|
713 |
+
}
|
714 |
+
]
|
715 |
+
},
|
716 |
+
'Ball': {
|
717 |
+
'description': 'Obstacle movement and interaction',
|
718 |
+
'plans': [
|
719 |
+
{
|
720 |
+
'event': 'event_whenflagclicked',
|
721 |
+
'logic': 'when green flag clicked\n go to x: 50 y: 100\n forever\n change x by 5\n if <(x position) > 240> then\n set x to -240\n end\n if <(x position) < -240> then\n set x to 240\n end\n if <touching [Cat v]?> then\n broadcast [Game Over v]\n end\n end',
|
722 |
+
'motion': [
|
723 |
+
'motion_changexby',
|
724 |
+
'motion_setx'
|
725 |
+
],
|
726 |
+
'control': [
|
727 |
+
'control_forever',
|
728 |
+
'control_if'
|
729 |
+
],
|
730 |
+
'operator': [
|
731 |
+
|
732 |
+
],
|
733 |
+
'sensing': [
|
734 |
+
'sensing_touchingobject'
|
735 |
+
],
|
736 |
+
'looks': [
|
737 |
+
|
738 |
+
],
|
739 |
+
'sounds': [
|
740 |
+
|
741 |
+
],
|
742 |
+
'events': [
|
743 |
+
'event_broadcast'
|
744 |
+
],
|
745 |
+
'data': [
|
746 |
+
|
747 |
+
],
|
748 |
+
'opcode_counts': [
|
749 |
+
{
|
750 |
+
'opcode': 'event_whenflagclicked',
|
751 |
+
'count': 1
|
752 |
+
},
|
753 |
+
{
|
754 |
+
'opcode': 'motion_gotoxy',
|
755 |
+
'count': 1
|
756 |
+
},
|
757 |
+
{
|
758 |
+
'opcode': 'motion_changexby',
|
759 |
+
'count': 1
|
760 |
+
},
|
761 |
+
{
|
762 |
+
'opcode': 'motion_setx',
|
763 |
+
'count': 2
|
764 |
+
},
|
765 |
+
{
|
766 |
+
'opcode': 'control_forever',
|
767 |
+
'count': 1
|
768 |
+
},
|
769 |
+
{
|
770 |
+
'opcode': 'control_if',
|
771 |
+
'count': 2
|
772 |
+
},
|
773 |
+
{
|
774 |
+
'opcode': 'sensing_touchingobject',
|
775 |
+
'count': 1
|
776 |
+
},
|
777 |
+
{
|
778 |
+
'opcode': 'event_broadcast',
|
779 |
+
'count': 1
|
780 |
+
}
|
781 |
+
]
|
782 |
+
}
|
783 |
+
]
|
784 |
+
}
|
785 |
+
}
|
786 |
+
|
787 |
+
|
788 |
+
refinement_prompt = f"""
|
789 |
+
You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).
|
790 |
+
Review the following plan for '{target_name}' triggered by '{event}'.
|
791 |
+
|
792 |
+
Game Description:
|
793 |
+
{game_description}
|
794 |
+
|
795 |
+
--- Scratch 3.0 Block Reference ---
|
796 |
+
### Hat Blocks
|
797 |
+
Description: {hat_description}
|
798 |
+
Blocks:
|
799 |
+
{hat_opcodes_functionalities}
|
800 |
+
|
801 |
+
### Boolean Blocks
|
802 |
+
Description: {boolean_description}
|
803 |
+
Blocks:
|
804 |
+
{boolean_opcodes_functionalities}
|
805 |
+
|
806 |
+
### C Blocks
|
807 |
+
Description: {c_description}
|
808 |
+
Blocks:
|
809 |
+
{c_opcodes_functionalities}
|
810 |
+
|
811 |
+
### Cap Blocks
|
812 |
+
Description: {cap_description}
|
813 |
+
Blocks:
|
814 |
+
{cap_opcodes_functionalities}
|
815 |
+
|
816 |
+
### Reporter Blocks
|
817 |
+
Description: {reporter_description}
|
818 |
+
Blocks:
|
819 |
+
{reporter_opcodes_functionalities}
|
820 |
+
|
821 |
+
### Stack Blocks
|
822 |
+
Description: {stack_description}
|
823 |
+
Blocks:
|
824 |
+
{stack_opcodes_functionalities}
|
825 |
+
-----------------------------------
|
826 |
+
Current Plan Details:
|
827 |
+
- Event (Hat Block Opcode): {event}
|
828 |
+
- Overall plans made on {target_name} are in the list: {overall_plans_in_sprite}
|
829 |
+
- Associated Opcodes by Category: {json.dumps(opcodes, indent=2)}
|
830 |
+
- Reference code, functionality, and logic script to check: {combined_blocks}
|
831 |
+
- Current Logic Description to Update if required: "{original_logic}"
|
832 |
+
|
833 |
+
Your task is to:
|
834 |
+
1. **Refine the 'logic'**: Make it precise, accurate, and fully aligned with the Game Description. Use Scratch‑consistent verbs and phrasing. **Do NOT** use raw double‑quotes inside the logic string.
|
835 |
+
|
836 |
+
2. **Structural requirements**:
|
837 |
+
- **Numeric values** `(e.g., 0, 5, 0.2, -130)` **must** be in parentheses: `(0)`, `(5)`, `(0.2)`, `(-130)`.
|
838 |
+
- **AlphaNumeric values** `(e.g., hello, say 5, 4, hi!)` **must** be in parentheses: `(hello)`, `(say 5)`, `(4)`, `(hi!)`.
|
839 |
+
- **Variables** must be in the form `[variable v]` (e.g., `[score v]`), even when used inside expressions two example use `set [score v] to (1)` or `show variable ([speed v])`.
|
840 |
+
- **Dropdown options** must be in the form `[option v]` (e.g., `[Game Start v]`, `[blue sky v]`). example use `when [space v] key pressed`.
|
841 |
+
- **Reporter blocks** used as inputs must be double‑wrapped: `((x position))`, `((y position))`. example use `if <((y position)) = (-130)> then` or `(((x position)) * (1))`.
|
842 |
+
- **Boolean blocks** in conditions must be inside `< >`, including nested ones: `<not <condition>>`, `<<cond1> and <cond2>>`,`<<cond1> or <cond2>>`.
|
843 |
+
- **Other Boolean blocks** in conditions must be inside `< >`, including nested ones or values or variables: `<(block/value/variable) * (block/value/variable)>`,`<(block/value/variable) < (block/value/variable)>`, and example of another variable`<[apple v] contains [a v]?>`.
|
844 |
+
- **Operator expressions** must use explicit Scratch operator blocks, e.g.:
|
845 |
+
```
|
846 |
+
(([ballSpeed v]) * (1.1))
|
847 |
+
```
|
848 |
+
- **Every hat block script must end** with a final `end` on its own line.
|
849 |
+
|
850 |
+
3. **Pseudo‑code formatting**:
|
851 |
+
- Represent each block or nested block on its own line.
|
852 |
+
- Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.).
|
853 |
+
- No comments or explanatory text—just the block sequence.
|
854 |
+
- a natural language breakdown of each step taken after the event, formatted as a multi-line string representing pseudo-code. Ensure clarity and granularity—each described action should map closely to a Scratch block or tight sequence.
|
855 |
+
|
856 |
+
4. **Logic content**:
|
857 |
+
- Build clear flow for mechanics (movement, jumping, flying, scoring, collisions).
|
858 |
+
- Match each action closely to a Scratch block or tight sequence.
|
859 |
+
- Do **NOT** include any justification or comments—only the raw logic.
|
860 |
+
|
861 |
+
5. **Examples for reference**:
|
862 |
+
**Correct** pattern for a simple start script:
|
863 |
+
```
|
864 |
+
when green flag clicked
|
865 |
+
switch backdrop to [blue sky v]
|
866 |
+
set [score v] to (0)
|
867 |
+
show variable [score v]
|
868 |
+
broadcast [Game Start v]
|
869 |
+
end
|
870 |
+
```
|
871 |
+
**Correct** pattern for updating the high score variable handling:
|
872 |
+
```
|
873 |
+
when I receive [Game Over v]
|
874 |
+
if <((score)) > (([High Score v]))> then
|
875 |
+
set [High Score v] to ([score v])
|
876 |
+
end
|
877 |
+
switch backdrop to [Game Over v]
|
878 |
+
end
|
879 |
+
```
|
880 |
+
**Correct** pattern for level up and increase difficulty use:
|
881 |
+
```
|
882 |
+
when I receive [Level Up v]
|
883 |
+
change [level v] by (1)
|
884 |
+
set [ballSpeed v] to ((([ballSpeed v]) * (1.1)))
|
885 |
+
end
|
886 |
+
```
|
887 |
+
**Correct** pattern for jumping mechanics use:
|
888 |
+
```
|
889 |
+
when [space v] key pressed
|
890 |
+
if <((y position)) = (-100)> then
|
891 |
+
repeat (5)
|
892 |
+
change y by (100)
|
893 |
+
wait (0.1) seconds
|
894 |
+
change y by (-100)
|
895 |
+
wait (0.1) seconds
|
896 |
+
end
|
897 |
+
end
|
898 |
+
end
|
899 |
+
```
|
900 |
+
**Correct** pattern for continuos moving objects use:
|
901 |
+
```
|
902 |
+
when green flag clicked
|
903 |
+
go to x: (240) y: (-100)
|
904 |
+
set [speed v] to (-5)
|
905 |
+
show variable [speed v]
|
906 |
+
forever
|
907 |
+
change x by ([speed v])
|
908 |
+
if <((x position)) < (-240)> then
|
909 |
+
go to x: (240) y: (-100)
|
910 |
+
end
|
911 |
+
end
|
912 |
+
end
|
913 |
+
```
|
914 |
+
**Correct** pattern for continuos moving objects use:
|
915 |
+
```
|
916 |
+
when green flag clicked
|
917 |
+
go to x: (240) y: (-100)
|
918 |
+
set [speed v] to (-5)
|
919 |
+
show variable [speed v]
|
920 |
+
forever
|
921 |
+
change x by ([speed v])
|
922 |
+
if <((x position)) < (-240)> then
|
923 |
+
go to x: (240) y: (-100)
|
924 |
+
end
|
925 |
+
end
|
926 |
+
end
|
927 |
+
```
|
928 |
+
6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json.
|
929 |
+
7. **Output**:
|
930 |
+
Return **only** a JSON object, using double quotes everywhere:
|
931 |
+
```json
|
932 |
+
{{
|
933 |
+
"refined_logic": "…your fully‑formatted pseudo‑code here…"
|
934 |
+
}}
|
935 |
+
```
|
936 |
+
"""
|
937 |
+
|
938 |
+
|
939 |
+
refinement_prompt = f"""
|
940 |
+
You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).
|
941 |
+
Review the following plan for '{target_name}' triggered by '{event}'.
|
942 |
+
|
943 |
+
Game Description:
|
944 |
+
{game_description}
|
945 |
+
|
946 |
+
--- Scratch 3.0 Block Reference ---
|
947 |
+
### Hat Blocks
|
948 |
+
Description: {hat_description}
|
949 |
+
Blocks:
|
950 |
+
{hat_opcodes_functionalities}
|
951 |
+
|
952 |
+
### Boolean Blocks
|
953 |
+
Description: {boolean_description}
|
954 |
+
Blocks:
|
955 |
+
{boolean_opcodes_functionalities}
|
956 |
+
|
957 |
+
### C Blocks
|
958 |
+
Description: {c_description}
|
959 |
+
Blocks:
|
960 |
+
{c_opcodes_functionalities}
|
961 |
+
|
962 |
+
### Cap Blocks
|
963 |
+
Description: {cap_description}
|
964 |
+
Blocks:
|
965 |
+
{cap_opcodes_functionalities}
|
966 |
+
|
967 |
+
### Reporter Blocks
|
968 |
+
Description: {reporter_description}
|
969 |
+
Blocks:
|
970 |
+
{reporter_opcodes_functionalities}
|
971 |
+
|
972 |
+
### Stack Blocks
|
973 |
+
Description: {stack_description}
|
974 |
+
Blocks:
|
975 |
+
{stack_opcodes_functionalities}
|
976 |
+
-----------------------------------
|
977 |
+
Current Plan Details:
|
978 |
+
- Event (Hat Block Opcode): {event}
|
979 |
+
- Overall plans made on {target_name} are in the list: {overall_plans_in_sprite}
|
980 |
+
- Associated Opcodes by Category: {json.dumps(opcodes, indent=2)}
|
981 |
+
- Reference code and functionality and logics script for to check is script correct: {combined_blocks}
|
982 |
+
- Current Logic Description to Update if required: "{original_logic}"
|
983 |
+
|
984 |
+
Your task is to:
|
985 |
+
1. **Refine the 'Logic'**: Make it more precise, accurate, and fully aligned with the game mechanics described in the 'Game Description'. Ensure it uses Scratch-consistent verbs and phrasing. **Do NOT** use double quotes within the 'logic' string itself for values.
|
986 |
+
2. The logic should build flow which can correctly describe motion or flow for e.g jumping, running, flying and other mechanics.
|
987 |
+
3. **'logic'**: a natural language breakdown of each step taken after the event, formatted as a multi-line string representing pseudo-code. Ensure clarity and granularity—each described action should map closely to a Scratch block or tight sequence.
|
988 |
+
4 The understading of the block generation and important instruction:
|
989 |
+
some understanding of pseudo-code as given:
|
990 |
+
logic:
|
991 |
+
"
|
992 |
+
when green flag clicked
|
993 |
+
go to x: (240) y: (-135)
|
994 |
+
set [score v] to (1)
|
995 |
+
set [speed v] to (1)
|
996 |
+
show variable [score v]
|
997 |
+
show variable [speed v]
|
998 |
+
forever
|
999 |
+
glide (2) seconds to x: (-240) y: (-135)
|
1000 |
+
if <((x position)) < (-235)> then
|
1001 |
+
set x to (240)
|
1002 |
+
end
|
1003 |
+
if <touching [Sprite1 v]?> then
|
1004 |
+
broadcast [Game Over v]
|
1005 |
+
stop [all v]
|
1006 |
+
end
|
1007 |
+
end
|
1008 |
+
end
|
1009 |
+
"
|
1010 |
+
* **Block Value Types:**
|
1011 |
+
|
1012 |
+
* `[variable v]` (e.g., `[score v]`, `[speed v]`): Denotes a **variable** within a block, such as in `set [score v] to (1)` or `show variable [speed v]`. It's distinct from selecting an option from a dropdown.
|
1013 |
+
* `[option v]` (e.g., `[space v]` in `when [space v] key pressed`): Denotes a **dropdown option** selected within a block.
|
1014 |
+
* `(10)`: Denotes a **numeric value** input into a block, like in `move (50)`.
|
1015 |
+
* `(hello)`: Denotes an **alphanumeric string value** input into a block, like in `say (hello)`.
|
1016 |
+
* `([level v])`: Denotes a **variable used as an input value** for another block, like in `set x to ([level v])`.
|
1017 |
+
* `((x position))`: Denotes an **inbuilt reporter block** used as an input value, like in `go to ((x position))`.
|
1018 |
+
* `(input)`: Parentheses `()` can enclose a value, a variable, or another block that serves as an input.
|
1019 |
+
* `<condition>`: Angle brackets `<>` enclose a **Boolean block**.
|
1020 |
+
* Nested Boolean blocks: Some Boolean blocks can contain other Boolean blocks, for example, `<not <condition>>`, `<<condition> and <condition>>`, or `<<condition> or <condition>>`.
|
1021 |
+
* **Structuring Pseudo-code:**
|
1022 |
+
* **Indentation:** Use consistent indentation to show block nesting (e.g., blocks inside a `forever` loop or an `if` statement).
|
1023 |
+
* **Stacking:** Blocks that are stacked sequentially should be on new lines without additional indentation.
|
1024 |
+
* **Comments:** Do not include comments or explanations within the pseudo-code itself; the 'logic' should be self-explanatory based on Scratch block structure.
|
1025 |
+
[Note: This understanding will let you know about the pseudo-code generation.]
|
1026 |
+
5. Few Example of content of logics inside for a specific plan:
|
1027 |
+
- example 1[continuos moving objects]: "
|
1028 |
+
when green flag clicked
|
1029 |
+
go to x: (240) y: (-100)
|
1030 |
+
set [speed v] to (-5)
|
1031 |
+
show variable [speed v]
|
1032 |
+
forever
|
1033 |
+
change x by ([speed v])
|
1034 |
+
if <((x position)) < (-240)> then
|
1035 |
+
go to x: (240) y: (-100)
|
1036 |
+
end
|
1037 |
+
end
|
1038 |
+
end
|
1039 |
+
"
|
1040 |
+
- example 2[jumping script of an plan]: "
|
1041 |
+
when [space v] key pressed
|
1042 |
+
if <((y position)) = (-100)> then
|
1043 |
+
repeat (5)
|
1044 |
+
change y by (100)
|
1045 |
+
wait (0.1) seconds
|
1046 |
+
change y by (-100)
|
1047 |
+
wait (0.1) seconds
|
1048 |
+
end
|
1049 |
+
end
|
1050 |
+
end
|
1051 |
+
"
|
1052 |
+
- example 3[score decrement on collision]: "
|
1053 |
+
when green flag clicked
|
1054 |
+
set [score v] to (0)
|
1055 |
+
forever
|
1056 |
+
if <touching [other sprite v]?> then
|
1057 |
+
change [score v] by (-1)
|
1058 |
+
wait (0.1) seconds
|
1059 |
+
end
|
1060 |
+
end
|
1061 |
+
end
|
1062 |
+
end
|
1063 |
+
"
|
1064 |
+
6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json.
|
1065 |
+
7. Output a JSON object and [NOTE: use double quates always ""]:
|
1066 |
+
- `refined_logic`: The refined logic string.
|
1067 |
+
Output ONLY the JSON object.
|
1068 |
+
"""
|
1069 |
+
|
1070 |
+
|
1071 |
+
[Overall Action Plan received at the block generator]: {
|
1072 |
+
"Stage": {
|
1073 |
+
"description": "Background and global game state management, including broadcasts, rewards, and score.",
|
1074 |
+
"plans": [
|
1075 |
+
{
|
1076 |
+
"event": "event_whenflagclicked",
|
1077 |
+
"logic": "when green flag clicked\n set [score v] to (0)\n set [lives v] to (3)\n show variable [score v]\n show variable [lives v]\n broadcast [Game Start v]",
|
1078 |
+
"motion": [],
|
1079 |
+
"control": [],
|
1080 |
+
"operator": [],
|
1081 |
+
"sensing": [],
|
1082 |
+
"looks": [],
|
1083 |
+
"sounds": [],
|
1084 |
+
"events": [
|
1085 |
+
"event_broadcast"
|
1086 |
+
],
|
1087 |
+
"data": [
|
1088 |
+
"data_setvariableto",
|
1089 |
+
"data_showvariable"
|
1090 |
+
],
|
1091 |
+
"opcode_counts": [
|
1092 |
+
{
|
1093 |
+
"opcode": "event_whenflagclicked",
|
1094 |
+
"count": 1
|
1095 |
+
},
|
1096 |
+
{
|
1097 |
+
"opcode": "data_setvariableto",
|
1098 |
+
"count": 2
|
1099 |
+
},
|
1100 |
+
{
|
1101 |
+
"opcode": "data_showvariable",
|
1102 |
+
"count": 2
|
1103 |
+
},
|
1104 |
+
{
|
1105 |
+
"opcode": "event_broadcast",
|
1106 |
+
"count": 1
|
1107 |
+
}
|
1108 |
+
]
|
1109 |
+
},
|
1110 |
+
{
|
1111 |
+
"event": "event_whenbroadcastreceived",
|
1112 |
+
"logic": "when I receive [Game Over v]\n broadcast [Reset Game v]\n set [score v] to (0)\n set [lives v] to (3)",
|
1113 |
+
"motion": [],
|
1114 |
+
"control": [],
|
1115 |
+
"operator": [],
|
1116 |
+
"sensing": [],
|
1117 |
+
"looks": [],
|
1118 |
+
"sounds": [],
|
1119 |
+
"events": [],
|
1120 |
+
"data": [
|
1121 |
+
"data_setvariableto"
|
1122 |
+
],
|
1123 |
+
"opcode_counts": [
|
1124 |
+
{
|
1125 |
+
"opcode": "event_whenbroadcastreceived",
|
1126 |
+
"count": 1
|
1127 |
+
},
|
1128 |
+
{
|
1129 |
+
"opcode": "event_broadcast",
|
1130 |
+
"count": 1
|
1131 |
+
},
|
1132 |
+
{
|
1133 |
+
"opcode": "data_setvariableto",
|
1134 |
+
"count": 2
|
1135 |
+
}
|
1136 |
+
]
|
1137 |
+
}
|
1138 |
+
]
|
1139 |
+
},
|
1140 |
+
"Cat": {
|
1141 |
+
"description": "Main character (cat) actions",
|
1142 |
+
"plans": [
|
1143 |
+
{
|
1144 |
+
"event": "event_whenflagclicked",
|
1145 |
+
"logic": "when green flag clicked\n go to x: (0) y: (-100)\n set [speed v] to (0)",
|
1146 |
+
"motion": [
|
1147 |
+
"motion_gotoxy"
|
1148 |
+
],
|
1149 |
+
"control": [],
|
1150 |
+
"operator": [],
|
1151 |
+
"sensing": [],
|
1152 |
+
"looks": [],
|
1153 |
+
"sounds": [],
|
1154 |
+
"events": [],
|
1155 |
+
"data": [],
|
1156 |
+
"opcode_counts": [
|
1157 |
+
{
|
1158 |
+
"opcode": "event_whenflagclicked",
|
1159 |
+
"count": 1
|
1160 |
+
},
|
1161 |
+
{
|
1162 |
+
"opcode": "motion_gotoxy",
|
1163 |
+
"count": 1
|
1164 |
+
},
|
1165 |
+
{
|
1166 |
+
"opcode": "data_setvariableto",
|
1167 |
+
"count": 1
|
1168 |
+
}
|
1169 |
+
]
|
1170 |
+
},
|
1171 |
+
{
|
1172 |
+
"event": "event_whenkeypressed",
|
1173 |
+
"logic": "when [right arrow v] key pressed\n change x by (10)",
|
1174 |
+
"motion": [
|
1175 |
+
"motion_changexby"
|
1176 |
+
],
|
1177 |
+
"control": [],
|
1178 |
+
"operator": [],
|
1179 |
+
"sensing": [],
|
1180 |
+
"looks": [],
|
1181 |
+
"sounds": [],
|
1182 |
+
"events": [],
|
1183 |
+
"data": [],
|
1184 |
+
"opcode_counts": [
|
1185 |
+
{
|
1186 |
+
"opcode": "event_whenkeypressed",
|
1187 |
+
"count": 1
|
1188 |
+
},
|
1189 |
+
{
|
1190 |
+
"opcode": "motion_changexby",
|
1191 |
+
"count": 1
|
1192 |
+
}
|
1193 |
+
]
|
1194 |
+
},
|
1195 |
+
{
|
1196 |
+
"event": "event_whenkeypressed",
|
1197 |
+
"logic": "when [left arrow v] key pressed\n change x by (-10)",
|
1198 |
+
"motion": [
|
1199 |
+
"motion_changexby"
|
1200 |
+
],
|
1201 |
+
"control": [],
|
1202 |
+
"operator": [],
|
1203 |
+
"sensing": [],
|
1204 |
+
"looks": [],
|
1205 |
+
"sounds": [],
|
1206 |
+
"events": [],
|
1207 |
+
"data": [],
|
1208 |
+
"opcode_counts": [
|
1209 |
+
{
|
1210 |
+
"opcode": "event_whenkeypressed",
|
1211 |
+
"count": 1
|
1212 |
+
},
|
1213 |
+
{
|
1214 |
+
"opcode": "motion_changexby",
|
1215 |
+
"count": 1
|
1216 |
+
}
|
1217 |
+
]
|
1218 |
+
},
|
1219 |
+
{
|
1220 |
+
"event": "event_whenkeypressed",
|
1221 |
+
"logic": "when [space v] key pressed\n if <((y position)) = (-100)> then \n forever\n change y by (10)\n wait (0.1) seconds\n change y by (-10)\n wait (0.1) seconds\n end\n end",
|
1222 |
+
"motion": [
|
1223 |
+
"motion_changeyby"
|
1224 |
+
],
|
1225 |
+
"control": [
|
1226 |
+
"control_forever",
|
1227 |
+
"control_if"
|
1228 |
+
],
|
1229 |
+
"operator": [],
|
1230 |
+
"sensing": [],
|
1231 |
+
"looks": [],
|
1232 |
+
"sounds": [],
|
1233 |
+
"events": [],
|
1234 |
+
"data": [],
|
1235 |
+
"opcode_counts": [
|
1236 |
+
{
|
1237 |
+
"opcode": "event_whenkeypressed",
|
1238 |
+
"count": 1
|
1239 |
+
},
|
1240 |
+
{
|
1241 |
+
"opcode": "control_if",
|
1242 |
+
"count": 1
|
1243 |
+
},
|
1244 |
+
{
|
1245 |
+
"opcode": "control_forever",
|
1246 |
+
"count": 1
|
1247 |
+
},
|
1248 |
+
{
|
1249 |
+
"opcode": "motion_changeyby",
|
1250 |
+
"count": 2
|
1251 |
+
}
|
1252 |
+
]
|
1253 |
+
}
|
1254 |
+
]
|
1255 |
+
},
|
1256 |
+
"ball": {
|
1257 |
+
"description": "Obstacle movement and interaction",
|
1258 |
+
"plans": [
|
1259 |
+
{
|
1260 |
+
"event": "event_whenflagclicked",
|
1261 |
+
"logic": "when green flag clicked\n go to x: (0) y: (100)\n set [ballSpeed v] to (5)\n forever\n glide (2) seconds to x: (-240) y: (100)\n
|
1262 |
+
if <(x position) < (-235)> then\n set x to (240)\n end\n if <touching [Cat v]?> then\n broadcast [Game Over v]\n stop [all v]\n end\n end",
|
1263 |
+
"motion": [
|
1264 |
+
"motion_gotoxy",
|
1265 |
+
"motion_glidesecstoxy",
|
1266 |
+
"motion_xposition",
|
1267 |
+
"motion_setx"
|
1268 |
+
],
|
1269 |
+
"control": [
|
1270 |
+
"control_forever",
|
1271 |
+
"control_if",
|
1272 |
+
"control_stop"
|
1273 |
+
],
|
1274 |
+
"operator": [
|
1275 |
+
"operator_lt"
|
1276 |
+
],
|
1277 |
+
"sensing": [
|
1278 |
+
"sensing_istouching"
|
1279 |
+
],
|
1280 |
+
"looks": [],
|
1281 |
+
"sounds": [],
|
1282 |
+
"events": [
|
1283 |
+
"event_broadcast"
|
1284 |
+
],
|
1285 |
+
"data": [],
|
1286 |
+
"opcode_counts": [
|
1287 |
+
{
|
1288 |
+
"opcode": "event_whenflagclicked",
|
1289 |
+
"count": 1
|
1290 |
+
},
|
1291 |
+
{
|
1292 |
+
"opcode": "motion_gotoxy",
|
1293 |
+
"count": 1
|
1294 |
+
},
|
1295 |
+
{
|
1296 |
+
"opcode": "motion_glidesecstoxy",
|
1297 |
+
"count": 1
|
1298 |
+
},
|
1299 |
+
{
|
1300 |
+
"opcode": "motion_xposition",
|
1301 |
+
"count": 1
|
1302 |
+
},
|
1303 |
+
{
|
1304 |
+
"opcode": "motion_setx",
|
1305 |
+
"count": 1
|
1306 |
+
},
|
1307 |
+
{
|
1308 |
+
"opcode": "control_forever",
|
1309 |
+
"count": 1
|
1310 |
+
},
|
1311 |
+
{
|
1312 |
+
"opcode": "control_if",
|
1313 |
+
"count": 2
|
1314 |
+
},
|
1315 |
+
{
|
1316 |
+
"opcode": "operator_lt",
|
1317 |
+
"count": 1
|
1318 |
+
},
|
1319 |
+
{
|
1320 |
+
"opcode": "sensing_istouching",
|
1321 |
+
"count": 1
|
1322 |
+
},
|
1323 |
+
{
|
1324 |
+
"opcode": "event_broadcast",
|
1325 |
+
"count": 1
|
1326 |
+
},
|
1327 |
+
{
|
1328 |
+
"opcode": "control_stop",
|
1329 |
+
"count": 1
|
1330 |
+
},
|
1331 |
+
{
|
1332 |
+
"opcode": "data_setvariableto",
|
1333 |
+
"count": 1
|
1334 |
+
}
|
1335 |
+
]
|
1336 |
+
}
|
1337 |
+
]
|
1338 |
+
}
|
1339 |
+
}
|
1340 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenflagclicked_1': {'block_name': 'when green flag pressed', 'block_type': 'Events', 'op_code': 'event_whenflagclicked', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': True, 'id': 'event_whenflagclicked_1', 'parent': None, 'next': 'data_setvariableto_1'}, 'data_setvariableto_1': {'block_name': 'set [my variable v] to ()', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_setvariableto', 'functionality': 'Assigns a specific value (number, string, or boolean) to a variable.', 'inputs': {'VALUE': {'kind': 'value', 'value': 0}}, 'fields': {'VARIABLE': ['score', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_setvariableto_1', 'parent': 'event_whenflagclicked_1', 'next': 'data_setvariableto_2'}, 'data_setvariableto_2': {'block_name': 'set [my variable v] to ()', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_setvariableto', 'functionality': 'Assigns a specific value (number, string, or boolean) to a variable.', 'inputs': {'VALUE': {'kind': 'value', 'value': 3}}, 'fields': {'VARIABLE': ['lives', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_setvariableto_2', 'parent': 'data_setvariableto_1', 'next': 'data_showvariable_1'}, 'data_showvariable_1': {'block_name': 'show variable [my variable v]', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_showvariable', 'functionality': "Makes a variable's monitor visible on the stage.", 'inputs': {}, 'fields': {'VARIABLE': ['score', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_showvariable_1', 'parent': 'data_setvariableto_2', 'next': 'data_showvariable_2'}, 'data_showvariable_2': {'block_name': 'show variable [my variable v]', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_showvariable', 'functionality': "Makes a variable's monitor visible on the stage.", 'inputs': {}, 'fields': {'VARIABLE': ['lives', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_showvariable_2', 'parent': 'data_showvariable_1', 'next': 'event_broadcast_1'}, 'event_broadcast_1': {'block_name': 'broadcast ()', 'block_type': 'Events', 'block_shape': 'Stack Block', 'op_code': 'event_broadcast', 'functionality': "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.", 'inputs': {'BROADCAST_INPUT': {'kind': 'menu', 'option': 'Game Start'}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'event_broadcast_1', 'parent': 'data_showvariable_2', 'next': None}}
|
1341 |
+
2025-07-25 14:08:27,550 - __main__ - INFO - Action blocks added for sprite 'Stage' by OverallBlockBuilderNode.
|
1342 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenbroadcastreceived_1': {'block_name': 'when I receive ()', 'block_type': 'Events', 'op_code': 'event_whenbroadcastreceived', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script upon the reception of a specific broadcast message. This mechanism facilitates indirect communication between sprites or the stage.', 'inputs': {}, 'fields': {'BROADCAST_OPTION': ['Game Over ', None]}, 'shadow': False, 'topLevel': True, 'id': 'event_whenbroadcastreceived_1', 'parent': None, 'next': 'event_broadcast_1'}, 'event_broadcast_1': {'block_name': 'broadcast ()', 'block_type': 'Events', 'block_shape': 'Stack Block', 'op_code': 'event_broadcast', 'functionality': "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.", 'inputs': {'BROADCAST_INPUT': {'kind': 'menu', 'option': 'Reset Game'}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'event_broadcast_1', 'parent': 'event_whenbroadcastreceived_1', 'next': 'data_setvariableto_1'}, 'data_setvariableto_1': {'block_name': 'set [my variable v] to ()', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_setvariableto', 'functionality': 'Assigns a specific value (number, string, or boolean) to a variable.', 'inputs': {'VALUE': {'kind': 'value', 'value': 0}}, 'fields': {'VARIABLE': ['score', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_setvariableto_1', 'parent': 'event_broadcast_1', 'next': 'data_setvariableto_2'}, 'data_setvariableto_2': {'block_name': 'set [my variable v] to ()', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_setvariableto', 'functionality': 'Assigns a specific value (number, string, or boolean) to a variable.', 'inputs': {'VALUE': {'kind': 'value', 'value': 3}}, 'fields': {'VARIABLE': ['lives', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_setvariableto_2', 'parent': 'data_setvariableto_1', 'next': None}}
|
1343 |
+
2025-07-25 14:08:27,551 - __main__ - INFO - Action blocks added for sprite 'Stage' by OverallBlockBuilderNode.
|
1344 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenflagclicked_1': {'block_name': 'when green flag pressed', 'block_type': 'Events', 'op_code': 'event_whenflagclicked', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': True, 'id': 'event_whenflagclicked_1', 'parent': None, 'next': 'motion_gotoxy_1'}, 'motion_gotoxy_1': {'block_name': 'go to x: () y: ()', 'block_type': 'Motion', 'block_shape': 'Stack Block', 'op_code': 'motion_gotoxy', 'functionality': 'Moves the sprite to the specified X and Y coordinates on the stage.', 'inputs': {'X': {'kind': 'value', 'value': 0}, 'Y': {'kind': 'value', 'value': -100}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_gotoxy_1', 'parent': 'event_whenflagclicked_1', 'next': 'data_setvariableto_1'}, 'data_setvariableto_1': {'block_name': 'set [my variable v] to ()', 'block_type': 'Data', 'block_shape': 'Stack Block', 'op_code': 'data_setvariableto', 'functionality': 'Assigns a specific value (number, string, or boolean) to a variable.', 'inputs': {'VALUE': {'kind': 'value', 'value': 0}}, 'fields': {'VARIABLE': ['speed', None]}, 'shadow': False, 'topLevel': False, 'id': 'data_setvariableto_1', 'parent': 'motion_gotoxy_1', 'next': None}}
|
1345 |
+
2025-07-25 14:08:27,552 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode.
|
1346 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenkeypressed_1': {'block_name': 'when () key pressed', 'block_type': 'Events', 'op_code': 'event_whenkeypressed', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script when a specified keyboard key is pressed.', 'inputs': {}, 'fields': {'KEY_OPTION': ['right arrow ', None]}, 'shadow': False, 'topLevel': True, 'id': 'event_whenkeypressed_1', 'parent': None, 'next': 'motion_changexby_1'}, 'motion_changexby_1': {'block_name': 'change x by ()', 'block_type': 'Motion', 'block_shape': 'Stack Block', 'op_code': 'motion_changexby', 'functionality': "Changes the sprite's X-coordinate by the specified amount, moving it horizontally.", 'inputs': {'DX': {'kind': 'value', 'value': 10}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_changexby_1', 'parent': 'event_whenkeypressed_1', 'next': None}}
|
1347 |
+
2025-07-25 14:08:27,553 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode.
|
1348 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenkeypressed_1': {'block_name': 'when () key pressed', 'block_type': 'Events', 'op_code': 'event_whenkeypressed', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script when a specified keyboard key is pressed.', 'inputs': {}, 'fields': {'KEY_OPTION': ['left arrow ', None]}, 'shadow': False, 'topLevel': True, 'id': 'event_whenkeypressed_1', 'parent': None, 'next': 'motion_changexby_1'}, 'motion_changexby_1': {'block_name': 'change x by ()', 'block_type': 'Motion', 'block_shape': 'Stack Block', 'op_code': 'motion_changexby', 'functionality': "Changes the sprite's X-coordinate by the specified amount, moving it horizontally.", 'inputs': {'DX': {'kind': 'value', 'value': -10}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_changexby_1', 'parent': 'event_whenkeypressed_1', 'next': None}}
|
1349 |
+
2025-07-25 14:08:27,554 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode.
|
1350 |
+
[THE CONDA MATCH]------------->((y position)) = (-100)
|
1351 |
+
the stmt was this ((y position)) = (-100) and parsed was this ((y position)) = (-100)
|
1352 |
+
[ALL OPCODE BLCOKS KEY 2]: {'event_whenkeypressed_1': {'block_name': 'when () key pressed', 'block_type': 'Events', 'op_code': 'event_whenkeypressed', 'block_shape': 'Hat Block', 'functionality': 'This Hat block initiates the script when a specified keyboard key is pressed.', 'inputs': {}, 'fields': {'KEY_OPTION': ['space ', None]}, 'shadow': False, 'topLevel': True, 'id': 'event_whenkeypressed_1', 'parent': None, 'next': 'control_if_1'}, 'control_if_1': {'block_name': 'if <> then', 'block_type': 'Control', 'block_shape': 'C-Block', 'op_code': 'control_if', 'functionality': 'Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]', 'inputs': {'CONDITION': {'kind': 'block', 'block': 'operator_equals_1'}, 'SUBSTACK': [2, 'control_forever_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'control_if_1', 'parent': 'event_whenkeypressed_1', 'next': None}, 'motion_yposition_1': {'block_name': '(y position)', 'block_type': 'Motion', 'block_shape': 'Reporter Block', 'op_code': 'motion_yposition', 'functionality': 'Reports the current Y coordinate of the sprite on the stage.[NOTE: not used in stage/backdrops]', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_yposition_1', 'parent': 'operator_equals_1', 'next': None}, 'operator_equals_1': {'block_name': '<() = ()>', 'block_type': 'operator', 'block_shape': 'Boolean Block', 'op_code': 'operator_equals', 'functionality': 'Checks if two values are equal.', 'inputs': {'OPERAND1': {'kind': 'block', 'block': 'motion_yposition_1'}, 'OPERAND2': {'kind': 'value', 'value': -100}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'operator_equals_1', 'parent': 'control_if_1', 'next': None}, 'control_forever_1': {'block_name': 'forever', 'block_type': 'Control', 'block_shape': 'C-Block', 'op_code': 'control_forever', 'functionality': 'Continuously runs the blocks inside it.', 'inputs': {'SUBSTACK': [2, 'motion_changeyby_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'control_forever_1', 'parent': 'control_if_1', 'next': None}, 'motion_changeyby_1': {'block_name': 'change y by ()', 'block_type': 'Motion', 'block_shape': 'Stack Block', 'op_code': 'motion_changeyby', 'functionality': "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.", 'inputs': {'DY': {'kind': 'value', 'value': 10}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_changeyby_1', 'parent': 'control_forever_1', 'next': 'control_wait_1'}, 'control_wait_1': {'block_name': 'wait () seconds', 'block_type': 'Control', 'block_shape': 'Stack Block', 'op_code': 'control_wait', 'functionality': 'Pauses the script for a specified duration.', 'inputs': {'DURATION': {'kind': 'value', 'value': 0.1}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'control_wait_1', 'parent': 'motion_changeyby_1', 'next': 'motion_changeyby_2'}, 'motion_changeyby_2': {'block_name': 'change y by ()', 'block_type': 'Motion', 'block_shape': 'Stack Block', 'op_code': 'motion_changeyby', 'functionality': "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.", 'inputs': {'DY': {'kind': 'value', 'value': -10}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'motion_changeyby_2', 'parent': 'control_wait_1', 'next': 'control_wait_2'}, 'control_wait_2': {'block_name': 'wait () seconds', 'block_type': 'Control', 'block_shape': 'Stack Block', 'op_code': 'control_wait', 'functionality': 'Pauses the script for a specified duration.', 'inputs': {'DURATION': {'kind': 'value', 'value': 0.1}}, 'fields': {}, 'shadow': False, 'topLevel': False, 'id': 'control_wait_2', 'parent': 'motion_changeyby_2', 'next': None}}
|
1353 |
+
2025-07-25 14:08:27,557 - __main__ - INFO - Action blocks added for sprite 'Cat' by OverallBlockBuilderNode.
|
1354 |
+
Error generating plan from blocks: Can't parse reporter or value: 2) seconds to x: (
|
1355 |
+
2025-07-25 14:08:27,560 - __main__ - INFO - Action blocks added for sprite 'ball' by OverallBlockBuilderNode.
|
1356 |
+
2025-07-25 14:08:27,562 - __main__ - INFO - Final project JSON saved to generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020\project.json
|
1357 |
+
2025-07-25 14:08:27,586 - __main__ - INFO - Project folder zipped to: D:\DEV PATEL\2025\scratch_VLM\scratch_agent\generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020.zip
|
1358 |
+
2025-07-25 14:08:27,586 - __main__ - INFO - Renamed D:\DEV PATEL\2025\scratch_VLM\scratch_agent\generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020.zip to generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3
|
1359 |
+
2025-07-25 14:08:27,587 - __main__ - INFO - Successfully created SB3 file: generated_projects\8f9aca5d-60b6-4ca3-b9c9-d69410033020.sb3
|
1360 |
+
2025-07-25 14:08:27,587 - werkzeug - INFO - 127.0.0.1 - - [25/Jul/2025 14:08:27] "POST /generate_game HTTP/1.1" 200 -
|
1361 |
+
|
1362 |
+
Raw response from LLM [OverallPlannerNode 2]: ```json
|
1363 |
+
{
|
1364 |
+
"action_overall_flow": {
|
1365 |
+
"Stage": {
|
1366 |
+
"description": "Background and global game state management, including broadcasts, rewards, and score.",
|
1367 |
+
"plans": [
|
1368 |
+
{
|
1369 |
+
"event": "event_whenflagclicked",
|
1370 |
+
"logic": "when green flag clicked\nswitch backdrop to [blue sky v]\nset [score v] to (0)\nshow variable [score v]\nbroadcast [Game Start v]",
|
1371 |
+
"motion": [],
|
1372 |
+
"control": [],
|
1373 |
+
"operator": [],
|
1374 |
+
"sensing": [],
|
1375 |
+
"looks": [
|
1376 |
+
"looks_switchbackdropto"
|
1377 |
+
],
|
1378 |
+
"sounds": [],
|
1379 |
+
"events": [
|
1380 |
+
"event_broadcast"
|
1381 |
+
],
|
1382 |
+
"data": [
|
1383 |
+
"data_setvariableto",
|
1384 |
+
"data_showvariable"
|
1385 |
+
]
|
1386 |
+
},
|
1387 |
+
{
|
1388 |
+
"event": "event_whenbroadcastreceived",
|
1389 |
+
"logic": "when I receive [Game Over v]\nif <(score) > ([High Score v])> then\nset [High Score v] to (score)\nend\nswitch backdrop to [Game Over v]",
|
1390 |
+
"motion": [],
|
1391 |
+
"control": [
|
1392 |
+
"control_if"
|
1393 |
+
],
|
1394 |
+
"operator": [
|
1395 |
+
"operator_gt"
|
1396 |
+
],
|
1397 |
+
"sensing": [],
|
1398 |
+
"looks": [
|
1399 |
+
"looks_switchbackdropto"
|
1400 |
+
],
|
1401 |
+
"sounds": [],
|
1402 |
+
"events": [],
|
1403 |
+
"data": [
|
1404 |
+
"data_setvariableto"
|
1405 |
+
]
|
1406 |
+
}
|
1407 |
+
]
|
1408 |
+
},
|
1409 |
+
"Cat": {
|
1410 |
+
"description": "Main character (cat) actions",
|
1411 |
+
"plans": [
|
1412 |
+
{
|
1413 |
+
"event": "event_whenflagclicked",
|
1414 |
+
"logic": "when green flag clicked\ngo to x: (0) y: (-120)\nend",
|
1415 |
+
"motion": [
|
1416 |
+
"motion_gotoxy"
|
1417 |
+
],
|
1418 |
+
"control": [],
|
1419 |
+
"operator": [],
|
1420 |
+
"sensing": [],
|
1421 |
+
"looks": [],
|
1422 |
+
"sounds": [],
|
1423 |
+
"events": [],
|
1424 |
+
"data": []
|
1425 |
+
},
|
1426 |
+
{
|
1427 |
+
"event": "event_whenkeypressed",
|
1428 |
+
"logic": "when [space v] key pressed\nif <((y position)) = (-120)> then\nrepeat (10)\nchange y by (10)\nwait (0.1) seconds\nchange y by (-10)\nwait (0.1) seconds\nend\nend",
|
1429 |
+
"motion": [
|
1430 |
+
"motion_changeyby"
|
1431 |
+
],
|
1432 |
+
"control": [
|
1433 |
+
"control_repeat",
|
1434 |
+
"control_if",
|
1435 |
+
"control_wait"
|
1436 |
+
],
|
1437 |
+
"operator": [],
|
1438 |
+
"sensing": [],
|
1439 |
+
"looks": [],
|
1440 |
+
"sounds": [],
|
1441 |
+
"events": [],
|
1442 |
+
"data": []
|
1443 |
+
}
|
1444 |
+
]
|
1445 |
+
},
|
1446 |
+
"ball": {
|
1447 |
+
"description": "Obstacle movement and interaction",
|
1448 |
+
"plans": [
|
1449 |
+
{
|
1450 |
+
"event": "event_whenflagclicked",
|
1451 |
+
"logic": "when green flag clicked\ngo to x: (130) y: (0)\nforever\nchange x by (-5)\nif <((x position)) < (-130)> then\nset x to (130)\nend\nif <touching [Cat v]?> then\nbroadcast [Game Over v]\nstop [all v]\nend\nend",
|
1452 |
+
"motion": [
|
1453 |
+
"motion_gotoxy",
|
1454 |
+
"motion_changexby",
|
1455 |
+
"motion_setx"
|
1456 |
+
],
|
1457 |
+
"control": [
|
1458 |
+
"control_forever",
|
1459 |
+
"control_if",
|
1460 |
+
"control_stop"
|
1461 |
+
],
|
1462 |
+
"operator": [],
|
1463 |
+
"sensing": [
|
1464 |
+
"sensing_touchingobject"
|
1465 |
+
],
|
1466 |
+
"looks": [],
|
1467 |
+
"sounds": [],
|
1468 |
+
"events": [
|
1469 |
+
"event_broadcast"
|
1470 |
+
],
|
1471 |
+
"data": []
|
1472 |
+
}
|
1473 |
+
]
|
1474 |
+
}
|
1475 |
+
}
|
1476 |
+
}
|
1477 |
+
```
|
1478 |
+
|
1479 |
+
[OVREALL REFINED LOGIC]: {'Stage': {'description': 'Background and global game state management, including broadcasts, rewards, and score.', 'plans': [{'event': 'event_whenflagclicked', 'logic': '\nwhen green flag clicked\n switch backdrop to [blue sky v]\n set [score v] to (0)\n show variable [score v]\n broadcast [Game Start v]\nend\n', 'motion': [], 'control': [], 'operator': [], 'sensing': [], 'looks': ['looks_switchbackdropto'], 'sounds': [], 'events': ['event_broadcast'], 'data': ['data_setvariableto', 'data_showvariable']}, {'event': 'event_whenbroadcastreceived', 'logic': '\n when I receive [Game Over v]\n if <((score) > (([High Score v])))> then\n set [High Score v] to ((score) v)\n end\n switch backdrop to [Game Over v]\n end\n', 'motion': [], 'control': ['control_if'], 'operator': ['operator_gt'], 'sensing': [], 'looks': ['looks_switchbackdropto'], 'sounds': [], 'events': [], 'data': ['data_setvariableto']}]}, 'Cat': {'description': 'Main character (cat) actions', 'plans': [{'event': 'event_whenflagclicked', 'logic': '\nwhen green flag clicked\n go to x: (0) y: (-120)\n set [score v] to (0)\n show variable [score v]\n forever\n change x by (5)\n if <((x position)) > (240)> then\n go to x: (-240) y: ((y position))\n end\n if <((y position)) < (-150)> then\n change y by (5)\n end\n if <((y position)) > (150)> then\n change y by (-5)\n end\n end\nend\n', 'motion': ['motion_gotoxy'], 'control': [], 'operator': [], 'sensing': [], 'looks': [], 'sounds': [], 'events': [], 'data': []}, {'event': 'event_whenkeypressed', 'logic': '\nwhen [space v] key pressed\n if <((y position)) = (-120)> then\n set [jump height v] to (10)\n set [gravity v] to (1)\n repeat (10)\n change y by (([jump height v]) * (1))\n set [jump height v] to (([jump height v]) - ([gravity v])))\n wait (0.1) seconds\n change y by (-(([jump height v]) * (1)))\n wait (0.1) seconds\n end\n end\nend\n', 'motion': ['motion_changeyby'], 'control': ['control_repeat', 'control_if', 'control_wait'], 'operator': [], 'sensing': [], 'looks': [], 'sounds': [], 'events': [], 'data': []}]}, 'ball': {'description': 'Obstacle movement and interaction', 'plans': [{'event': 'event_whenflagclicked', 'logic': '\nwhen green flag clicked\ngo to x: (130) y: (0)\nforever\nchange x by (-5)\nif <((x position)) < (-130)> then\nset x to (130)\nend\nif <touching [Cat v]?> then\nbroadcast [Game Over v]\nstop [all v]\nend\nend\n', 'motion': ['motion_gotoxy', 'motion_changexby', 'motion_setx'], 'control': ['control_forever', 'control_if', 'control_stop'], 'operator': [], 'sensing': ['sensing_touchingobject'], 'looks': [], 'sounds': [], 'events': ['event_broadcast'], 'data': []}]}}
|
1480 |
+
|
1481 |
+
|
1482 |
+
[Refined Action Plan]: {
|
1483 |
+
"action_overall_flow": {
|
1484 |
+
"Stage": {
|
1485 |
+
"description": "Background and global game state management, including broadcasts, rewards, and score.",
|
1486 |
+
"plans": [
|
1487 |
+
{
|
1488 |
+
"event": "event_whenflagclicked",
|
1489 |
+
"logic": "when green flag clicked\n set [score v] to (0)\n show variable [score v]\n broadcast [Game Start v]",
|
1490 |
+
"control": [
|
1491 |
+
"control_broadcast"
|
1492 |
+
],
|
1493 |
+
"data": [
|
1494 |
+
"data_setvariableto",
|
1495 |
+
"data_showvariable"
|
1496 |
+
],
|
1497 |
+
"events": [
|
1498 |
+
"event_broadcast"
|
1499 |
+
]
|
1500 |
+
},
|
1501 |
+
{
|
1502 |
+
"event": "event_whenbroadcastreceived",
|
1503 |
+
"logic": "when I receive [Game Over v]\n if <(score) > (High Score)> then\n set [High Score v] to (score)\n end\n switch backdrop to [Game Over v]",
|
1504 |
+
"control": [
|
1505 |
+
"control_if"
|
1506 |
+
],
|
1507 |
+
"operator": [
|
1508 |
+
"operator_gt"
|
1509 |
+
],
|
1510 |
+
"looks": [
|
1511 |
+
"looks_switchbackdropto"
|
1512 |
+
],
|
1513 |
+
"data": [
|
1514 |
+
"data_setvariableto"
|
1515 |
+
]
|
1516 |
+
}
|
1517 |
+
]
|
1518 |
+
},
|
1519 |
+
"Cat": {
|
1520 |
+
"description": "Main character (cat) actions",
|
1521 |
+
"plans": [
|
1522 |
+
{
|
1523 |
+
"event": "event_whenflagclicked",
|
1524 |
+
"logic": "when green flag clicked\n go to x: (0) y: (-50)\n set [speed v] to (5)\n forever\n change x by ([speed v])\n if <((x position)) > (240)> then\n set x to (-240)\n end\n if <((x position)) < (-240)> then\n set x to (240)\n end\n end",
|
1525 |
+
"motion": [
|
1526 |
+
"motion_gotoxy",
|
1527 |
+
"motion_changexby"
|
1528 |
+
],
|
1529 |
+
"control": [
|
1530 |
+
"control_forever",
|
1531 |
+
"control_if"
|
1532 |
+
],
|
1533 |
+
"operator": [
|
1534 |
+
"operator_gt",
|
1535 |
+
"operator_lt"
|
1536 |
+
]
|
1537 |
+
},
|
1538 |
+
{
|
1539 |
+
"event": "event_whenkeypressed",
|
1540 |
+
"logic": "when [space v] key pressed\n change y by (100)\n wait (0.2) seconds\n change y by (-100)",
|
1541 |
+
"motion": [
|
1542 |
+
"motion_changeyby"
|
1543 |
+
],
|
1544 |
+
"control": [
|
1545 |
+
"control_wait"
|
1546 |
+
]
|
1547 |
+
}
|
1548 |
+
]
|
1549 |
+
},
|
1550 |
+
"ball": {
|
1551 |
+
"description": "Obstacle movement and interaction",
|
1552 |
+
"plans": [
|
1553 |
+
{
|
1554 |
+
"event": "event_whenflagclicked",
|
1555 |
+
"logic": "when green flag clicked\n go to x: (100) y: (50)\n forever\n glide (2) seconds to x: (-240) y: (50)\n if <((x position)) < (-235)> then\n set x to (240)\n end\n if <touching [Cat v]?> then\n broadcast [Game Over v]\n stop [all v]\n end\n end",
|
1556 |
+
"motion": [
|
1557 |
+
"motion_gotoxy",
|
1558 |
+
"motion_glidesecstoxy",
|
1559 |
+
"motion_xposition",
|
1560 |
+
"motion_setx"
|
1561 |
+
],
|
1562 |
+
"control": [
|
1563 |
+
"control_forever",
|
1564 |
+
"control_if",
|
1565 |
+
"control_stop"
|
1566 |
+
],
|
1567 |
+
"sensing": [
|
1568 |
+
"sensing_istouching"
|
1569 |
+
],
|
1570 |
+
"events": [
|
1571 |
+
"event_broadcast"
|
1572 |
+
]
|
1573 |
+
}
|
1574 |
+
]
|
1575 |
+
}
|
1576 |
+
}
|
1577 |
+
}
|
v2/utils/agent.py
ADDED
@@ -0,0 +1,1647 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#─── Basic imports ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
2 |
+
import os
|
3 |
+
import math
|
4 |
+
import sqlite3
|
5 |
+
import fitz # PyMuPDF for PDF parsing
|
6 |
+
import re
|
7 |
+
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
# Load environment variables from .env file
|
10 |
+
load_dotenv() # This line ensures .env variables are loaded
|
11 |
+
|
12 |
+
from langgraph.graph import START, StateGraph, MessagesState, END
|
13 |
+
from langgraph.prebuilt import tools_condition
|
14 |
+
from langgraph.prebuilt import ToolNode
|
15 |
+
from langgraph.constants import START
|
16 |
+
from langchain_core.tools import tool
|
17 |
+
from langchain.schema import SystemMessage
|
18 |
+
#from langchain.chat_models import init_chat_model
|
19 |
+
#from langgraph.prebuilt import create_react_agent
|
20 |
+
|
21 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
22 |
+
#from langchain.vectorstores import Pinecone
|
23 |
+
from langchain.tools.retriever import create_retriever_tool
|
24 |
+
#import pinecone
|
25 |
+
#from pinecone import Pinecone as PineconeClient, ServerlessSpec
|
26 |
+
#from pinecone import Index # the blocking‐call client constructor
|
27 |
+
#from pinecone import Pinecone as PineconeClient, ServerlessSpec
|
28 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
29 |
+
from langchain_community.vectorstores.pinecone import Pinecone as LC_Pinecone
|
30 |
+
|
31 |
+
# ─── Langchain Frameworks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
32 |
+
#from langchain.tools import Tool
|
33 |
+
from langchain.chat_models import ChatOpenAI
|
34 |
+
from langchain_groq import ChatGroq
|
35 |
+
from langchain_mistralai import ChatMistralAI
|
36 |
+
from langchain.agents import initialize_agent, AgentType
|
37 |
+
from langchain.schema import Document
|
38 |
+
from langchain.chains import RetrievalQA
|
39 |
+
from langchain.embeddings import OpenAIEmbeddings
|
40 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
41 |
+
from langchain.vectorstores import FAISS
|
42 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
43 |
+
from langchain.prompts import PromptTemplate
|
44 |
+
from langchain_community.document_loaders import TextLoader, PyMuPDFLoader
|
45 |
+
from langchain_community.document_loaders.wikipedia import WikipediaLoader
|
46 |
+
from langchain_community.document_loaders.arxiv import ArxivLoader
|
47 |
+
from langchain_experimental.tools.python.tool import PythonREPLTool
|
48 |
+
|
49 |
+
|
50 |
+
# ─── Memory ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
51 |
+
from langchain.agents import initialize_agent, AgentType
|
52 |
+
from langchain.tools import Tool
|
53 |
+
from typing import List, Callable
|
54 |
+
from langchain.schema import BaseMemory, AIMessage, HumanMessage, SystemMessage
|
55 |
+
from langchain.schema import HumanMessage, SystemMessage
|
56 |
+
from langchain.llms.base import LLM
|
57 |
+
from langchain.memory.chat_memory import BaseChatMemory
|
58 |
+
from pydantic import PrivateAttr
|
59 |
+
from langchain_core.messages import get_buffer_string
|
60 |
+
|
61 |
+
# ─── Image Processing ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
62 |
+
|
63 |
+
from PIL import Image
|
64 |
+
import pytesseract
|
65 |
+
from transformers import pipeline
|
66 |
+
from groq import Groq
|
67 |
+
import requests
|
68 |
+
from io import BytesIO
|
69 |
+
from transformers import pipeline, TrOCRProcessor, VisionEncoderDecoderModel
|
70 |
+
import requests
|
71 |
+
import base64
|
72 |
+
from PIL import UnidentifiedImageError
|
73 |
+
|
74 |
+
# ─── Browser var ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
75 |
+
from typing import List, Dict
|
76 |
+
import json
|
77 |
+
from io import BytesIO
|
78 |
+
#from langchain.tools import tool # or langchain_core.tools
|
79 |
+
from playwright.sync_api import sync_playwright
|
80 |
+
from duckduckgo_search import DDGS
|
81 |
+
import time
|
82 |
+
import random
|
83 |
+
import logging
|
84 |
+
from functools import lru_cache, wraps
|
85 |
+
import requests
|
86 |
+
from playwright.sync_api import sync_playwright
|
87 |
+
from bs4 import BeautifulSoup
|
88 |
+
import tenacity
|
89 |
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
90 |
+
|
91 |
+
# Initialize logger
|
92 |
+
logger = logging.getLogger(__name__)
|
93 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
94 |
+
|
95 |
+
# Additional imports for new functionality
|
96 |
+
import pandas as pd
|
97 |
+
from PyPDF2 import PdfReader
|
98 |
+
import docx
|
99 |
+
import pytesseract
|
100 |
+
import speech_recognition as sr
|
101 |
+
from pydub import AudioSegment
|
102 |
+
from pytube import YouTube
|
103 |
+
from newspaper import Article
|
104 |
+
from langchain.document_loaders import ArxivLoader
|
105 |
+
from langchain_community.document_loaders.youtube import YoutubeLoader, TranscriptFormat
|
106 |
+
|
107 |
+
from playwright.sync_api import sync_playwright
|
108 |
+
# Attempt to import Playwright for dynamic page rendering
|
109 |
+
try:
|
110 |
+
from playwright.sync_api import sync_playwright
|
111 |
+
_playwright_available = True
|
112 |
+
except ImportError:
|
113 |
+
_playwright_available = False
|
114 |
+
|
115 |
+
# Define forbidden keywords for basic NSFW filtering
|
116 |
+
_forbidden = ["porn", "sex", "xxx", "nude", "erotic"]
|
117 |
+
|
118 |
+
# ─── LLM Setup ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
119 |
+
# Load OpenAI API key from environment (required for LLM and embeddings)
|
120 |
+
|
121 |
+
# API Keys from .env file
|
122 |
+
os.environ.setdefault("OPENAI_API_KEY", "<YOUR_OPENAI_KEY>") # Set your own key or env var
|
123 |
+
os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY", "default_key_or_placeholder")
|
124 |
+
os.environ["MISTRAL_API_KEY"] = os.getenv("MISTRAL_API_KEY", "default_key_or_placeholder")
|
125 |
+
|
126 |
+
# Tavily API Key
|
127 |
+
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "default_key_or_placeholder")
|
128 |
+
_forbidden = ["nsfw", "porn", "sex", "explicit"]
|
129 |
+
_playwright_available = True # set False to disable Playwright
|
130 |
+
|
131 |
+
# Globals for RAG system
|
132 |
+
vector_store = None
|
133 |
+
rag_chain = None
|
134 |
+
DB_PATH = None # will be set when a .db is uploaded
|
135 |
+
DOC_PATH = None # will be set when a document is uploaded
|
136 |
+
IMG_PATH = None # will be set when an image is uploaded
|
137 |
+
OTH_PATH = None # will be set when an other file is uploaded
|
138 |
+
|
139 |
+
|
140 |
+
# ─── LLMS ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
141 |
+
#llm = ChatOpenAI(model_name="gpt-3.5-turbo", streaming=True, temperature=0)
|
142 |
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
143 |
+
|
144 |
+
# Import the RetryingChatGroq client
|
145 |
+
from retry_groq import RetryingChatGroq
|
146 |
+
|
147 |
+
# Use the retrying version instead
|
148 |
+
llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False, temperature=0)
|
149 |
+
#llm = ChatMistralAI(model="mistral-large-latest", streaming=True, temperature=0)
|
150 |
+
|
151 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
152 |
+
# ─────────────────────────────────────────────── Tool for multiply ──────────────────────────────────────────────────────────────────────
|
153 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
154 |
+
@tool(parse_docstring=True)
|
155 |
+
def multiply(a: int, b: int) -> int:
|
156 |
+
"""
|
157 |
+
Multiply two numbers.
|
158 |
+
|
159 |
+
Args:
|
160 |
+
a (int): The first factor.
|
161 |
+
b (int): The second factor.
|
162 |
+
|
163 |
+
Returns:
|
164 |
+
int: The product of a and b.
|
165 |
+
"""
|
166 |
+
try:
|
167 |
+
# Direct calculation without relying on LangChain handling
|
168 |
+
result = a * b
|
169 |
+
return result
|
170 |
+
except Exception as e:
|
171 |
+
return f"Error in multiplication: {str(e)}"
|
172 |
+
|
173 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
174 |
+
# ─────────────────────────────────────────────── Tool for add ──────────────────────────────────────────────────────────────────────────
|
175 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
176 |
+
@tool(parse_docstring=True)
|
177 |
+
def add(a: int, b: int) -> int:
|
178 |
+
"""
|
179 |
+
Add two numbers.
|
180 |
+
|
181 |
+
Args:
|
182 |
+
a (int): The first factor.
|
183 |
+
b (int): The second factor.
|
184 |
+
|
185 |
+
Returns:
|
186 |
+
int: The addition of a and b.
|
187 |
+
"""
|
188 |
+
try:
|
189 |
+
# Direct calculation without relying on LangChain handling
|
190 |
+
result = a + b
|
191 |
+
return result
|
192 |
+
except Exception as e:
|
193 |
+
return f"Error in addition: {str(e)}"
|
194 |
+
|
195 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
196 |
+
# ─────────────────────────────────────────────── Tool for subtract ──────────────────────────────────────────────────────────────────────
|
197 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
198 |
+
@tool(parse_docstring=True)
|
199 |
+
def subtract(a: int, b: int) -> int:
|
200 |
+
"""
|
201 |
+
Subtract two numbers.
|
202 |
+
|
203 |
+
Args:
|
204 |
+
a (int): The first factor.
|
205 |
+
b (int): The second factor.
|
206 |
+
|
207 |
+
Returns:
|
208 |
+
int: The subtraction of a and b.
|
209 |
+
"""
|
210 |
+
try:
|
211 |
+
# Direct calculation without relying on LangChain handling
|
212 |
+
result = a - b
|
213 |
+
return result
|
214 |
+
except Exception as e:
|
215 |
+
return f"Error in subtraction: {str(e)}"
|
216 |
+
|
217 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
218 |
+
# ─────────────────────────────────────────────── Tool for divide ──────────────────────────────────────────────────────────────────────
|
219 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
220 |
+
@tool(parse_docstring=True)
|
221 |
+
def divide(a: int, b: int) -> int:
|
222 |
+
"""
|
223 |
+
Divide two numbers.
|
224 |
+
|
225 |
+
Args:
|
226 |
+
a (int): The numerator.
|
227 |
+
b (int): The denominator.
|
228 |
+
|
229 |
+
Returns:
|
230 |
+
float: The result of a divided by b.
|
231 |
+
|
232 |
+
Raises:
|
233 |
+
ValueError: If b is zero.
|
234 |
+
"""
|
235 |
+
try:
|
236 |
+
if b == 0:
|
237 |
+
return "Error: Cannot divide by zero."
|
238 |
+
# Direct calculation without relying on LangChain handling
|
239 |
+
result = a / b
|
240 |
+
return result
|
241 |
+
except Exception as e:
|
242 |
+
return f"Error in division: {str(e)}"
|
243 |
+
|
244 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────���───────────────────────────────────────
|
245 |
+
# ─────────────────────────────────────────────── Tool for modulus ──────────────────────────────────────────────────────────────────────
|
246 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
247 |
+
@tool(parse_docstring=True)
|
248 |
+
def modulus(a: int, b: int) -> int:
|
249 |
+
"""
|
250 |
+
Get the modulus (remainder) of two numbers.
|
251 |
+
|
252 |
+
Args:
|
253 |
+
a (int): The dividend.
|
254 |
+
b (int): The divisor.
|
255 |
+
|
256 |
+
Returns:
|
257 |
+
int: The remainder when a is divided by b.
|
258 |
+
"""
|
259 |
+
try:
|
260 |
+
if b == 0:
|
261 |
+
return "Error: Cannot calculate modulus with zero divisor."
|
262 |
+
# Direct calculation without relying on LangChain handling
|
263 |
+
result = a % b
|
264 |
+
return result
|
265 |
+
except Exception as e:
|
266 |
+
return f"Error in modulus calculation: {str(e)}"
|
267 |
+
|
268 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
269 |
+
# ─────────────────────────────────────────────── Tool for browsing ──────────────────────────────────────────────────────────────────────
|
270 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
271 |
+
def with_retry(max_attempts: int = 3, backoff_base: int = 2):
|
272 |
+
"""
|
273 |
+
Decorator for retrying a function with exponential backoff on exception.
|
274 |
+
"""
|
275 |
+
def decorator(fn):
|
276 |
+
@wraps(fn)
|
277 |
+
def wrapper(*args, **kwargs):
|
278 |
+
for attempt in range(max_attempts):
|
279 |
+
try:
|
280 |
+
return fn(*args, **kwargs)
|
281 |
+
except Exception as e:
|
282 |
+
wait = backoff_base ** attempt + random.uniform(0, 1)
|
283 |
+
logger.warning(f"{fn.__name__} failed (attempt {attempt+1}/{max_attempts}): {e}")
|
284 |
+
if attempt < max_attempts - 1:
|
285 |
+
time.sleep(wait)
|
286 |
+
logger.error(f"{fn.__name__} failed after {max_attempts} attempts.")
|
287 |
+
return []
|
288 |
+
return wrapper
|
289 |
+
return decorator
|
290 |
+
|
291 |
+
@with_retry()
|
292 |
+
@lru_cache(maxsize=128)
|
293 |
+
def tavily_search(query: str, top_k: int = 3) -> List[Dict]:
|
294 |
+
"""Call Tavily API and return a list of result dicts."""
|
295 |
+
if not TAVILY_API_KEY:
|
296 |
+
logger.info("[Tavily] No API key set. Skipping Tavily search.")
|
297 |
+
return []
|
298 |
+
url = "https://api.tavily.com/search"
|
299 |
+
headers = {
|
300 |
+
"Authorization": f"Bearer {TAVILY_API_KEY}",
|
301 |
+
"Content-Type": "application/json",
|
302 |
+
}
|
303 |
+
payload = {"query": query, "num_results": top_k}
|
304 |
+
resp = requests.post(url, headers=headers, json=payload, timeout=10)
|
305 |
+
resp.raise_for_status()
|
306 |
+
data = resp.json()
|
307 |
+
results = []
|
308 |
+
for item in data.get("results", []):
|
309 |
+
results.append({
|
310 |
+
"title": item.get("title", ""),
|
311 |
+
"url": item.get("url", ""),
|
312 |
+
"content": item.get("content", "")[:200],
|
313 |
+
"source": "Tavily"
|
314 |
+
})
|
315 |
+
return results
|
316 |
+
|
317 |
+
@with_retry()
|
318 |
+
@lru_cache(maxsize=128)
|
319 |
+
def duckduckgo_search(query: str, top_k: int = 3) -> List[Dict]:
|
320 |
+
"""Query DuckDuckGo and return up to top_k raw SERP hits."""
|
321 |
+
results = []
|
322 |
+
try:
|
323 |
+
with DDGS(timeout=15) as ddgs: # Increase timeout from default
|
324 |
+
for hit in ddgs.text(query, safesearch="On", max_results=top_k, timeout=15):
|
325 |
+
results.append({
|
326 |
+
"title": hit.get("title", ""),
|
327 |
+
"url": hit.get("href") or hit.get("url", ""),
|
328 |
+
"content": hit.get("body", ""),
|
329 |
+
"source": "DuckDuckGo"
|
330 |
+
})
|
331 |
+
if len(results) >= top_k:
|
332 |
+
break
|
333 |
+
except Exception as e:
|
334 |
+
logger.warning(f"DuckDuckGo search failed: {e}")
|
335 |
+
# Don't re-raise - just return empty results to allow fallbacks to work
|
336 |
+
|
337 |
+
return results
|
338 |
+
|
339 |
+
# Additional fallback search alternative
|
340 |
+
def simple_google_search(query: str, top_k: int = 3) -> List[Dict]:
|
341 |
+
"""Simplified Google search as a fallback when other methods fail."""
|
342 |
+
try:
|
343 |
+
# Encode the query
|
344 |
+
import urllib.parse
|
345 |
+
import bs4
|
346 |
+
|
347 |
+
encoded_query = urllib.parse.quote(query)
|
348 |
+
url = f"https://www.google.com/search?q={encoded_query}"
|
349 |
+
|
350 |
+
headers = {
|
351 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
|
352 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
353 |
+
"Accept-Language": "en-US,en;q=0.5",
|
354 |
+
"Referer": "https://www.google.com/",
|
355 |
+
"Connection": "keep-alive",
|
356 |
+
}
|
357 |
+
|
358 |
+
response = requests.get(url, headers=headers, timeout=20)
|
359 |
+
response.raise_for_status()
|
360 |
+
|
361 |
+
soup = bs4.BeautifulSoup(response.text, "html.parser")
|
362 |
+
results = []
|
363 |
+
|
364 |
+
# Extract search results
|
365 |
+
for result in soup.select("div.g")[:top_k]:
|
366 |
+
title_elem = result.select_one("h3")
|
367 |
+
link_elem = result.select_one("a")
|
368 |
+
snippet_elem = result.select_one("div.VwiC3b")
|
369 |
+
|
370 |
+
if title_elem and link_elem and snippet_elem and "href" in link_elem.attrs:
|
371 |
+
href = link_elem["href"]
|
372 |
+
if href.startswith("/url?q="):
|
373 |
+
href = href.split("/url?q=")[1].split("&")[0]
|
374 |
+
|
375 |
+
if href.startswith("http"):
|
376 |
+
results.append({
|
377 |
+
"title": title_elem.get_text(),
|
378 |
+
"url": href,
|
379 |
+
"content": snippet_elem.get_text(),
|
380 |
+
"source": "Google"
|
381 |
+
})
|
382 |
+
|
383 |
+
return results
|
384 |
+
|
385 |
+
except Exception as e:
|
386 |
+
logger.warning(f"Simple Google search failed: {e}")
|
387 |
+
return []
|
388 |
+
|
389 |
+
def hybrid_search(query: str, top_k: int = 3) -> List[Dict]:
|
390 |
+
"""Combine multiple search sources with fallbacks."""
|
391 |
+
# Try primary search methods first
|
392 |
+
results = []
|
393 |
+
|
394 |
+
# Start with Tavily if API key is available
|
395 |
+
if TAVILY_API_KEY and TAVILY_API_KEY != "default_key_or_placeholder":
|
396 |
+
try:
|
397 |
+
tavily_results = tavily_search(query, top_k)
|
398 |
+
results.extend(tavily_results)
|
399 |
+
logger.info(f"Retrieved {len(tavily_results)} results from Tavily")
|
400 |
+
except Exception as e:
|
401 |
+
logger.warning(f"Tavily search failed: {e}")
|
402 |
+
|
403 |
+
# If we don't have enough results, try DuckDuckGo
|
404 |
+
if len(results) < top_k:
|
405 |
+
try:
|
406 |
+
ddg_results = duckduckgo_search(query, top_k - len(results))
|
407 |
+
results.extend(ddg_results)
|
408 |
+
logger.info(f"Retrieved {len(ddg_results)} results from DuckDuckGo")
|
409 |
+
except Exception as e:
|
410 |
+
logger.warning(f"DuckDuckGo search failed: {e}")
|
411 |
+
|
412 |
+
# If we still don't have enough results, try Google
|
413 |
+
if len(results) < top_k:
|
414 |
+
try:
|
415 |
+
google_results = simple_google_search(query, top_k - len(results))
|
416 |
+
results.extend(google_results)
|
417 |
+
logger.info(f"Retrieved {len(google_results)} results from Google")
|
418 |
+
except Exception as e:
|
419 |
+
logger.warning(f"Google search failed: {e}")
|
420 |
+
|
421 |
+
# If all search methods failed, return a dummy result
|
422 |
+
if not results:
|
423 |
+
results.append({
|
424 |
+
"title": "Search Failed",
|
425 |
+
"url": "",
|
426 |
+
"content": f"Sorry, I couldn't find results for '{query}'. Please try refining your search terms or check your internet connection.",
|
427 |
+
"source": "No results"
|
428 |
+
})
|
429 |
+
|
430 |
+
return results[:top_k] # Ensure we only return top_k results
|
431 |
+
|
432 |
+
def format_search_docs(search_docs: List[Dict]) -> Dict[str, str]:
|
433 |
+
"""
|
434 |
+
Turn a list of {source, page, content} dicts into one big
|
435 |
+
string with <Document ...>…</Document> entries separated by `---`.
|
436 |
+
"""
|
437 |
+
formatted_search_docs = "\n\n---\n\n".join(
|
438 |
+
[
|
439 |
+
f'<Document source="{doc["source"]}" page="{doc.get("page", "")}"/>\n'
|
440 |
+
f'{doc.get("content", "")}\n'
|
441 |
+
f'</Document>'
|
442 |
+
for doc in search_docs
|
443 |
+
]
|
444 |
+
)
|
445 |
+
return {"web_results": formatted_search_docs}
|
446 |
+
|
447 |
+
|
448 |
+
@tool(parse_docstring=True)
|
449 |
+
def web_search(query: str, top_k: int = 3) -> Dict[str, str]:
|
450 |
+
"""
|
451 |
+
Perform a hybrid web search combining multiple search engines with robust fallbacks.
|
452 |
+
|
453 |
+
Args:
|
454 |
+
query: The search query string to look up.
|
455 |
+
top_k: The maximum number of search results to return (default is 3).
|
456 |
+
|
457 |
+
Returns:
|
458 |
+
A dictionary mapping result indices to XML-like <Document> blocks, each containing:
|
459 |
+
- source: The URL of the webpage.
|
460 |
+
- page: Placeholder for page identifier (empty string by default).
|
461 |
+
- content: The first 200 words of the page text, cleaned of HTML tags.
|
462 |
+
"""
|
463 |
+
try:
|
464 |
+
# Use our robust hybrid search to get initial results
|
465 |
+
search_results = hybrid_search(query, top_k)
|
466 |
+
results = []
|
467 |
+
|
468 |
+
# Process each search result to get better content
|
469 |
+
for hit in search_results:
|
470 |
+
url = hit.get("url")
|
471 |
+
if not url:
|
472 |
+
continue
|
473 |
+
|
474 |
+
# Start with the snippet from search
|
475 |
+
content = hit.get("content", "")
|
476 |
+
title = hit.get("title", "")
|
477 |
+
|
478 |
+
# Try to scrape additional content if possible
|
479 |
+
try:
|
480 |
+
# Use a random user agent to avoid blocking
|
481 |
+
headers = {
|
482 |
+
"User-Agent": random.choice([
|
483 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
|
484 |
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15",
|
485 |
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
|
486 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.62"
|
487 |
+
]),
|
488 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
489 |
+
"Accept-Language": "en-US,en;q=0.5",
|
490 |
+
"Referer": "https://www.google.com/",
|
491 |
+
"DNT": "1",
|
492 |
+
"Connection": "keep-alive"
|
493 |
+
}
|
494 |
+
|
495 |
+
# Higher timeout for better reliability
|
496 |
+
resp = requests.get(url, timeout=15, headers=headers)
|
497 |
+
|
498 |
+
# Only process if successful
|
499 |
+
if resp.status_code == 200:
|
500 |
+
soup = BeautifulSoup(resp.text, "html.parser")
|
501 |
+
|
502 |
+
# Try to find main content
|
503 |
+
main_content = soup.find('main') or soup.find('article') or soup.find('div', class_='content')
|
504 |
+
|
505 |
+
# If we found main content, use it
|
506 |
+
if main_content:
|
507 |
+
extracted_text = main_content.get_text(separator=" ", strip=True)
|
508 |
+
# Take first 200 words
|
509 |
+
content = " ".join(extracted_text.split()[:200])
|
510 |
+
else:
|
511 |
+
# Otherwise use all text
|
512 |
+
all_text = soup.get_text(separator=" ", strip=True)
|
513 |
+
content = " ".join(all_text.split()[:200])
|
514 |
+
|
515 |
+
# Use content from page only if it's substantial
|
516 |
+
if len(content) < 50:
|
517 |
+
content = hit.get("content", "")[:200]
|
518 |
+
|
519 |
+
# Random delay between 0.5-1.5 seconds to avoid rate limits
|
520 |
+
time.sleep(0.5 + random.random())
|
521 |
+
|
522 |
+
except requests.exceptions.HTTPError as e:
|
523 |
+
logger.warning(f"HTTP error when scraping {url}: {e}")
|
524 |
+
# Keep the search snippet as a fallback
|
525 |
+
except requests.exceptions.RequestException as e:
|
526 |
+
logger.warning(f"Request error when scraping {url}: {e}")
|
527 |
+
# Keep the search snippet as a fallback
|
528 |
+
except Exception as e:
|
529 |
+
logger.warning(f"Unexpected error when scraping {url}: {e}")
|
530 |
+
# Keep the search snippet as a fallback
|
531 |
+
|
532 |
+
# Filter out inappropriate content
|
533 |
+
if any(f in content.lower() for f in _forbidden):
|
534 |
+
continue
|
535 |
+
|
536 |
+
# Add to results
|
537 |
+
results.append({
|
538 |
+
"source": url,
|
539 |
+
"page": "",
|
540 |
+
"content": content
|
541 |
+
})
|
542 |
+
|
543 |
+
# Return formatted search docs
|
544 |
+
return format_search_docs(results[:top_k])
|
545 |
+
except Exception as e:
|
546 |
+
logger.error(f"Web search failed: {e}")
|
547 |
+
# Return a helpful error message
|
548 |
+
return format_search_docs([{
|
549 |
+
"source": "Error",
|
550 |
+
"page": "",
|
551 |
+
"content": f"Search failed with error: {e}. Please try again with different search terms."
|
552 |
+
}])
|
553 |
+
|
554 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
555 |
+
# ─────────────────────────────────────────────── Tool for File System ───────────────────────────────────────────────────────────────────
|
556 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
557 |
+
@tool(parse_docstring=True)
|
558 |
+
def download_file(url: str, dest_path: str) -> str:
|
559 |
+
"""
|
560 |
+
Download a file from a given URL and save it locally.
|
561 |
+
|
562 |
+
Args:
|
563 |
+
url: The direct URL of the file to download.
|
564 |
+
dest_path: The local path to save the downloaded file.
|
565 |
+
|
566 |
+
Returns:
|
567 |
+
The destination path where the file was saved.
|
568 |
+
"""
|
569 |
+
r = requests.get(url, stream=True)
|
570 |
+
r.raise_for_status()
|
571 |
+
with open(dest_path, 'wb') as f:
|
572 |
+
for chunk in r.iter_content(8192):
|
573 |
+
f.write(chunk)
|
574 |
+
return dest_path
|
575 |
+
|
576 |
+
@tool(parse_docstring=True)
|
577 |
+
def process_excel_to_text(file_path: str) -> str:
|
578 |
+
"""
|
579 |
+
Convert an Excel file into CSV-formatted text.
|
580 |
+
|
581 |
+
Args:
|
582 |
+
file_path: Path to the Excel (.xlsx) file.
|
583 |
+
|
584 |
+
Returns:
|
585 |
+
A string of CSV-formatted content extracted from the Excel file.
|
586 |
+
"""
|
587 |
+
try:
|
588 |
+
# Check if file exists
|
589 |
+
import os
|
590 |
+
if not os.path.exists(file_path):
|
591 |
+
return f"Error: Excel file '{file_path}' does not exist."
|
592 |
+
|
593 |
+
# Try different engines
|
594 |
+
engines = ['openpyxl', 'xlrd', None]
|
595 |
+
|
596 |
+
for engine in engines:
|
597 |
+
try:
|
598 |
+
# For engine=None, pandas will try to auto-detect
|
599 |
+
if engine:
|
600 |
+
df = pd.read_excel(file_path, engine=engine)
|
601 |
+
else:
|
602 |
+
df = pd.read_excel(file_path)
|
603 |
+
return df.to_csv(index=False)
|
604 |
+
except Exception as e:
|
605 |
+
print(f"Excel engine {engine} failed: {e}")
|
606 |
+
last_error = e
|
607 |
+
continue
|
608 |
+
|
609 |
+
# If we got here, all engines failed
|
610 |
+
return f"Error processing Excel file: {str(last_error)}"
|
611 |
+
except Exception as e:
|
612 |
+
return f"Error with Excel file: {str(e)}"
|
613 |
+
|
614 |
+
@tool(parse_docstring=True)
|
615 |
+
def read_text_from_pdf(file_path: str, question: str = None) -> str:
|
616 |
+
"""
|
617 |
+
Extract text from a PDF file, chunking large documents if needed.
|
618 |
+
|
619 |
+
Args:
|
620 |
+
file_path: Path to the PDF file.
|
621 |
+
question: Optional question to help retrieve relevant parts of long documents.
|
622 |
+
|
623 |
+
Returns:
|
624 |
+
The extracted text content, potentially chunked if the document is large.
|
625 |
+
"""
|
626 |
+
try:
|
627 |
+
# Check if file exists
|
628 |
+
import os
|
629 |
+
if not os.path.exists(file_path):
|
630 |
+
return f"Error: PDF file '{file_path}' does not exist."
|
631 |
+
|
632 |
+
reader = PdfReader(file_path)
|
633 |
+
full_text = "\n".join([page.extract_text() or "" for page in reader.pages])
|
634 |
+
|
635 |
+
# If a question is provided, use retrieval to get relevant parts
|
636 |
+
if question and len(full_text) > 5000: # Only chunk if text is large
|
637 |
+
return process_large_document(full_text, question)
|
638 |
+
|
639 |
+
return full_text
|
640 |
+
except Exception as e:
|
641 |
+
return f"Error reading PDF: {str(e)}"
|
642 |
+
|
643 |
+
@tool(parse_docstring=True)
|
644 |
+
def read_text_from_docx(file_path: str, question: str = None) -> str:
|
645 |
+
"""
|
646 |
+
Extract text from a DOCX (Word) document, chunking large documents if needed.
|
647 |
+
|
648 |
+
Args:
|
649 |
+
file_path: Path to the DOCX file.
|
650 |
+
question: Optional question to help retrieve relevant parts of long documents.
|
651 |
+
|
652 |
+
Returns:
|
653 |
+
The extracted text, potentially chunked if the document is large.
|
654 |
+
"""
|
655 |
+
try:
|
656 |
+
# Check if file exists
|
657 |
+
import os
|
658 |
+
if not os.path.exists(file_path):
|
659 |
+
return f"Error: File '{file_path}' does not exist."
|
660 |
+
|
661 |
+
try:
|
662 |
+
doc = docx.Document(file_path)
|
663 |
+
full_text = "\n".join([para.text for para in doc.paragraphs])
|
664 |
+
except Exception as docx_err:
|
665 |
+
# Handle "Package not found" error specifically
|
666 |
+
if "Package not found" in str(docx_err):
|
667 |
+
# Try to read raw text if possible
|
668 |
+
try:
|
669 |
+
import zipfile
|
670 |
+
from xml.etree.ElementTree import XML
|
671 |
+
|
672 |
+
WORD_NAMESPACE = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
|
673 |
+
PARA = WORD_NAMESPACE + 'p'
|
674 |
+
TEXT = WORD_NAMESPACE + 't'
|
675 |
+
|
676 |
+
with zipfile.ZipFile(file_path) as docx_file:
|
677 |
+
with docx_file.open('word/document.xml') as document:
|
678 |
+
tree = XML(document.read())
|
679 |
+
paragraphs = []
|
680 |
+
for paragraph in tree.iter(PARA):
|
681 |
+
texts = [node.text for node in paragraph.iter(TEXT) if node.text]
|
682 |
+
if texts:
|
683 |
+
paragraphs.append(''.join(texts))
|
684 |
+
full_text = '\n'.join(paragraphs)
|
685 |
+
except Exception as e:
|
686 |
+
return f"Error reading DOCX file: {str(e)}"
|
687 |
+
else:
|
688 |
+
return f"Error reading DOCX file: {str(docx_err)}"
|
689 |
+
|
690 |
+
# If a question is provided, use retrieval to get relevant parts
|
691 |
+
if question and len(full_text) > 5000: # Only chunk if text is large
|
692 |
+
return process_large_document(full_text, question)
|
693 |
+
|
694 |
+
return full_text
|
695 |
+
except Exception as e:
|
696 |
+
return f"Error reading DOCX file: {str(e)}"
|
697 |
+
|
698 |
+
|
699 |
+
@tool(parse_docstring=True)
|
700 |
+
def transcribe_audio(file_path: str) -> str:
|
701 |
+
"""
|
702 |
+
Transcribe speech from a local audio file to text.
|
703 |
+
|
704 |
+
Args:
|
705 |
+
file_path: Path to the audio file.
|
706 |
+
|
707 |
+
Returns:
|
708 |
+
Transcribed text using Google Web Speech API.
|
709 |
+
"""
|
710 |
+
try:
|
711 |
+
# Check if file exists
|
712 |
+
import os
|
713 |
+
if not os.path.exists(file_path):
|
714 |
+
return f"Error: Audio file '{file_path}' does not exist."
|
715 |
+
|
716 |
+
# For non-WAV files, convert to WAV first
|
717 |
+
if not file_path.lower().endswith('.wav'):
|
718 |
+
try:
|
719 |
+
from pydub import AudioSegment
|
720 |
+
temp_wav = os.path.splitext(file_path)[0] + "_temp.wav"
|
721 |
+
audio = AudioSegment.from_file(file_path)
|
722 |
+
audio.export(temp_wav, format="wav")
|
723 |
+
file_path = temp_wav
|
724 |
+
except Exception as e:
|
725 |
+
return f"Failed to convert audio to WAV format: {str(e)}"
|
726 |
+
|
727 |
+
recognizer = sr.Recognizer()
|
728 |
+
with sr.AudioFile(file_path) as src:
|
729 |
+
audio = recognizer.record(src)
|
730 |
+
return recognizer.recognize_google(audio)
|
731 |
+
except Exception as e:
|
732 |
+
if "Audio file could not be read" in str(e):
|
733 |
+
return f"Error: Audio format not supported. Try converting to WAV, MP3, OGG, or FLAC."
|
734 |
+
return f"Error transcribing audio: {str(e)}"
|
735 |
+
|
736 |
+
@tool(parse_docstring=True)
|
737 |
+
def youtube_audio_processing(youtube_url: str) -> str:
|
738 |
+
"""
|
739 |
+
Download and transcribe audio from a YouTube video.
|
740 |
+
|
741 |
+
Args:
|
742 |
+
youtube_url: URL of the YouTube video.
|
743 |
+
|
744 |
+
Returns:
|
745 |
+
Transcription text extracted from the video's audio.
|
746 |
+
"""
|
747 |
+
yt = YouTube(youtube_url)
|
748 |
+
audio_stream = yt.streams.filter(only_audio=True).first()
|
749 |
+
out_file = audio_stream.download(output_path='.', filename='yt_audio')
|
750 |
+
wav_path = 'yt_audio.wav'
|
751 |
+
AudioSegment.from_file(out_file).export(wav_path, format='wav')
|
752 |
+
return transcribe_audio(wav_path)
|
753 |
+
|
754 |
+
@tool(parse_docstring=True)
|
755 |
+
def extract_article_text(url: str, question: str = None) -> str:
|
756 |
+
"""
|
757 |
+
Download and extract the main article content from a webpage, chunking large articles if needed.
|
758 |
+
|
759 |
+
Args:
|
760 |
+
url: The URL of the article to extract.
|
761 |
+
question: Optional question to help retrieve relevant parts of long articles.
|
762 |
+
|
763 |
+
Returns:
|
764 |
+
The article's textual content, potentially chunked if large.
|
765 |
+
"""
|
766 |
+
try:
|
767 |
+
art = Article(url)
|
768 |
+
art.download()
|
769 |
+
art.parse()
|
770 |
+
full_text = art.text
|
771 |
+
|
772 |
+
# If a question is provided, use retrieval to get relevant parts
|
773 |
+
if question and len(full_text) > 5000: # Only chunk if text is large
|
774 |
+
return process_large_document(full_text, question)
|
775 |
+
|
776 |
+
return full_text
|
777 |
+
except Exception as e:
|
778 |
+
return f"Error extracting article: {str(e)}"
|
779 |
+
|
780 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
781 |
+
# ───────────────────────────────────────────────────────────── Tool for ArXiv ────────────────────────────────────────────────────────────
|
782 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
783 |
+
|
784 |
+
@tool(parse_docstring=True)
|
785 |
+
def arvix_search(query: str) -> Dict[str, str]:
|
786 |
+
"""
|
787 |
+
Search for academic papers on ArXiv.
|
788 |
+
|
789 |
+
Args:
|
790 |
+
query: The search term to look for in ArXiv.
|
791 |
+
|
792 |
+
Returns:
|
793 |
+
A dictionary of up to 3 relevant paper entries in JSON format.
|
794 |
+
"""
|
795 |
+
papers = ArxivLoader(query=query, load_max_docs=3).load()
|
796 |
+
results = []
|
797 |
+
for doc in papers:
|
798 |
+
try:
|
799 |
+
# Handle different metadata formats that might be returned
|
800 |
+
source = doc.metadata.get("source", "ArXiv")
|
801 |
+
doc_id = doc.metadata.get("id", doc.metadata.get("entry_id", ""))
|
802 |
+
result = {
|
803 |
+
"source": source,
|
804 |
+
"id": doc_id,
|
805 |
+
"summary": doc.page_content[:1000] if hasattr(doc, "page_content") else str(doc)[:1000],
|
806 |
+
}
|
807 |
+
results.append(result)
|
808 |
+
except Exception as e:
|
809 |
+
# Add error information as a fallback
|
810 |
+
results.append({
|
811 |
+
"source": "ArXiv Error",
|
812 |
+
"id": "error",
|
813 |
+
"summary": f"Error processing paper: {str(e)}"
|
814 |
+
})
|
815 |
+
|
816 |
+
return {"arvix_results": json.dumps(results)}
|
817 |
+
|
818 |
+
@tool(parse_docstring=True)
|
819 |
+
def answer_youtube_video_question(
|
820 |
+
youtube_url: str,
|
821 |
+
question: str,
|
822 |
+
chunk_size_seconds: int = 30
|
823 |
+
) -> str:
|
824 |
+
"""
|
825 |
+
Answer a question based on a YouTube video's transcript.
|
826 |
+
|
827 |
+
Args:
|
828 |
+
youtube_url: URL of the YouTube video.
|
829 |
+
question: The question to be answered using video content.
|
830 |
+
chunk_size_seconds: Duration of each transcript chunk.
|
831 |
+
|
832 |
+
Returns:
|
833 |
+
The answer to the question generated from the video transcript.
|
834 |
+
"""
|
835 |
+
loader = YoutubeLoader.from_youtube_url(
|
836 |
+
youtube_url,
|
837 |
+
add_video_info=True,
|
838 |
+
transcript_format=TranscriptFormat.CHUNKS,
|
839 |
+
chunk_size_seconds=chunk_size_seconds,
|
840 |
+
)
|
841 |
+
documents = loader.load()
|
842 |
+
embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-mpnet-base-v2')
|
843 |
+
vectorstore = FAISS.from_documents(documents, embeddings)
|
844 |
+
llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False)
|
845 |
+
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=vectorstore.as_retriever())
|
846 |
+
return qa_chain.run(question)
|
847 |
+
|
848 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
849 |
+
# ───────────────────────────────────────────────────────────── Tool for Python REPL tool ────────────────────────────────────────────────
|
850 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
851 |
+
|
852 |
+
python_repl = PythonREPLTool()
|
853 |
+
|
854 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
855 |
+
# ───────────────────────────────────────────────────────────── Tool for Wiki ────────────────────────────────────────────────────────────
|
856 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
857 |
+
|
858 |
+
@tool(parse_docstring=True)
|
859 |
+
def wiki_search(query: str) -> str:
|
860 |
+
"""
|
861 |
+
Search Wikipedia for information on a given topic.
|
862 |
+
|
863 |
+
Args:
|
864 |
+
query: The search term for Wikipedia.
|
865 |
+
|
866 |
+
Returns:
|
867 |
+
A JSON string with up to 3 summary results.
|
868 |
+
"""
|
869 |
+
# load up to top_k pages
|
870 |
+
pages = WikipediaLoader(query=query, load_max_docs=3).load()
|
871 |
+
results: List[Dict] = []
|
872 |
+
for doc in pages:
|
873 |
+
results.append({
|
874 |
+
"source": doc.metadata["source"],
|
875 |
+
"page": doc.metadata.get("page", ""),
|
876 |
+
"content": doc.page_content[:1000], # truncate if you like
|
877 |
+
})
|
878 |
+
return {"wiki_results": format_search_docs(results)}
|
879 |
+
|
880 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
881 |
+
# ───────────────────────────────────── Tool for Image (understading, captioning & classification) ─────────────────────────────────────────
|
882 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
883 |
+
|
884 |
+
def _load_image(img_path: str, resize_to=(512, 512)) -> Image.Image:
|
885 |
+
"""
|
886 |
+
Load, verify, convert, and resize an image.
|
887 |
+
Raises ValueError on failure.
|
888 |
+
"""
|
889 |
+
if not img_path:
|
890 |
+
raise ValueError("No image path provided.")
|
891 |
+
try:
|
892 |
+
with Image.open(img_path) as img:
|
893 |
+
img.verify()
|
894 |
+
img = Image.open(img_path).convert("RGB")
|
895 |
+
img = img.resize(resize_to)
|
896 |
+
return img
|
897 |
+
except UnidentifiedImageError:
|
898 |
+
raise ValueError(f"File at {img_path} is not a valid image.")
|
899 |
+
except Exception as e:
|
900 |
+
raise ValueError(f"Failed to load image at {img_path}: {e}")
|
901 |
+
|
902 |
+
def _encode_image_to_base64(img_path: str) -> str:
|
903 |
+
"""
|
904 |
+
Load an image, save optimized PNG into memory, and base64‑encode it.
|
905 |
+
"""
|
906 |
+
img = _load_image(img_path)
|
907 |
+
buffer = BytesIO()
|
908 |
+
img.save(buffer, format="PNG", optimize=True)
|
909 |
+
return base64.b64encode(buffer.getvalue()).decode("utf-8")
|
910 |
+
|
911 |
+
@tool
|
912 |
+
def image_processing(prompt: str, img_path: str) -> str:
|
913 |
+
"""Process an image using a vision LLM, with OCR fallback.
|
914 |
+
|
915 |
+
Args:
|
916 |
+
prompt: Instruction or question related to the image.
|
917 |
+
img_path: Path to the image file.
|
918 |
+
|
919 |
+
Returns:
|
920 |
+
The model's response or fallback OCR result.
|
921 |
+
"""
|
922 |
+
try:
|
923 |
+
import os
|
924 |
+
# Check if file exists
|
925 |
+
if not os.path.exists(img_path):
|
926 |
+
return f"Error: Image file '{img_path}' does not exist."
|
927 |
+
|
928 |
+
try:
|
929 |
+
b64 = _encode_image_to_base64(img_path)
|
930 |
+
# Build a single markdown string with inline base64 image
|
931 |
+
md = f"{prompt}\n\n"
|
932 |
+
message = HumanMessage(content=md)
|
933 |
+
# Use RetryingChatGroq with Llama 4 Maverick for vision
|
934 |
+
llm = RetryingChatGroq(model="meta-llama/llama-4-maverick-17b-128e-instruct", streaming=False, temperature=0)
|
935 |
+
try:
|
936 |
+
resp = llm.invoke([message])
|
937 |
+
if hasattr(resp, 'content'):
|
938 |
+
return resp.content.strip()
|
939 |
+
elif isinstance(resp, str):
|
940 |
+
return resp.strip()
|
941 |
+
else:
|
942 |
+
# Handle dictionary or other response types
|
943 |
+
return str(resp)
|
944 |
+
except Exception as invoke_err:
|
945 |
+
print(f"[LLM invoke error] {invoke_err}")
|
946 |
+
# Fall back to OCR
|
947 |
+
raise ValueError("LLM invocation failed")
|
948 |
+
except Exception as llama_err:
|
949 |
+
print(f"[LLM vision failed] {llama_err}")
|
950 |
+
try:
|
951 |
+
img = _load_image(img_path)
|
952 |
+
return pytesseract.image_to_string(img).strip()
|
953 |
+
except Exception as ocr_err:
|
954 |
+
print(f"[OCR fallback failed] {ocr_err}")
|
955 |
+
return "Unable to process the image. Please check the file and try again."
|
956 |
+
except Exception as e:
|
957 |
+
# Catch any other errors
|
958 |
+
print(f"[image_processing error] {e}")
|
959 |
+
return f"Error processing image: {str(e)}"
|
960 |
+
|
961 |
+
python_repl_tool = PythonREPLTool()
|
962 |
+
|
963 |
+
@tool
|
964 |
+
def echo(text: str) -> str:
|
965 |
+
"""Echo back the input text.
|
966 |
+
|
967 |
+
Args:
|
968 |
+
text: The string to be echoed.
|
969 |
+
|
970 |
+
Returns:
|
971 |
+
The same text that was provided as input.
|
972 |
+
"""
|
973 |
+
return text
|
974 |
+
|
975 |
+
# ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────��───────────────────
|
976 |
+
# ─────────────────────────────────────────────── Langgraph Agent ───────────────────────────────────────────────────────────────────────
|
977 |
+
# ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
978 |
+
|
979 |
+
|
980 |
+
# Build graph function
|
981 |
+
from langchain_core.tools import tool
|
982 |
+
from langchain.chat_models import ChatOpenAI
|
983 |
+
from langgraph.prebuilt.chat_agent_executor import create_react_agent, AgentState
|
984 |
+
from langchain.chat_models import init_chat_model
|
985 |
+
|
986 |
+
|
987 |
+
|
988 |
+
def build_graph(provider: str = "groq"):
|
989 |
+
"""Construct and compile the multi‑agent GAIA workflow StateGraph.
|
990 |
+
|
991 |
+
This graph wires together three React‑style agents into a streamlined pipeline:
|
992 |
+
PerceptionAgent → ActionAgent → EvaluationAgent (with appropriate entry/exit points)
|
993 |
+
|
994 |
+
The agents have the following responsibilities:
|
995 |
+
- PerceptionAgent: Handles web searches, Wikipedia, ArXiv, and image processing
|
996 |
+
- ActionAgent: Performs calculations, file operations, and code analysis
|
997 |
+
- EvaluationAgent: Reviews results and ensures the final answer is properly formatted
|
998 |
+
|
999 |
+
Args:
|
1000 |
+
provider: The name of the LLM provider. Must be "groq".
|
1001 |
+
|
1002 |
+
Returns:
|
1003 |
+
CompiledGraph: A compiled LangGraph state machine ready for invocation.
|
1004 |
+
|
1005 |
+
Raises:
|
1006 |
+
ValueError: If `provider` is anything other than "groq".
|
1007 |
+
"""
|
1008 |
+
try:
|
1009 |
+
if provider != "groq":
|
1010 |
+
raise ValueError("Invalid provider. Expected 'groq'.")
|
1011 |
+
|
1012 |
+
# Initialize LLM
|
1013 |
+
try:
|
1014 |
+
logger.info("Initializing LLM with model: deepseek-r1-distill-llama-70b")
|
1015 |
+
api_key = os.getenv("GROQ_API_KEY")
|
1016 |
+
if not api_key or api_key == "default_key_or_placeholder":
|
1017 |
+
logger.error("GROQ_API_KEY is not set or is using placeholder value")
|
1018 |
+
raise ValueError("GROQ_API_KEY environment variable is not set properly. Please set a valid API key.")
|
1019 |
+
|
1020 |
+
llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", temperature=0)
|
1021 |
+
logger.info("LLM initialized successfully")
|
1022 |
+
except Exception as e:
|
1023 |
+
logger.error(f"Error initializing LLM: {str(e)}")
|
1024 |
+
raise
|
1025 |
+
|
1026 |
+
# General system message for agents
|
1027 |
+
sys_msg = SystemMessage(content="""
|
1028 |
+
You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template:
|
1029 |
+
|
1030 |
+
FINAL ANSWER: [YOUR FINAL ANSWER]
|
1031 |
+
|
1032 |
+
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma-separated list of numbers and/or strings.
|
1033 |
+
|
1034 |
+
If you are asked for a number, don't use commas or units (e.g., $, %, kg) unless specified otherwise.
|
1035 |
+
|
1036 |
+
If you are asked for a string, don't use articles (a, an, the), and don't use abbreviations (e.g., for states).
|
1037 |
+
|
1038 |
+
If you are asked for a comma-separated list, apply the above rules to each element in the list.
|
1039 |
+
""".strip())
|
1040 |
+
|
1041 |
+
# Special system message for the evaluation agent with stricter formatting requirements
|
1042 |
+
eval_sys_msg = SystemMessage(content="""
|
1043 |
+
You are a specialized evaluation agent. Your job is to review the work done by other agents
|
1044 |
+
and provide a final, properly formatted answer.
|
1045 |
+
|
1046 |
+
IMPORTANT: You MUST ALWAYS format your answer using this exact template:
|
1047 |
+
|
1048 |
+
FINAL ANSWER: [concise answer]
|
1049 |
+
|
1050 |
+
Rules for formatting the answer:
|
1051 |
+
1. The answer must be extremely concise - use as few words as possible
|
1052 |
+
2. For numeric answers, provide only the number without units unless units are specifically requested
|
1053 |
+
3. For text answers, avoid articles (a, an, the) and unnecessary words
|
1054 |
+
4. For list answers, use a comma-separated format
|
1055 |
+
5. NEVER explain your reasoning in the FINAL ANSWER section
|
1056 |
+
6. NEVER skip the "FINAL ANSWER:" prefix
|
1057 |
+
|
1058 |
+
Example good answers:
|
1059 |
+
FINAL ANSWER: 42
|
1060 |
+
FINAL ANSWER: Paris
|
1061 |
+
FINAL ANSWER: 1912, 1945, 1989
|
1062 |
+
|
1063 |
+
Example bad answers (don't do these):
|
1064 |
+
- Based on my analysis, the answer is 42.
|
1065 |
+
- I think it's Paris because that's the capital of France.
|
1066 |
+
- The years were 1912, 1945, and 1989.
|
1067 |
+
|
1068 |
+
Remember: ALWAYS include "FINAL ANSWER:" followed by the most concise answer possible.
|
1069 |
+
""".strip())
|
1070 |
+
|
1071 |
+
# Define tools for each agent
|
1072 |
+
logger.info("Setting up agent tools")
|
1073 |
+
perception_tools = [web_search, wiki_search, news_article_search, arvix_search, image_processing, echo]
|
1074 |
+
execution_tools = [
|
1075 |
+
multiply, add, subtract, divide, modulus,
|
1076 |
+
download_file, process_excel_to_text,
|
1077 |
+
read_text_from_pdf, read_text_from_docx,
|
1078 |
+
transcribe_audio, youtube_audio_processing,
|
1079 |
+
extract_article_text, answer_youtube_video_question,
|
1080 |
+
python_repl_tool, analyze_code, read_code_file, analyze_python_function
|
1081 |
+
]
|
1082 |
+
|
1083 |
+
# ─────────────── Agent Creation ───────────────
|
1084 |
+
logger.info("Creating agents")
|
1085 |
+
try:
|
1086 |
+
# Create agents with proper error handling
|
1087 |
+
PerceptionAgent = create_react_agent(
|
1088 |
+
model=llm,
|
1089 |
+
tools=perception_tools,
|
1090 |
+
prompt=sys_msg,
|
1091 |
+
state_schema=AgentState,
|
1092 |
+
name="PerceptionAgent"
|
1093 |
+
)
|
1094 |
+
logger.info("Created PerceptionAgent successfully")
|
1095 |
+
|
1096 |
+
# Combined Planning and Execution agent for better efficiency
|
1097 |
+
ActionAgent = create_react_agent(
|
1098 |
+
model=llm,
|
1099 |
+
tools=execution_tools, # Has access to all execution tools
|
1100 |
+
prompt=sys_msg,
|
1101 |
+
state_schema=AgentState,
|
1102 |
+
name="ActionAgent"
|
1103 |
+
)
|
1104 |
+
logger.info("Created ActionAgent successfully")
|
1105 |
+
|
1106 |
+
# Evaluation agent with stricter prompt
|
1107 |
+
EvaluationAgent = create_react_agent(
|
1108 |
+
model=llm,
|
1109 |
+
tools=[], # No tools needed for evaluation
|
1110 |
+
prompt=eval_sys_msg, # Use the specialized evaluation prompt
|
1111 |
+
state_schema=AgentState,
|
1112 |
+
name="EvaluationAgent"
|
1113 |
+
)
|
1114 |
+
logger.info("Created EvaluationAgent successfully")
|
1115 |
+
except Exception as e:
|
1116 |
+
logger.error(f"Error creating agent: {str(e)}")
|
1117 |
+
import traceback
|
1118 |
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
1119 |
+
raise
|
1120 |
+
|
1121 |
+
# Build the StateGraph
|
1122 |
+
logger.info("Building StateGraph")
|
1123 |
+
try:
|
1124 |
+
builder = StateGraph(AgentState)
|
1125 |
+
|
1126 |
+
# Add agent nodes first
|
1127 |
+
builder.add_node("PerceptionAgent", PerceptionAgent)
|
1128 |
+
builder.add_node("ActionAgent", ActionAgent)
|
1129 |
+
builder.add_node("EvaluationAgent", EvaluationAgent)
|
1130 |
+
|
1131 |
+
# Define the flow with a starting edge
|
1132 |
+
builder.set_entry_point("PerceptionAgent")
|
1133 |
+
|
1134 |
+
# Add the edges for the simpler linear flow
|
1135 |
+
builder.add_edge("PerceptionAgent", "ActionAgent")
|
1136 |
+
builder.add_edge("ActionAgent", "EvaluationAgent")
|
1137 |
+
|
1138 |
+
# Set EvaluationAgent as the end node
|
1139 |
+
builder.set_finish_point("EvaluationAgent")
|
1140 |
+
|
1141 |
+
logger.info("Compiling StateGraph")
|
1142 |
+
return builder.compile()
|
1143 |
+
except Exception as e:
|
1144 |
+
logger.error(f"Error building graph: {str(e)}")
|
1145 |
+
import traceback
|
1146 |
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
1147 |
+
raise
|
1148 |
+
except Exception as e:
|
1149 |
+
logger.error(f"Overall error in build_graph: {str(e)}")
|
1150 |
+
import traceback
|
1151 |
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
1152 |
+
raise
|
1153 |
+
|
1154 |
+
def get_final_answer(text):
|
1155 |
+
"""Extract just the FINAL ANSWER from the model's response.
|
1156 |
+
|
1157 |
+
Args:
|
1158 |
+
text: The full text response from the LLM
|
1159 |
+
|
1160 |
+
Returns:
|
1161 |
+
str: The extracted answer without the "FINAL ANSWER:" prefix
|
1162 |
+
"""
|
1163 |
+
# Log the raw text for debugging if needed
|
1164 |
+
logger.debug(f"Extracting answer from: {text[:200]}...")
|
1165 |
+
|
1166 |
+
if not text:
|
1167 |
+
logger.warning("Empty response received")
|
1168 |
+
return "No answer provided."
|
1169 |
+
|
1170 |
+
# Method 1: Look for "FINAL ANSWER:" with most comprehensive pattern matching
|
1171 |
+
pattern = r'(?:^|\n)FINAL ANSWER:\s*(.*?)(?:\n\s*$|$)'
|
1172 |
+
match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
|
1173 |
+
if match:
|
1174 |
+
# Return just the answer part, cleaned up
|
1175 |
+
logger.debug("Found answer using pattern 1")
|
1176 |
+
return match.group(1).strip()
|
1177 |
+
|
1178 |
+
# Method 2: Try looking for variations on the final answer format
|
1179 |
+
for variant in ["FINAL ANSWER:", "FINAL_ANSWER:", "Final Answer:", "Answer:"]:
|
1180 |
+
lines = text.split('\n')
|
1181 |
+
for i, line in enumerate(reversed(lines)):
|
1182 |
+
if variant in line:
|
1183 |
+
# Extract everything after the variant text
|
1184 |
+
logger.debug(f"Found answer using variant: {variant}")
|
1185 |
+
answer = line[line.find(variant) + len(variant):].strip()
|
1186 |
+
if answer:
|
1187 |
+
return answer
|
1188 |
+
# If the answer is on the next line, return that
|
1189 |
+
if i > 0:
|
1190 |
+
next_line = lines[len(lines) - i]
|
1191 |
+
if next_line.strip():
|
1192 |
+
return next_line.strip()
|
1193 |
+
|
1194 |
+
# Method 3: Look for phrases that suggest an answer
|
1195 |
+
for phrase in ["The answer is", "The result is", "We get", "Therefore,", "In conclusion,"]:
|
1196 |
+
phrase_pos = text.find(phrase)
|
1197 |
+
if phrase_pos != -1:
|
1198 |
+
# Try to extract everything after the phrase until the end of the sentence
|
1199 |
+
sentence_end = text.find(".", phrase_pos)
|
1200 |
+
if sentence_end != -1:
|
1201 |
+
logger.debug(f"Found answer using phrase: {phrase}")
|
1202 |
+
return text[phrase_pos + len(phrase):sentence_end].strip()
|
1203 |
+
|
1204 |
+
# Method 4: Fall back to taking the last paragraph with actual content
|
1205 |
+
paragraphs = text.strip().split('\n\n')
|
1206 |
+
for para in reversed(paragraphs):
|
1207 |
+
para = para.strip()
|
1208 |
+
if para and not para.startswith("I ") and not para.lower().startswith("to "):
|
1209 |
+
logger.debug("Using last meaningful paragraph")
|
1210 |
+
# If paragraph is very long, try to extract a concise answer
|
1211 |
+
if len(para) > 100:
|
1212 |
+
sentences = re.split(r'[.!?]', para)
|
1213 |
+
for sentence in reversed(sentences):
|
1214 |
+
sent = sentence.strip()
|
1215 |
+
if sent and len(sent) > 5 and not sent.startswith("I "):
|
1216 |
+
return sent
|
1217 |
+
return para
|
1218 |
+
|
1219 |
+
# Method 5: Last resort - just return the last line with content
|
1220 |
+
lines = text.strip().split('\n')
|
1221 |
+
for line in reversed(lines):
|
1222 |
+
line = line.strip()
|
1223 |
+
if line and len(line) > 3:
|
1224 |
+
logger.debug("Using last line with content")
|
1225 |
+
return line
|
1226 |
+
|
1227 |
+
# If everything fails, warn and return the truncated response
|
1228 |
+
logger.warning("Could not find a properly formatted answer")
|
1229 |
+
return text[:100] + "..." if len(text) > 100 else text
|
1230 |
+
|
1231 |
+
# test
|
1232 |
+
if __name__ == "__main__":
|
1233 |
+
question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?"
|
1234 |
+
# Build the graph
|
1235 |
+
graph = build_graph(provider="groq")
|
1236 |
+
# Run the graph
|
1237 |
+
messages = [HumanMessage(content=question)]
|
1238 |
+
messages = graph.invoke({"messages": messages})
|
1239 |
+
for m in messages["messages"]:
|
1240 |
+
m.pretty_print()
|
1241 |
+
|
1242 |
+
# ─────────────────────────────────────────────── Tool for Code Analysis ───────────────────────────────────────────────────────────────
|
1243 |
+
@tool
|
1244 |
+
def analyze_code(code_string: str) -> str:
|
1245 |
+
"""Analyze a string of code to understand its structure, functionality, and potential issues.
|
1246 |
+
|
1247 |
+
Args:
|
1248 |
+
code_string: The code to analyze as a string.
|
1249 |
+
|
1250 |
+
Returns:
|
1251 |
+
A structured analysis of the code including functions, classes, and key operations.
|
1252 |
+
"""
|
1253 |
+
try:
|
1254 |
+
import ast
|
1255 |
+
|
1256 |
+
# Try to parse with Python's AST module
|
1257 |
+
try:
|
1258 |
+
parsed = ast.parse(code_string)
|
1259 |
+
|
1260 |
+
# Extract functions and classes
|
1261 |
+
functions = [node.name for node in ast.walk(parsed) if isinstance(node, ast.FunctionDef)]
|
1262 |
+
classes = [node.name for node in ast.walk(parsed) if isinstance(node, ast.ClassDef)]
|
1263 |
+
imports = [node.names[0].name for node in ast.walk(parsed) if isinstance(node, ast.Import)]
|
1264 |
+
imports.extend([f"{node.module}.{name.name}" if node.module else name.name
|
1265 |
+
for node in ast.walk(parsed) if isinstance(node, ast.ImportFrom)
|
1266 |
+
for name in node.names])
|
1267 |
+
|
1268 |
+
# Count various node types for complexity assessment
|
1269 |
+
num_loops = len([node for node in ast.walk(parsed)
|
1270 |
+
if isinstance(node, (ast.For, ast.While))])
|
1271 |
+
num_conditionals = len([node for node in ast.walk(parsed)
|
1272 |
+
if isinstance(node, (ast.If, ast.IfExp))])
|
1273 |
+
|
1274 |
+
analysis = {
|
1275 |
+
"language": "Python",
|
1276 |
+
"functions": functions,
|
1277 |
+
"classes": classes,
|
1278 |
+
"imports": imports,
|
1279 |
+
"complexity": {
|
1280 |
+
"functions": len(functions),
|
1281 |
+
"classes": len(classes),
|
1282 |
+
"loops": num_loops,
|
1283 |
+
"conditionals": num_conditionals
|
1284 |
+
}
|
1285 |
+
}
|
1286 |
+
return str(analysis)
|
1287 |
+
except SyntaxError:
|
1288 |
+
# If not valid Python, try some simple pattern matching
|
1289 |
+
if "{" in code_string and "}" in code_string:
|
1290 |
+
if "function" in code_string or "=>" in code_string:
|
1291 |
+
language = "JavaScript/TypeScript"
|
1292 |
+
elif "func" in code_string or "struct" in code_string:
|
1293 |
+
language = "Go or Rust"
|
1294 |
+
elif "public" in code_string or "private" in code_string or "class" in code_string:
|
1295 |
+
language = "Java/C#/C++"
|
1296 |
+
else:
|
1297 |
+
language = "Unknown C-like language"
|
1298 |
+
elif "<" in code_string and ">" in code_string and ("/>" in code_string or "</"):
|
1299 |
+
language = "HTML/XML/JSX"
|
1300 |
+
else:
|
1301 |
+
language = "Unknown"
|
1302 |
+
|
1303 |
+
return f"Non-Python code detected ({language}). Basic code structure analysis not available."
|
1304 |
+
except Exception as e:
|
1305 |
+
return f"Error analyzing code: {str(e)}"
|
1306 |
+
|
1307 |
+
@tool
|
1308 |
+
def read_code_file(file_path: str) -> str:
|
1309 |
+
"""Read a code file and return its contents with proper syntax detection.
|
1310 |
+
|
1311 |
+
Args:
|
1312 |
+
file_path: Path to the code file.
|
1313 |
+
|
1314 |
+
Returns:
|
1315 |
+
The file contents and detected language.
|
1316 |
+
"""
|
1317 |
+
try:
|
1318 |
+
# Check if file exists
|
1319 |
+
import os
|
1320 |
+
if not os.path.exists(file_path):
|
1321 |
+
return f"Error: File '{file_path}' does not exist."
|
1322 |
+
|
1323 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
1324 |
+
content = f.read()
|
1325 |
+
|
1326 |
+
# Try to detect language from extension
|
1327 |
+
ext = os.path.splitext(file_path)[1].lower()
|
1328 |
+
|
1329 |
+
language_map = {
|
1330 |
+
'.py': 'Python',
|
1331 |
+
'.js': 'JavaScript',
|
1332 |
+
'.ts': 'TypeScript',
|
1333 |
+
'.html': 'HTML',
|
1334 |
+
'.css': 'CSS',
|
1335 |
+
'.java': 'Java',
|
1336 |
+
'.c': 'C',
|
1337 |
+
'.cpp': 'C++',
|
1338 |
+
'.cs': 'C#',
|
1339 |
+
'.go': 'Go',
|
1340 |
+
'.rs': 'Rust',
|
1341 |
+
'.php': 'PHP',
|
1342 |
+
'.rb': 'Ruby',
|
1343 |
+
'.sh': 'Shell',
|
1344 |
+
'.bat': 'Batch',
|
1345 |
+
'.ps1': 'PowerShell',
|
1346 |
+
'.sql': 'SQL',
|
1347 |
+
'.json': 'JSON',
|
1348 |
+
'.xml': 'XML',
|
1349 |
+
'.yaml': 'YAML',
|
1350 |
+
'.yml': 'YAML',
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
language = language_map.get(ext, 'Unknown')
|
1354 |
+
|
1355 |
+
return f"File content ({language}):\n\n{content}"
|
1356 |
+
except Exception as e:
|
1357 |
+
return f"Error reading file: {str(e)}"
|
1358 |
+
|
1359 |
+
@tool
|
1360 |
+
def analyze_python_function(function_name: str, code_string: str) -> str:
|
1361 |
+
"""Extract and analyze a specific function from Python code.
|
1362 |
+
|
1363 |
+
Args:
|
1364 |
+
function_name: The name of the function to analyze.
|
1365 |
+
code_string: The complete code containing the function.
|
1366 |
+
|
1367 |
+
Returns:
|
1368 |
+
Analysis of the function including parameters, return type, and docstring.
|
1369 |
+
"""
|
1370 |
+
try:
|
1371 |
+
import ast
|
1372 |
+
import inspect
|
1373 |
+
from types import CodeType, FunctionType
|
1374 |
+
|
1375 |
+
# Parse the code string
|
1376 |
+
parsed = ast.parse(code_string)
|
1377 |
+
|
1378 |
+
# Find the function definition
|
1379 |
+
function_def = None
|
1380 |
+
for node in ast.walk(parsed):
|
1381 |
+
if isinstance(node, ast.FunctionDef) and node.name == function_name:
|
1382 |
+
function_def = node
|
1383 |
+
break
|
1384 |
+
|
1385 |
+
if not function_def:
|
1386 |
+
return f"Function '{function_name}' not found in the provided code."
|
1387 |
+
|
1388 |
+
# Extract parameters
|
1389 |
+
params = []
|
1390 |
+
for arg in function_def.args.args:
|
1391 |
+
param_name = arg.arg
|
1392 |
+
# Get annotation if it exists
|
1393 |
+
if arg.annotation:
|
1394 |
+
if isinstance(arg.annotation, ast.Name):
|
1395 |
+
param_type = arg.annotation.id
|
1396 |
+
elif isinstance(arg.annotation, ast.Attribute):
|
1397 |
+
param_type = f"{arg.annotation.value.id}.{arg.annotation.attr}"
|
1398 |
+
else:
|
1399 |
+
param_type = "complex_type"
|
1400 |
+
params.append(f"{param_name}: {param_type}")
|
1401 |
+
else:
|
1402 |
+
params.append(param_name)
|
1403 |
+
|
1404 |
+
# Extract return type if it exists
|
1405 |
+
return_type = None
|
1406 |
+
if function_def.returns:
|
1407 |
+
if isinstance(function_def.returns, ast.Name):
|
1408 |
+
return_type = function_def.returns.id
|
1409 |
+
elif isinstance(function_def.returns, ast.Attribute):
|
1410 |
+
return_type = f"{function_def.returns.value.id}.{function_def.returns.attr}"
|
1411 |
+
else:
|
1412 |
+
return_type = "complex_return_type"
|
1413 |
+
|
1414 |
+
# Extract docstring
|
1415 |
+
docstring = ast.get_docstring(function_def)
|
1416 |
+
|
1417 |
+
# Create a summary
|
1418 |
+
summary = {
|
1419 |
+
"function_name": function_name,
|
1420 |
+
"parameters": params,
|
1421 |
+
"return_type": return_type,
|
1422 |
+
"docstring": docstring,
|
1423 |
+
"decorators": [d.id if isinstance(d, ast.Name) else "complex_decorator" for d in function_def.decorator_list],
|
1424 |
+
"line_count": len(function_def.body)
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
# Create a more explicit string representation that ensures key terms are included
|
1428 |
+
result = f"Function '{function_name}' analysis:\n"
|
1429 |
+
result += f"- Parameters: {', '.join(params)}\n"
|
1430 |
+
result += f"- Return type: {return_type or 'None specified'}\n"
|
1431 |
+
result += f"- Docstring: {docstring or 'None'}\n"
|
1432 |
+
result += f"- Line count: {len(function_def.body)}"
|
1433 |
+
|
1434 |
+
return result
|
1435 |
+
except Exception as e:
|
1436 |
+
return f"Error analyzing function: {str(e)}"
|
1437 |
+
|
1438 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
1439 |
+
# ─────────────────────────────────────────────── Tool for News Article Retrieval ──────────────────────────────────────────────────────────────────────
|
1440 |
+
# ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
1441 |
+
|
1442 |
+
@tool
|
1443 |
+
def news_article_search(query: str, top_k: int = 3) -> Dict[str, str]:
|
1444 |
+
"""Search for and retrieve news articles with robust error handling for news sites.
|
1445 |
+
|
1446 |
+
Args:
|
1447 |
+
query: The news topic or keywords to search for.
|
1448 |
+
top_k: Maximum number of articles to retrieve.
|
1449 |
+
|
1450 |
+
Returns:
|
1451 |
+
A dictionary with search results formatted as XML-like document entries.
|
1452 |
+
"""
|
1453 |
+
# First, get URLs from DuckDuckGo with "news" focus
|
1454 |
+
results = []
|
1455 |
+
news_sources = [
|
1456 |
+
"bbc.com", "reuters.com", "apnews.com", "nasa.gov",
|
1457 |
+
"space.com", "universetoday.com", "nature.com", "science.org",
|
1458 |
+
"scientificamerican.com", "nytimes.com", "theguardian.com"
|
1459 |
+
]
|
1460 |
+
|
1461 |
+
# Find news from reliable sources
|
1462 |
+
try:
|
1463 |
+
with DDGS() as ddgs:
|
1464 |
+
search_query = f"{query} site:{' OR site:'.join(news_sources)}"
|
1465 |
+
for hit in ddgs.text(search_query, safesearch="On", max_results=top_k*2):
|
1466 |
+
url = hit.get("href") or hit.get("url", "")
|
1467 |
+
if not url:
|
1468 |
+
continue
|
1469 |
+
|
1470 |
+
# Add the search snippet first as a fallback
|
1471 |
+
result = {
|
1472 |
+
"source": url,
|
1473 |
+
"page": "",
|
1474 |
+
"content": hit.get("body", "")[:250],
|
1475 |
+
"title": hit.get("title", "")
|
1476 |
+
}
|
1477 |
+
|
1478 |
+
# Try to get better content via a more robust method
|
1479 |
+
try:
|
1480 |
+
headers = {
|
1481 |
+
"User-Agent": random.choice([
|
1482 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
|
1483 |
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15",
|
1484 |
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
|
1485 |
+
]),
|
1486 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
1487 |
+
"Accept-Language": "en-US,en;q=0.5",
|
1488 |
+
"Referer": "https://www.google.com/",
|
1489 |
+
"DNT": "1",
|
1490 |
+
"Connection": "keep-alive",
|
1491 |
+
"Upgrade-Insecure-Requests": "1"
|
1492 |
+
}
|
1493 |
+
|
1494 |
+
# Add a short delay between requests
|
1495 |
+
time.sleep(1 + random.random())
|
1496 |
+
|
1497 |
+
# Try to use newspaper3k for more reliable article extraction
|
1498 |
+
from newspaper import Article
|
1499 |
+
article = Article(url)
|
1500 |
+
article.download()
|
1501 |
+
article.parse()
|
1502 |
+
|
1503 |
+
# If we got meaningful content, update the result
|
1504 |
+
if article.text and len(article.text) > 100:
|
1505 |
+
# Get a summary - first paragraph + some highlights
|
1506 |
+
paragraphs = article.text.split('\n\n')
|
1507 |
+
first_para = paragraphs[0] if paragraphs else ""
|
1508 |
+
summary = first_para[:300]
|
1509 |
+
if len(paragraphs) > 1:
|
1510 |
+
summary += "... " + paragraphs[1][:200]
|
1511 |
+
|
1512 |
+
result["content"] = summary
|
1513 |
+
if article.title:
|
1514 |
+
result["title"] = article.title
|
1515 |
+
|
1516 |
+
except Exception as article_err:
|
1517 |
+
logger.warning(f"Article extraction failed for {url}: {article_err}")
|
1518 |
+
# Fallback to simple requests-based extraction
|
1519 |
+
try:
|
1520 |
+
resp = requests.get(url, timeout=12, headers=headers)
|
1521 |
+
resp.raise_for_status()
|
1522 |
+
soup = BeautifulSoup(resp.text, "html.parser")
|
1523 |
+
|
1524 |
+
# Try to get main content
|
1525 |
+
main_content = soup.find('main') or soup.find('article') or soup.find('div', class_='content')
|
1526 |
+
|
1527 |
+
if main_content:
|
1528 |
+
content = " ".join(main_content.get_text(separator=" ", strip=True).split()[:250])
|
1529 |
+
result["content"] = content
|
1530 |
+
except Exception as req_err:
|
1531 |
+
logger.warning(f"Fallback extraction failed for {url}: {req_err}")
|
1532 |
+
# Keep the original snippet as fallback
|
1533 |
+
|
1534 |
+
results.append(result)
|
1535 |
+
if len(results) >= top_k:
|
1536 |
+
break
|
1537 |
+
|
1538 |
+
except Exception as e:
|
1539 |
+
logger.error(f"News search failed: {e}")
|
1540 |
+
return format_search_docs([{
|
1541 |
+
"source": "Error",
|
1542 |
+
"page": "",
|
1543 |
+
"content": f"Failed to retrieve news articles for '{query}': {str(e)}"
|
1544 |
+
}])
|
1545 |
+
|
1546 |
+
if not results:
|
1547 |
+
# Fallback to regular web search
|
1548 |
+
logger.info(f"No news results found, falling back to web_search for {query}")
|
1549 |
+
return web_search(query, top_k)
|
1550 |
+
|
1551 |
+
return format_search_docs(results[:top_k])
|
1552 |
+
|
1553 |
+
# ───────────────────────────────────────────────────────────── Document Chunking Utilities ──────────────────────────────────────────────────────────
|
1554 |
+
def chunk_document(text: str, chunk_size: int = 1000, overlap: int = 100) -> List[str]:
|
1555 |
+
"""
|
1556 |
+
Split a large document into smaller chunks with overlap to maintain context across chunks.
|
1557 |
+
|
1558 |
+
Args:
|
1559 |
+
text: The document text to split into chunks
|
1560 |
+
chunk_size: Maximum size of each chunk in characters
|
1561 |
+
overlap: Number of characters to overlap between chunks
|
1562 |
+
|
1563 |
+
Returns:
|
1564 |
+
List of text chunks
|
1565 |
+
"""
|
1566 |
+
# If text is smaller than chunk_size, return it as is
|
1567 |
+
if len(text) <= chunk_size:
|
1568 |
+
return [text]
|
1569 |
+
|
1570 |
+
chunks = []
|
1571 |
+
start = 0
|
1572 |
+
|
1573 |
+
while start < len(text):
|
1574 |
+
# Get chunk with overlap
|
1575 |
+
end = min(start + chunk_size, len(text))
|
1576 |
+
|
1577 |
+
# Try to find sentence boundary for cleaner breaks
|
1578 |
+
if end < len(text):
|
1579 |
+
# Look for sentence endings: period, question mark, or exclamation followed by space
|
1580 |
+
for sentence_end in ['. ', '? ', '! ']:
|
1581 |
+
last_period = text[start:end].rfind(sentence_end)
|
1582 |
+
if last_period != -1:
|
1583 |
+
end = start + last_period + 2 # +2 to include the period and space
|
1584 |
+
break
|
1585 |
+
|
1586 |
+
# Add chunk to list
|
1587 |
+
chunks.append(text[start:end])
|
1588 |
+
|
1589 |
+
# Move start position, accounting for overlap
|
1590 |
+
start = end - overlap if end < len(text) else len(text)
|
1591 |
+
|
1592 |
+
return chunks
|
1593 |
+
|
1594 |
+
# Document processing utility that uses chunking
|
1595 |
+
def process_large_document(text: str, question: str, llm=None) -> str:
|
1596 |
+
"""
|
1597 |
+
Process a large document by chunking it and using retrieval to find relevant parts.
|
1598 |
+
|
1599 |
+
Args:
|
1600 |
+
text: The document text to process
|
1601 |
+
question: The question being asked about the document
|
1602 |
+
llm: Optional language model to use (defaults to agent's LLM)
|
1603 |
+
|
1604 |
+
Returns:
|
1605 |
+
Summarized answer based on relevant chunks
|
1606 |
+
"""
|
1607 |
+
if not llm:
|
1608 |
+
llm = RetryingChatGroq(model="deepseek-r1-distill-llama-70b", streaming=False, temperature=0)
|
1609 |
+
|
1610 |
+
# Split document into chunks
|
1611 |
+
chunks = chunk_document(text)
|
1612 |
+
|
1613 |
+
# If document is small enough, don't bother with retrieval
|
1614 |
+
if len(chunks) <= 1:
|
1615 |
+
return text
|
1616 |
+
|
1617 |
+
# For larger documents, create embeddings to find relevant chunks
|
1618 |
+
try:
|
1619 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
1620 |
+
from langchain.vectorstores import FAISS
|
1621 |
+
from langchain.schema import Document
|
1622 |
+
|
1623 |
+
# Create documents with chunk content
|
1624 |
+
documents = [Document(page_content=chunk, metadata={"chunk_id": i}) for i, chunk in enumerate(chunks)]
|
1625 |
+
|
1626 |
+
# Create embeddings and vector store
|
1627 |
+
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
|
1628 |
+
vectorstore = FAISS.from_documents(documents, embeddings)
|
1629 |
+
|
1630 |
+
# Get most relevant chunks
|
1631 |
+
relevant_chunks = vectorstore.similarity_search(question, k=2) # Get top 2 most relevant chunks
|
1632 |
+
|
1633 |
+
# Join the relevant chunks
|
1634 |
+
relevant_text = "\n\n".join([doc.page_content for doc in relevant_chunks])
|
1635 |
+
|
1636 |
+
# Option 1: Return relevant chunks directly
|
1637 |
+
return relevant_text
|
1638 |
+
|
1639 |
+
# Option 2: Summarize with LLM (commented out for now)
|
1640 |
+
# prompt = f"Using only the following information, answer the question: '{question}'\n\nInformation:\n{relevant_text}"
|
1641 |
+
# response = llm.invoke([HumanMessage(content=prompt)])
|
1642 |
+
# return response.content
|
1643 |
+
|
1644 |
+
except Exception as e:
|
1645 |
+
# Fall back to first chunk if retrieval fails
|
1646 |
+
logger.warning(f"Retrieval failed: {e}. Falling back to first chunk.")
|
1647 |
+
return chunks[0]
|
v2/utils/agent2.py
ADDED
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
|
4 |
+
from langchain_core.tools import tool
|
5 |
+
from langgraph.prebuilt import tools_condition, ToolNode
|
6 |
+
from langgraph.graph import START, StateGraph, MessagesState
|
7 |
+
|
8 |
+
from langchain_community.tools.tavily_search import TavilySearchResults
|
9 |
+
from langchain_community.document_loaders import WikipediaLoader, ArxivLoader
|
10 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
11 |
+
from langchain_community.vectorstores import FAISS
|
12 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
13 |
+
from langchain_groq import ChatGroq
|
14 |
+
from langchain.tools.retriever import create_retriever_tool
|
15 |
+
|
16 |
+
"""LangGraph Agent"""
|
17 |
+
import os
|
18 |
+
from dotenv import load_dotenv
|
19 |
+
from langgraph.graph import START, StateGraph, MessagesState
|
20 |
+
from langgraph.prebuilt import tools_condition
|
21 |
+
from langgraph.prebuilt import ToolNode
|
22 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
23 |
+
from langchain_groq import ChatGroq
|
24 |
+
from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint, HuggingFaceEmbeddings
|
25 |
+
from langchain_community.tools.tavily_search import TavilySearchResults
|
26 |
+
from langchain_community.document_loaders import WikipediaLoader
|
27 |
+
from langchain_community.document_loaders import ArxivLoader
|
28 |
+
from langchain_community.vectorstores import SupabaseVectorStore,FAISS
|
29 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
30 |
+
from langchain_core.tools import tool
|
31 |
+
from langchain.tools.retriever import create_retriever_tool
|
32 |
+
#from supabase.client import Client, create_client
|
33 |
+
|
34 |
+
# ──────────────────────────────────────────────────────────
|
35 |
+
# ENV
|
36 |
+
# ──────────────────────────────────────────────────────────
|
37 |
+
load_dotenv()
|
38 |
+
# API Keys from .env file
|
39 |
+
os.environ.setdefault("OPENAI_API_KEY", "<YOUR_OPENAI_KEY>") # Set your own key or env var
|
40 |
+
os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY", "default_key_or_placeholder")
|
41 |
+
os.environ["MISTRAL_API_KEY"] = os.getenv("MISTRAL_API_KEY", "default_key_or_placeholder")
|
42 |
+
|
43 |
+
# Tavily API Key
|
44 |
+
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "default_key_or_placeholder")
|
45 |
+
_forbidden = ["nsfw", "porn", "sex", "explicit"]
|
46 |
+
_playwright_available = True # set False to disable Playwright
|
47 |
+
|
48 |
+
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") # dim = 768
|
49 |
+
|
50 |
+
@tool
|
51 |
+
def multiply(a: int, b: int) -> int:
|
52 |
+
"""Multiply two numbers.
|
53 |
+
|
54 |
+
Args:
|
55 |
+
a: first int
|
56 |
+
b: second int
|
57 |
+
"""
|
58 |
+
return a * b
|
59 |
+
|
60 |
+
@tool
|
61 |
+
def add(a: int, b: int) -> int:
|
62 |
+
"""Add two numbers.
|
63 |
+
|
64 |
+
Args:
|
65 |
+
a: first int
|
66 |
+
b: second int
|
67 |
+
"""
|
68 |
+
return a + b
|
69 |
+
|
70 |
+
@tool
|
71 |
+
def subtract(a: int, b: int) -> int:
|
72 |
+
"""Subtract two numbers.
|
73 |
+
|
74 |
+
Args:
|
75 |
+
a: first int
|
76 |
+
b: second int
|
77 |
+
"""
|
78 |
+
return a - b
|
79 |
+
|
80 |
+
@tool
|
81 |
+
def divide(a: int, b: int) -> int:
|
82 |
+
"""Divide two numbers.
|
83 |
+
|
84 |
+
Args:
|
85 |
+
a: first int
|
86 |
+
b: second int
|
87 |
+
"""
|
88 |
+
if b == 0:
|
89 |
+
raise ValueError("Cannot divide by zero.")
|
90 |
+
return a / b
|
91 |
+
|
92 |
+
@tool
|
93 |
+
def modulus(a: int, b: int) -> int:
|
94 |
+
"""Get the modulus of two numbers.
|
95 |
+
|
96 |
+
Args:
|
97 |
+
a: first int
|
98 |
+
b: second int
|
99 |
+
"""
|
100 |
+
return a % b
|
101 |
+
|
102 |
+
@tool
|
103 |
+
def wiki_search(query: str) -> str:
|
104 |
+
"""Search Wikipedia for a query and return maximum 2 results.
|
105 |
+
|
106 |
+
Args:
|
107 |
+
query: The search query."""
|
108 |
+
search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
|
109 |
+
formatted_search_docs = "\n\n---\n\n".join(
|
110 |
+
[
|
111 |
+
f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
|
112 |
+
for doc in search_docs
|
113 |
+
])
|
114 |
+
return {"wiki_results": formatted_search_docs}
|
115 |
+
|
116 |
+
@tool
|
117 |
+
def web_search(query: str) -> str:
|
118 |
+
"""Search Tavily for a query and return maximum 3 results.
|
119 |
+
|
120 |
+
Args:
|
121 |
+
query: The search query."""
|
122 |
+
search_docs = TavilySearchResults(max_results=3).invoke(query=query)
|
123 |
+
formatted_search_docs = "\n\n---\n\n".join(
|
124 |
+
[
|
125 |
+
f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
|
126 |
+
for doc in search_docs
|
127 |
+
])
|
128 |
+
return {"web_results": formatted_search_docs}
|
129 |
+
|
130 |
+
@tool
|
131 |
+
def arvix_search(query: str) -> str:
|
132 |
+
"""Search Arxiv for a query and return maximum 3 result.
|
133 |
+
|
134 |
+
Args:
|
135 |
+
query: The search query."""
|
136 |
+
search_docs = ArxivLoader(query=query, load_max_docs=3).load()
|
137 |
+
formatted_search_docs = "\n\n---\n\n".join(
|
138 |
+
[
|
139 |
+
f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
|
140 |
+
for doc in search_docs
|
141 |
+
])
|
142 |
+
return {"arvix_results": formatted_search_docs}
|
143 |
+
|
144 |
+
|
145 |
+
|
146 |
+
# load the system prompt from the file
|
147 |
+
with open("system_prompt.txt", "r", encoding="utf-8") as f:
|
148 |
+
system_prompt = f.read()
|
149 |
+
|
150 |
+
# System message
|
151 |
+
sys_msg = SystemMessage(content=system_prompt)
|
152 |
+
|
153 |
+
# build a retriever
|
154 |
+
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") # dim=768
|
155 |
+
|
156 |
+
INDEX_PATH = "faiss_index"
|
157 |
+
if os.path.exists(INDEX_PATH):
|
158 |
+
vector_store = FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
|
159 |
+
else:
|
160 |
+
vector_store = FAISS.from_texts(["__init__"], embeddings)
|
161 |
+
vector_store.save_local(INDEX_PATH)
|
162 |
+
|
163 |
+
create_retriever_tool = create_retriever_tool(
|
164 |
+
retriever=vector_store.as_retriever(),
|
165 |
+
name="Question Search",
|
166 |
+
description="A tool to retrieve similar questions from a local FAISS vector store."
|
167 |
+
)
|
168 |
+
|
169 |
+
|
170 |
+
|
171 |
+
tools = [
|
172 |
+
multiply,
|
173 |
+
add,
|
174 |
+
subtract,
|
175 |
+
divide,
|
176 |
+
modulus,
|
177 |
+
wiki_search,
|
178 |
+
web_search,
|
179 |
+
arvix_search,
|
180 |
+
]
|
181 |
+
|
182 |
+
# Build graph function
|
183 |
+
def build_graph(provider: str = "groq"):
|
184 |
+
"""Build the graph"""
|
185 |
+
# Load environment variables from .env file
|
186 |
+
if provider == "google":
|
187 |
+
# Google Gemini
|
188 |
+
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
|
189 |
+
elif provider == "groq":
|
190 |
+
# Groq https://console.groq.com/docs/models
|
191 |
+
try:
|
192 |
+
llm = ChatGroq(model="deepseek-r1-distill-llama-70b", temperature=0) # optional : qwen-qwq-32b gemma2-9b-it
|
193 |
+
except Exception as e:
|
194 |
+
print(f"Error initializing Groq: {str(e)}")
|
195 |
+
raise
|
196 |
+
elif provider == "huggingface":
|
197 |
+
# TODO: Add huggingface endpoint
|
198 |
+
llm = ChatHuggingFace(
|
199 |
+
llm=HuggingFaceEndpoint(
|
200 |
+
url="https://api-inference.huggingface.co/models/Meta-DeepLearning/llama-2-7b-chat-hf",
|
201 |
+
temperature=0,
|
202 |
+
),
|
203 |
+
)
|
204 |
+
else:
|
205 |
+
raise ValueError("Invalid provider. Choose 'google', 'groq' or 'huggingface'.")
|
206 |
+
|
207 |
+
# Bind tools to LLM
|
208 |
+
llm_with_tools = llm.bind_tools(tools)
|
209 |
+
|
210 |
+
# Node
|
211 |
+
def assistant(state: MessagesState):
|
212 |
+
"""Assistant node"""
|
213 |
+
try:
|
214 |
+
if not state["messages"] or not state["messages"][-1].content:
|
215 |
+
raise ValueError("Empty message content")
|
216 |
+
return {"messages": [llm_with_tools.invoke(state["messages"])]}
|
217 |
+
except Exception as e:
|
218 |
+
print(f"Error in assistant node: {str(e)}")
|
219 |
+
raise
|
220 |
+
|
221 |
+
def retriever(state: MessagesState):
|
222 |
+
"""Retriever node"""
|
223 |
+
try:
|
224 |
+
if not state["messages"] or not state["messages"][0].content:
|
225 |
+
raise ValueError("Empty message content")
|
226 |
+
similar_question = vector_store.similarity_search(state["messages"][0].content)
|
227 |
+
example_msg = HumanMessage(
|
228 |
+
content=f"Here I provide a similar question and answer for reference: \n\n{similar_question[0].page_content}",
|
229 |
+
)
|
230 |
+
return {"messages": [sys_msg] + state["messages"] + [example_msg]}
|
231 |
+
except Exception as e:
|
232 |
+
print(f"Error in retriever node: {str(e)}")
|
233 |
+
raise
|
234 |
+
|
235 |
+
builder = StateGraph(MessagesState)
|
236 |
+
builder.add_node("retriever", retriever)
|
237 |
+
builder.add_node("assistant", assistant)
|
238 |
+
builder.add_node("tools", ToolNode(tools))
|
239 |
+
builder.add_edge(START, "retriever")
|
240 |
+
builder.add_edge("retriever", "assistant")
|
241 |
+
builder.add_conditional_edges(
|
242 |
+
"assistant",
|
243 |
+
tools_condition,
|
244 |
+
)
|
245 |
+
builder.add_edge("tools", "assistant")
|
246 |
+
|
247 |
+
# Compile graph
|
248 |
+
return builder.compile()
|
249 |
+
|
250 |
+
# test
|
251 |
+
if __name__ == "__main__":
|
252 |
+
question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?"
|
253 |
+
# Build the graph
|
254 |
+
graph = build_graph(provider="groq")
|
255 |
+
# Run the graph
|
256 |
+
messages = [HumanMessage(content=question)]
|
257 |
+
messages = graph.invoke({"messages": messages})
|
258 |
+
for m in messages["messages"]:
|
259 |
+
m.pretty_print()
|
v2/utils/all_analysis.txt
ADDED
File without changes
|
v2/utils/all_analysis_trace.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Unknown statement: 'glide 2 seconds to x: (pick random -240 to 240) y: 100'
|
2 |
+
when green flag clicked
|
3 |
+
go to x: 0 y: 100
|
4 |
+
forever
|
5 |
+
glide 2 seconds to x: (pick random -240 to 240) y: 100
|
6 |
+
if <touching [Cat v]?> then
|
7 |
+
broadcast [Game Over v]
|
8 |
+
end
|
9 |
+
end
|
10 |
+
|
v2/utils/all_generated_blocks.json
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"event_whenkeypressed_1": {
|
3 |
+
"block_name": "when () key pressed",
|
4 |
+
"block_type": "Events",
|
5 |
+
"op_code": "event_whenkeypressed",
|
6 |
+
"block_shape": "Hat Block",
|
7 |
+
"functionality": "This Hat block initiates the script when a specified keyboard key is pressed.",
|
8 |
+
"inputs": {},
|
9 |
+
"fields": {
|
10 |
+
"KEY_OPTION": [
|
11 |
+
"space ",
|
12 |
+
null
|
13 |
+
]
|
14 |
+
},
|
15 |
+
"shadow": false,
|
16 |
+
"topLevel": true,
|
17 |
+
"id": "event_whenkeypressed_1",
|
18 |
+
"parent": null,
|
19 |
+
"next": "motion_changeyby_1"
|
20 |
+
},
|
21 |
+
"motion_changeyby_1": {
|
22 |
+
"block_name": "change y by ()",
|
23 |
+
"block_type": "Motion",
|
24 |
+
"block_shape": "Stack Block",
|
25 |
+
"op_code": "motion_changeyby",
|
26 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
27 |
+
"inputs": {
|
28 |
+
"DY": {
|
29 |
+
"kind": "value",
|
30 |
+
"value": 10
|
31 |
+
}
|
32 |
+
},
|
33 |
+
"fields": {},
|
34 |
+
"shadow": false,
|
35 |
+
"topLevel": false,
|
36 |
+
"id": "motion_changeyby_1",
|
37 |
+
"parent": "event_whenkeypressed_1",
|
38 |
+
"next": "control_wait_1"
|
39 |
+
},
|
40 |
+
"motion_changeyby_2": {
|
41 |
+
"block_name": "change y by ()",
|
42 |
+
"block_type": "Motion",
|
43 |
+
"block_shape": "Stack Block",
|
44 |
+
"op_code": "motion_changeyby",
|
45 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
46 |
+
"inputs": {
|
47 |
+
"DY": {
|
48 |
+
"kind": "value",
|
49 |
+
"value": -10
|
50 |
+
}
|
51 |
+
},
|
52 |
+
"fields": {},
|
53 |
+
"shadow": false,
|
54 |
+
"topLevel": false,
|
55 |
+
"id": "motion_changeyby_2",
|
56 |
+
"parent": "control_wait_1",
|
57 |
+
"next": null
|
58 |
+
},
|
59 |
+
"control_wait_1": {
|
60 |
+
"block_name": "wait () seconds",
|
61 |
+
"block_type": "Control",
|
62 |
+
"block_shape": "Stack Block",
|
63 |
+
"op_code": "control_wait",
|
64 |
+
"functionality": "Pauses the script for a specified duration.",
|
65 |
+
"inputs": {
|
66 |
+
"DURATION": {
|
67 |
+
"kind": "value",
|
68 |
+
"value": 0.2
|
69 |
+
}
|
70 |
+
},
|
71 |
+
"fields": {},
|
72 |
+
"shadow": false,
|
73 |
+
"topLevel": false,
|
74 |
+
"id": "control_wait_1",
|
75 |
+
"parent": "motion_changeyby_1",
|
76 |
+
"next": "motion_changeyby_2"
|
77 |
+
}
|
78 |
+
}
|
v2/utils/block_builder_main.py
ADDED
@@ -0,0 +1,446 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import copy
|
3 |
+
import re
|
4 |
+
from collections import defaultdict
|
5 |
+
import secrets
|
6 |
+
import string
|
7 |
+
from typing import Dict, Any, TypedDict
|
8 |
+
from plan_generator_10 import generate_plan,generate_blocks_from_opcodes,all_block_definitions
|
9 |
+
|
10 |
+
#################################################################################################################################################################
|
11 |
+
#--------------------------------------------------[Security key id generation for the better understanding of keys]---------------------------------------------
|
12 |
+
#################################################################################################################################################################
|
13 |
+
|
14 |
+
def generate_secure_token(length=20):
|
15 |
+
charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~"
|
16 |
+
return ''.join(secrets.choice(charset) for _ in range(length))
|
17 |
+
|
18 |
+
#################################################################################################################################################################
|
19 |
+
#--------------------------------------------------[Processed the two Skelton as input and generate refined skelton json]----------------------------------------
|
20 |
+
#################################################################################################################################################################
|
21 |
+
|
22 |
+
def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
23 |
+
|
24 |
+
processed_blocks = {}
|
25 |
+
|
26 |
+
# Initialize dictionaries to store and reuse generated unique IDs
|
27 |
+
# This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
|
28 |
+
variable_id_map = defaultdict(lambda: generate_secure_token(20))
|
29 |
+
broadcast_id_map = defaultdict(lambda: generate_secure_token(20))
|
30 |
+
|
31 |
+
for block_id, gen_block_data in generated_output_json.items():
|
32 |
+
processed_block = {}
|
33 |
+
all_gen_block_data = all_generated_blocks.get(block_id, {})
|
34 |
+
|
35 |
+
# Copy and update fields, inputs, next, parent, shadow, topLevel, mutation, and opcode
|
36 |
+
processed_block["opcode"] = all_gen_block_data.get("op_code", gen_block_data.get("op_code"))
|
37 |
+
processed_block["inputs"] = {}
|
38 |
+
processed_block["fields"] = {}
|
39 |
+
processed_block["shadow"] = all_gen_block_data.get("shadow", gen_block_data.get("shadow"))
|
40 |
+
processed_block["topLevel"] = all_gen_block_data.get("topLevel", gen_block_data.get("topLevel"))
|
41 |
+
processed_block["parent"] = all_gen_block_data.get("parent", gen_block_data.get("parent"))
|
42 |
+
processed_block["next"] = all_gen_block_data.get("next", gen_block_data.get("next"))
|
43 |
+
if "mutation" in all_gen_block_data:
|
44 |
+
processed_block["mutation"] = all_gen_block_data["mutation"]
|
45 |
+
|
46 |
+
# Process inputs
|
47 |
+
if "inputs" in all_gen_block_data:
|
48 |
+
for input_name, input_data in all_gen_block_data["inputs"].items():
|
49 |
+
if input_name in ["SUBSTACK", "CONDITION"]:
|
50 |
+
# These should always be type 2
|
51 |
+
if isinstance(input_data, list) and len(input_data) == 2:
|
52 |
+
processed_block["inputs"][input_name] = [2, input_data[1]]
|
53 |
+
elif isinstance(input_data, dict) and input_data.get("kind") == "block":
|
54 |
+
processed_block["inputs"][input_name] = [2, input_data.get("block")]
|
55 |
+
else: # Fallback for unexpected formats, try to use the original if possible
|
56 |
+
processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [2, None])
|
57 |
+
|
58 |
+
elif isinstance(input_data, dict):
|
59 |
+
if input_data.get("kind") == "value":
|
60 |
+
# Case 1: Direct value input
|
61 |
+
processed_block["inputs"][input_name] = [
|
62 |
+
1,
|
63 |
+
[
|
64 |
+
4,
|
65 |
+
str(input_data.get("value", ""))
|
66 |
+
]
|
67 |
+
]
|
68 |
+
elif input_data.get("kind") == "block":
|
69 |
+
# Case 3: Nested block input
|
70 |
+
existing_shadow_value = ""
|
71 |
+
if input_name in gen_block_data.get("inputs", {}) and \
|
72 |
+
isinstance(gen_block_data["inputs"][input_name], list) and \
|
73 |
+
len(gen_block_data["inputs"][input_name]) > 2 and \
|
74 |
+
isinstance(gen_block_data["inputs"][input_name][2], list) and \
|
75 |
+
len(gen_block_data["inputs"][input_name][2]) > 1:
|
76 |
+
existing_shadow_value = gen_block_data["inputs"][input_name][2][1]
|
77 |
+
|
78 |
+
processed_block["inputs"][input_name] = [
|
79 |
+
3,
|
80 |
+
input_data.get("block", ""),
|
81 |
+
[
|
82 |
+
10, # Assuming 10 for number/string shadow
|
83 |
+
existing_shadow_value
|
84 |
+
]
|
85 |
+
]
|
86 |
+
elif input_data.get("kind") == "menu":
|
87 |
+
# Handle menu inputs like in event_broadcast
|
88 |
+
menu_option = input_data.get("option", "")
|
89 |
+
|
90 |
+
# Generate or retrieve a unique ID for the broadcast message
|
91 |
+
broadcast_id = broadcast_id_map[menu_option] # Use defaultdict for unique IDs
|
92 |
+
|
93 |
+
processed_block["inputs"][input_name] = [
|
94 |
+
1,
|
95 |
+
[
|
96 |
+
11, # This is typically the code for menu dropdowns
|
97 |
+
menu_option,
|
98 |
+
broadcast_id
|
99 |
+
]
|
100 |
+
]
|
101 |
+
elif isinstance(input_data, list):
|
102 |
+
# For cases like TOUCHINGOBJECTMENU, where input_data is a list [1, "block_id"]
|
103 |
+
processed_block["inputs"][input_name] = input_data
|
104 |
+
|
105 |
+
|
106 |
+
# Process fields
|
107 |
+
if "fields" in all_gen_block_data:
|
108 |
+
for field_name, field_value in all_gen_block_data["fields"].items():
|
109 |
+
if field_name == "VARIABLE" and isinstance(field_value, list) and len(field_value) > 0:
|
110 |
+
# Generate or retrieve a unique ID for the variable
|
111 |
+
variable_name = field_value[0]
|
112 |
+
unique_id = variable_id_map[variable_name] # Use defaultdict for unique IDs
|
113 |
+
|
114 |
+
processed_block["fields"][field_name] = [
|
115 |
+
variable_name,
|
116 |
+
unique_id
|
117 |
+
]
|
118 |
+
elif field_name == "STOP_OPTION":
|
119 |
+
processed_block["fields"][field_name] = [
|
120 |
+
field_value[0],
|
121 |
+
None
|
122 |
+
]
|
123 |
+
elif field_name == "TOUCHINGOBJECTMENU":
|
124 |
+
referenced_menu_block_id = all_gen_block_data["inputs"].get("TOUCHINGOBJECTMENU", [None, None])[1]
|
125 |
+
if referenced_menu_block_id and referenced_menu_block_id in all_generated_blocks:
|
126 |
+
menu_block = all_generated_blocks[referenced_menu_block_id]
|
127 |
+
menu_value = menu_block.get("fields", {}).get("TOUCHINGOBJECTMENU", ["", None])[0]
|
128 |
+
processed_block["fields"][field_name] = [menu_value, None]
|
129 |
+
else:
|
130 |
+
processed_block["fields"][field_name] = [field_value[0], None]
|
131 |
+
else:
|
132 |
+
processed_block["fields"][field_name] = field_value
|
133 |
+
|
134 |
+
# Remove unwanted keys from the processed block
|
135 |
+
keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
|
136 |
+
for key in keys_to_remove:
|
137 |
+
if key in processed_block:
|
138 |
+
del processed_block[key]
|
139 |
+
|
140 |
+
processed_blocks[block_id] = processed_block
|
141 |
+
return processed_blocks
|
142 |
+
#################################################################################################################################################################
|
143 |
+
#--------------------------------------------------[Unique secret key for skelton json to make sure it donot overwrite each other]-------------------------------
|
144 |
+
#################################################################################################################################################################
|
145 |
+
|
146 |
+
def rename_blocks(block_json: dict, opcode_count: dict) -> tuple[dict, dict]:
|
147 |
+
"""
|
148 |
+
Replace each block key in block_json and each identifier in opcode_count
|
149 |
+
with a newly generated secure token.
|
150 |
+
|
151 |
+
Args:
|
152 |
+
block_json: Mapping of block_key -> block_data.
|
153 |
+
opcode_count: Mapping of opcode -> list of block_keys.
|
154 |
+
|
155 |
+
Returns:
|
156 |
+
A tuple of (new_block_json, new_opcode_count) with updated keys.
|
157 |
+
"""
|
158 |
+
# Step 1: Generate a secure token mapping for every existing block key
|
159 |
+
token_map = {}
|
160 |
+
for old_key in block_json.keys():
|
161 |
+
# Ensure uniqueness in the unlikely event of a collision
|
162 |
+
while True:
|
163 |
+
new_key = generate_secure_token()
|
164 |
+
if new_key not in token_map.values():
|
165 |
+
break
|
166 |
+
token_map[old_key] = new_key
|
167 |
+
|
168 |
+
# Step 2: Rebuild block_json with new keys
|
169 |
+
new_block_json = {}
|
170 |
+
for old_key, block in block_json.items():
|
171 |
+
new_key = token_map[old_key]
|
172 |
+
new_block_json[new_key] = block.copy()
|
173 |
+
|
174 |
+
# Update parent and next references
|
175 |
+
if 'parent' in block and block['parent'] in token_map:
|
176 |
+
new_block_json[new_key]['parent'] = token_map[block['parent']]
|
177 |
+
if 'next' in block and block['next'] in token_map:
|
178 |
+
new_block_json[new_key]['next'] = token_map[block['next']]
|
179 |
+
|
180 |
+
# Update inputs if they reference blocks
|
181 |
+
for inp_key, inp_val in block.get('inputs', {}).items():
|
182 |
+
if isinstance(inp_val, list) and len(inp_val) == 2:
|
183 |
+
idx, ref = inp_val
|
184 |
+
if idx in (2, 3) and isinstance(ref, str) and ref in token_map:
|
185 |
+
new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]]
|
186 |
+
|
187 |
+
# Step 3: Update opcode count map
|
188 |
+
new_opcode_count = {}
|
189 |
+
for opcode, key_list in opcode_count.items():
|
190 |
+
new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list]
|
191 |
+
|
192 |
+
return new_block_json, new_opcode_count
|
193 |
+
|
194 |
+
#################################################################################################################################################################
|
195 |
+
#--------------------------------------------------[Helper function to add Variables and Broadcasts [USed in main app file for main projectjson]]----------------
|
196 |
+
#################################################################################################################################################################
|
197 |
+
|
198 |
+
def variable_intialization(project_data):
|
199 |
+
"""
|
200 |
+
Updates variable and broadcast definitions in a Scratch project JSON,
|
201 |
+
populating the 'variables' and 'broadcasts' sections of the Stage target
|
202 |
+
and extracting initial values for variables.
|
203 |
+
|
204 |
+
Args:
|
205 |
+
project_data (dict): The loaded JSON data of the Scratch project.
|
206 |
+
|
207 |
+
Returns:
|
208 |
+
dict: The updated project JSON data.
|
209 |
+
"""
|
210 |
+
|
211 |
+
stage_target = None
|
212 |
+
for target in project_data['targets']:
|
213 |
+
if target.get('isStage'):
|
214 |
+
stage_target = target
|
215 |
+
break
|
216 |
+
|
217 |
+
if stage_target is None:
|
218 |
+
print("Error: Stage target not found in the project data.")
|
219 |
+
return project_data
|
220 |
+
|
221 |
+
# Ensure 'variables' and 'broadcasts' exist in the Stage target
|
222 |
+
if "variables" not in stage_target:
|
223 |
+
stage_target["variables"] = {}
|
224 |
+
if "broadcasts" not in stage_target:
|
225 |
+
stage_target["broadcasts"] = {}
|
226 |
+
|
227 |
+
# Helper function to recursively find and update variable/broadcast fields
|
228 |
+
def process_dict(obj):
|
229 |
+
if isinstance(obj, dict):
|
230 |
+
# Check for "data_setvariableto" opcode to extract initial values
|
231 |
+
if obj.get("opcode") == "data_setvariableto":
|
232 |
+
variable_field = obj.get("fields", {}).get("VARIABLE")
|
233 |
+
value_input = obj.get("inputs", {}).get("VALUE")
|
234 |
+
|
235 |
+
if variable_field and isinstance(variable_field, list) and len(variable_field) == 2:
|
236 |
+
var_name = variable_field[0]
|
237 |
+
var_id = variable_field[1]
|
238 |
+
|
239 |
+
initial_value = ""
|
240 |
+
if value_input and isinstance(value_input, list) and len(value_input) > 1 and \
|
241 |
+
isinstance(value_input[1], list) and len(value_input[1]) > 1:
|
242 |
+
# Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]]
|
243 |
+
if value_input[1][0] == 10: # Direct value like [10, "0"]
|
244 |
+
initial_value = str(value_input[1][1])
|
245 |
+
elif value_input[1][0] == 12 and len(value_input) > 2 and isinstance(value_input[2], list) and value_input[2][0] == 10: # Variable reference with initial value block
|
246 |
+
initial_value = str(value_input[2][1])
|
247 |
+
elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs
|
248 |
+
initial_value = str(value_input[1])
|
249 |
+
|
250 |
+
|
251 |
+
# Add/update the variable in the Stage's 'variables' with its initial value
|
252 |
+
stage_target["variables"][var_id] = [var_name, initial_value]
|
253 |
+
|
254 |
+
|
255 |
+
for key, value in obj.items():
|
256 |
+
# Process variable definitions in 'fields' (for blocks that define variables like 'show variable')
|
257 |
+
if key == "VARIABLE" and isinstance(value, list) and len(value) == 2:
|
258 |
+
var_name = value[0]
|
259 |
+
var_id = value[1]
|
260 |
+
# Only add if not already defined with an initial value from set_variableto
|
261 |
+
if var_id not in stage_target["variables"]:
|
262 |
+
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
|
263 |
+
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
|
264 |
+
stage_target["variables"][var_id][0] = var_name
|
265 |
+
|
266 |
+
|
267 |
+
# Process broadcast definitions in 'inputs' (BROADCAST_INPUT)
|
268 |
+
elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \
|
269 |
+
isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11:
|
270 |
+
broadcast_name = value[1][1]
|
271 |
+
broadcast_id = value[1][2]
|
272 |
+
# Add/update the broadcast in the Stage's 'broadcasts'
|
273 |
+
stage_target["broadcasts"][broadcast_id] = broadcast_name
|
274 |
+
|
275 |
+
# Process broadcast definitions in 'fields' (BROADCAST_OPTION)
|
276 |
+
elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2:
|
277 |
+
broadcast_name = value[0]
|
278 |
+
broadcast_id = value[1]
|
279 |
+
# Add/update the broadcast in the Stage's 'broadcasts'
|
280 |
+
stage_target["broadcasts"][broadcast_id] = broadcast_name
|
281 |
+
|
282 |
+
# Recursively call for nested dictionaries or lists
|
283 |
+
process_dict(value)
|
284 |
+
elif isinstance(obj, list):
|
285 |
+
for i, item in enumerate(obj):
|
286 |
+
# Process variable references in 'inputs' (like [12, "score", "id"])
|
287 |
+
if isinstance(item, list) and len(item) == 3 and item[0] == 12:
|
288 |
+
var_name = item[1]
|
289 |
+
var_id = item[2]
|
290 |
+
# Only add if not already defined with an initial value from set_variableto
|
291 |
+
if var_id not in stage_target["variables"]:
|
292 |
+
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
|
293 |
+
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
|
294 |
+
stage_target["variables"][var_id][0] = var_name
|
295 |
+
|
296 |
+
process_dict(item)
|
297 |
+
|
298 |
+
# Iterate through all targets to process their blocks
|
299 |
+
for target in project_data['targets']:
|
300 |
+
if "blocks" in target:
|
301 |
+
for block_id, block_data in target["blocks"].items():
|
302 |
+
process_dict(block_data)
|
303 |
+
|
304 |
+
return project_data
|
305 |
+
|
306 |
+
def deduplicate_variables(project_data):
|
307 |
+
"""
|
308 |
+
Removes duplicate variable entries in the 'variables' dictionary of the Stage target,
|
309 |
+
prioritizing entries with non-empty values.
|
310 |
+
|
311 |
+
Args:
|
312 |
+
project_data (dict): The loaded JSON data of the Scratch project.
|
313 |
+
|
314 |
+
Returns:
|
315 |
+
dict: The updated project JSON data with deduplicated variables.
|
316 |
+
"""
|
317 |
+
|
318 |
+
stage_target = None
|
319 |
+
for target in project_data['targets']:
|
320 |
+
if target.get('isStage'):
|
321 |
+
stage_target = target
|
322 |
+
break
|
323 |
+
|
324 |
+
if stage_target is None:
|
325 |
+
print("Error: Stage target not found in the project data.")
|
326 |
+
return project_data
|
327 |
+
|
328 |
+
if "variables" not in stage_target:
|
329 |
+
return project_data # No variables to deduplicate
|
330 |
+
|
331 |
+
# Use a temporary dictionary to store the preferred variable entry by name
|
332 |
+
# Format: {variable_name: [variable_id, variable_name, variable_value]}
|
333 |
+
resolved_variables = {}
|
334 |
+
|
335 |
+
for var_id, var_info in stage_target["variables"].items():
|
336 |
+
var_name = var_info[0]
|
337 |
+
var_value = var_info[1]
|
338 |
+
|
339 |
+
if var_name not in resolved_variables:
|
340 |
+
# If the variable name is not yet seen, add it
|
341 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
342 |
+
else:
|
343 |
+
# If the variable name is already seen, decide which one to keep
|
344 |
+
existing_id, existing_name, existing_value = resolved_variables[var_name]
|
345 |
+
|
346 |
+
# Prioritize the entry with a non-empty value
|
347 |
+
if var_value != "" and existing_value == "":
|
348 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
349 |
+
# If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent)
|
350 |
+
# The current logic will effectively keep the last one encountered that has a value,
|
351 |
+
# or the very last one if all are empty.
|
352 |
+
elif var_value != "" and existing_value != "":
|
353 |
+
# If there are multiple non-empty values for the same variable name
|
354 |
+
# this keeps the one from the most recent iteration.
|
355 |
+
# For the given example, this will correctly keep "5".
|
356 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
357 |
+
elif var_value == "" and existing_value == "":
|
358 |
+
# If both are empty, just keep the current one (arbitrary)
|
359 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
360 |
+
|
361 |
+
|
362 |
+
# Reconstruct the 'variables' dictionary using the resolved entries
|
363 |
+
new_variables_dict = {}
|
364 |
+
for var_name, var_data in resolved_variables.items():
|
365 |
+
var_id_to_keep = var_data[0]
|
366 |
+
var_name_to_keep = var_data[1]
|
367 |
+
var_value_to_keep = var_data[2]
|
368 |
+
new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep]
|
369 |
+
|
370 |
+
stage_target["variables"] = new_variables_dict
|
371 |
+
|
372 |
+
return project_data
|
373 |
+
|
374 |
+
def variable_adder_main(project_data):
|
375 |
+
try:
|
376 |
+
declare_variable_json= variable_intialization(project_data)
|
377 |
+
except Exception as e:
|
378 |
+
print(f"Error error in the variable initialization opcodes: {e}")
|
379 |
+
try:
|
380 |
+
processed_json= deduplicate_variables(declare_variable_json)
|
381 |
+
return
|
382 |
+
except Exception as e:
|
383 |
+
print(f"Error error in the variable initialization opcodes: {e}")
|
384 |
+
|
385 |
+
#################################################################################################################################################################
|
386 |
+
#--------------------------------------------------[Helper main function]----------------------------------------------------------------------------------------
|
387 |
+
#################################################################################################################################################################
|
388 |
+
|
389 |
+
def block_builder(opcode_count,pseudo_code):
|
390 |
+
try:
|
391 |
+
generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(opcode_count, all_block_definitions)
|
392 |
+
except Exception as e:
|
393 |
+
print(f"Error generating blocks from opcodes: {e}")
|
394 |
+
return {}
|
395 |
+
try:
|
396 |
+
all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code)
|
397 |
+
except Exception as e:
|
398 |
+
print(f"Error generating plan from blocks: {e}")
|
399 |
+
return {}
|
400 |
+
try:
|
401 |
+
processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json)
|
402 |
+
except Exception as e:
|
403 |
+
print(f"Error processing Scratch blocks: {e}")
|
404 |
+
return {}
|
405 |
+
renamed_blocks, renamed_counts = rename_blocks(processed_blocks, initial_opcode_occurrences)
|
406 |
+
return renamed_blocks
|
407 |
+
|
408 |
+
#################################################################################################################################################################
|
409 |
+
#--------------------------------------------------[Example use of the function here]----------------------------------------------------------------------------
|
410 |
+
#################################################################################################################################################################
|
411 |
+
|
412 |
+
initial_opcode_counts = [
|
413 |
+
{
|
414 |
+
"opcode": "event_whenflagclicked",
|
415 |
+
"count": 1
|
416 |
+
},
|
417 |
+
{
|
418 |
+
"opcode": "data_setvariableto",
|
419 |
+
"count": 2
|
420 |
+
},
|
421 |
+
{
|
422 |
+
"opcode": "data_showvariable",
|
423 |
+
"count": 2
|
424 |
+
},
|
425 |
+
{
|
426 |
+
"opcode": "event_broadcast",
|
427 |
+
"count": 1
|
428 |
+
}
|
429 |
+
]
|
430 |
+
pseudo_code="""
|
431 |
+
when green flag clicked
|
432 |
+
set [score v] to (0)
|
433 |
+
set [lives v] to (3)
|
434 |
+
show variable [score v]
|
435 |
+
show variable [lives v]
|
436 |
+
broadcast [Game Start v]
|
437 |
+
"""
|
438 |
+
|
439 |
+
generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions)
|
440 |
+
all_generated_blocks = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code)
|
441 |
+
processed_blocks= process_scratch_blocks(all_generated_blocks, generated_output_json)
|
442 |
+
print(all_generated_blocks)
|
443 |
+
print("--------------\n\n")
|
444 |
+
print(processed_blocks)
|
445 |
+
print("--------------\n\n")
|
446 |
+
print(initial_opcode_occurrences)
|
v2/utils/block_correcter.py
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import secrets
|
3 |
+
import string
|
4 |
+
from collections import defaultdict
|
5 |
+
|
6 |
+
def generate_secure_token(length=20):
|
7 |
+
charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~"
|
8 |
+
return ''.join(secrets.choice(charset) for _ in range(length))
|
9 |
+
|
10 |
+
def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
11 |
+
processed_blocks = {}
|
12 |
+
|
13 |
+
# Initialize dictionaries to store and reuse generated unique IDs
|
14 |
+
# This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
|
15 |
+
variable_id_map = defaultdict(lambda: generate_secure_token(20))
|
16 |
+
broadcast_id_map = defaultdict(lambda: generate_secure_token(20))
|
17 |
+
|
18 |
+
for block_id, gen_block_data in generated_output_json.items():
|
19 |
+
processed_block = {}
|
20 |
+
all_gen_block_data = all_generated_blocks.get(block_id, {})
|
21 |
+
|
22 |
+
# Copy and update fields, inputs, next, parent, shadow, topLevel, mutation, and opcode
|
23 |
+
processed_block["opcode"] = all_gen_block_data.get("op_code", gen_block_data.get("op_code"))
|
24 |
+
processed_block["inputs"] = {}
|
25 |
+
processed_block["fields"] = {}
|
26 |
+
processed_block["shadow"] = all_gen_block_data.get("shadow", gen_block_data.get("shadow"))
|
27 |
+
processed_block["topLevel"] = all_gen_block_data.get("topLevel", gen_block_data.get("topLevel"))
|
28 |
+
processed_block["parent"] = all_gen_block_data.get("parent", gen_block_data.get("parent"))
|
29 |
+
processed_block["next"] = all_gen_block_data.get("next", gen_block_data.get("next"))
|
30 |
+
if "mutation" in all_gen_block_data:
|
31 |
+
processed_block["mutation"] = all_gen_block_data["mutation"]
|
32 |
+
|
33 |
+
# Process inputs
|
34 |
+
if "inputs" in all_gen_block_data:
|
35 |
+
for input_name, input_data in all_gen_block_data["inputs"].items():
|
36 |
+
if input_name in ["SUBSTACK", "CONDITION"]:
|
37 |
+
# These should always be type 2
|
38 |
+
if isinstance(input_data, list) and len(input_data) == 2:
|
39 |
+
processed_block["inputs"][input_name] = [2, input_data[1]]
|
40 |
+
elif isinstance(input_data, dict) and input_data.get("kind") == "block":
|
41 |
+
processed_block["inputs"][input_name] = [2, input_data.get("block")]
|
42 |
+
else: # Fallback for unexpected formats, try to use the original if possible
|
43 |
+
processed_block["inputs"][input_name] = gen_block_data["inputs"].get(input_name, [2, None])
|
44 |
+
|
45 |
+
elif isinstance(input_data, dict):
|
46 |
+
if input_data.get("kind") == "value":
|
47 |
+
# Case 1: Direct value input
|
48 |
+
processed_block["inputs"][input_name] = [
|
49 |
+
1,
|
50 |
+
[
|
51 |
+
4,
|
52 |
+
str(input_data.get("value", ""))
|
53 |
+
]
|
54 |
+
]
|
55 |
+
elif input_data.get("kind") == "block":
|
56 |
+
# Case 3: Nested block input
|
57 |
+
existing_shadow_value = ""
|
58 |
+
if input_name in gen_block_data.get("inputs", {}) and \
|
59 |
+
isinstance(gen_block_data["inputs"][input_name], list) and \
|
60 |
+
len(gen_block_data["inputs"][input_name]) > 2 and \
|
61 |
+
isinstance(gen_block_data["inputs"][input_name][2], list) and \
|
62 |
+
len(gen_block_data["inputs"][input_name][2]) > 1:
|
63 |
+
existing_shadow_value = gen_block_data["inputs"][input_name][2][1]
|
64 |
+
|
65 |
+
processed_block["inputs"][input_name] = [
|
66 |
+
3,
|
67 |
+
input_data.get("block", ""),
|
68 |
+
[
|
69 |
+
10, # Assuming 10 for number/string shadow
|
70 |
+
existing_shadow_value
|
71 |
+
]
|
72 |
+
]
|
73 |
+
elif input_data.get("kind") == "menu":
|
74 |
+
# Handle menu inputs like in event_broadcast
|
75 |
+
menu_option = input_data.get("option", "")
|
76 |
+
|
77 |
+
# Generate or retrieve a unique ID for the broadcast message
|
78 |
+
broadcast_id = broadcast_id_map[menu_option] # Use defaultdict for unique IDs
|
79 |
+
|
80 |
+
processed_block["inputs"][input_name] = [
|
81 |
+
1,
|
82 |
+
[
|
83 |
+
11, # This is typically the code for menu dropdowns
|
84 |
+
menu_option,
|
85 |
+
broadcast_id
|
86 |
+
]
|
87 |
+
]
|
88 |
+
elif isinstance(input_data, list):
|
89 |
+
# For cases like TOUCHINGOBJECTMENU, where input_data is a list [1, "block_id"]
|
90 |
+
processed_block["inputs"][input_name] = input_data
|
91 |
+
|
92 |
+
|
93 |
+
# Process fields
|
94 |
+
if "fields" in all_gen_block_data:
|
95 |
+
for field_name, field_value in all_gen_block_data["fields"].items():
|
96 |
+
if field_name == "VARIABLE" and isinstance(field_value, list) and len(field_value) > 0:
|
97 |
+
# Generate or retrieve a unique ID for the variable
|
98 |
+
variable_name = field_value[0]
|
99 |
+
unique_id = variable_id_map[variable_name] # Use defaultdict for unique IDs
|
100 |
+
|
101 |
+
processed_block["fields"][field_name] = [
|
102 |
+
variable_name,
|
103 |
+
unique_id
|
104 |
+
]
|
105 |
+
elif field_name == "STOP_OPTION":
|
106 |
+
processed_block["fields"][field_name] = [
|
107 |
+
field_value[0],
|
108 |
+
None
|
109 |
+
]
|
110 |
+
elif field_name == "TOUCHINGOBJECTMENU":
|
111 |
+
referenced_menu_block_id = all_gen_block_data["inputs"].get("TOUCHINGOBJECTMENU", [None, None])[1]
|
112 |
+
if referenced_menu_block_id and referenced_menu_block_id in all_generated_blocks:
|
113 |
+
menu_block = all_generated_blocks[referenced_menu_block_id]
|
114 |
+
menu_value = menu_block.get("fields", {}).get("TOUCHINGOBJECTMENU", ["", None])[0]
|
115 |
+
processed_block["fields"][field_name] = [menu_value, None]
|
116 |
+
else:
|
117 |
+
processed_block["fields"][field_name] = [field_value[0], None]
|
118 |
+
else:
|
119 |
+
processed_block["fields"][field_name] = field_value
|
120 |
+
|
121 |
+
# Remove unwanted keys from the processed block
|
122 |
+
keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
|
123 |
+
for key in keys_to_remove:
|
124 |
+
if key in processed_block:
|
125 |
+
del processed_block[key]
|
126 |
+
|
127 |
+
processed_blocks[block_id] = processed_block
|
128 |
+
return processed_blocks
|
129 |
+
|
130 |
+
# Path to your JSON file
|
131 |
+
if __name__ == "__main__":
|
132 |
+
all_generated_blocks_path = 'all_generated_blocks.json'
|
133 |
+
generated_output_json_path = 'generated_output_json.json'
|
134 |
+
|
135 |
+
# Open and load the JSON files into Python dictionaries
|
136 |
+
with open(all_generated_blocks_path, 'r') as f:
|
137 |
+
all_generated_blocks_data = json.load(f)
|
138 |
+
|
139 |
+
with open(generated_output_json_path, 'r') as f:
|
140 |
+
generated_output_json_data = json.load(f)
|
141 |
+
|
142 |
+
processed_blocks = process_scratch_blocks(all_generated_blocks_data, generated_output_json_data)
|
143 |
+
print(json.dumps(processed_blocks, indent=4))
|
v2/utils/block_function.py
ADDED
@@ -0,0 +1,1147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import copy
|
3 |
+
import re
|
4 |
+
|
5 |
+
def generate_blocks_from_opcodes(opcode_counts, all_block_definitions):
|
6 |
+
"""
|
7 |
+
Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition.
|
8 |
+
It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories.
|
9 |
+
It ensures that menu blocks are only generated as children of their respective parent blocks.
|
10 |
+
|
11 |
+
Args:
|
12 |
+
opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property.
|
13 |
+
Example: [{"opcode": "motion_gotoxy", "count": 1}]
|
14 |
+
all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types.
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
tuple: A tuple containing:
|
18 |
+
- dict: A JSON object where keys are generated block IDs and values are the block definitions.
|
19 |
+
- dict: The opcode_occurrences dictionary for consistent unique key generation across functions.
|
20 |
+
"""
|
21 |
+
generated_blocks = {}
|
22 |
+
opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys
|
23 |
+
|
24 |
+
# Define explicit parent-menu relationships for linking purposes
|
25 |
+
# This maps main_opcode -> list of (input_field_name, menu_opcode)
|
26 |
+
explicit_menu_links = {
|
27 |
+
"motion_goto": [("TO", "motion_goto_menu")],
|
28 |
+
"motion_glideto": [("TO", "motion_glideto_menu")],
|
29 |
+
"motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
|
30 |
+
"sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
|
31 |
+
"sensing_of": [("OBJECT", "sensing_of_object_menu")],
|
32 |
+
"sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
|
33 |
+
"control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
|
34 |
+
"sound_play": [("SOUND_MENU", "sound_sounds_menu")],
|
35 |
+
"sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
|
36 |
+
"looks_switchcostumeto": [("COSTUME", "looks_costume")],
|
37 |
+
"looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
|
38 |
+
}
|
39 |
+
|
40 |
+
# --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus ---
|
41 |
+
for item in opcode_counts:
|
42 |
+
opcode = item.get("opcode")
|
43 |
+
count = item.get("count", 1)
|
44 |
+
|
45 |
+
if not opcode:
|
46 |
+
print("Warning: Skipping item with missing 'opcode'.")
|
47 |
+
continue
|
48 |
+
|
49 |
+
if opcode not in all_block_definitions:
|
50 |
+
print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.")
|
51 |
+
continue
|
52 |
+
|
53 |
+
for _ in range(count):
|
54 |
+
# Increment occurrence count for the current main opcode
|
55 |
+
opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1
|
56 |
+
main_block_instance_num = opcode_occurrences[opcode]
|
57 |
+
|
58 |
+
main_block_unique_key = f"{opcode}_{main_block_instance_num}"
|
59 |
+
|
60 |
+
# Create a deep copy of the main block definition
|
61 |
+
main_block_data = copy.deepcopy(all_block_definitions[opcode])
|
62 |
+
|
63 |
+
# Set properties for a top-level main block
|
64 |
+
main_block_data["parent"] = None
|
65 |
+
main_block_data["next"] = None
|
66 |
+
main_block_data["topLevel"] = True
|
67 |
+
main_block_data["shadow"] = False # Main blocks are typically not shadows
|
68 |
+
|
69 |
+
generated_blocks[main_block_unique_key] = main_block_data
|
70 |
+
|
71 |
+
# If this main block has associated menus, generate and link them now
|
72 |
+
if opcode in explicit_menu_links:
|
73 |
+
for input_field_name, menu_opcode_type in explicit_menu_links[opcode]:
|
74 |
+
if menu_opcode_type in all_block_definitions:
|
75 |
+
# Increment the occurrence for the menu block type
|
76 |
+
opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1
|
77 |
+
menu_block_instance_num = opcode_occurrences[menu_opcode_type]
|
78 |
+
|
79 |
+
menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type])
|
80 |
+
|
81 |
+
# Generate a unique key for this specific menu instance
|
82 |
+
menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}"
|
83 |
+
|
84 |
+
# Set properties for a shadow menu block
|
85 |
+
menu_block_data["shadow"] = True
|
86 |
+
menu_block_data["topLevel"] = False
|
87 |
+
menu_block_data["next"] = None
|
88 |
+
menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance
|
89 |
+
|
90 |
+
# Update the main block's input to point to this unique menu instance
|
91 |
+
if input_field_name in main_block_data.get("inputs", {}) and \
|
92 |
+
isinstance(main_block_data["inputs"][input_field_name], list) and \
|
93 |
+
len(main_block_data["inputs"][input_field_name]) > 1 and \
|
94 |
+
main_block_data["inputs"][input_field_name][0] == 1:
|
95 |
+
|
96 |
+
main_block_data["inputs"][input_field_name][1] = menu_unique_key
|
97 |
+
|
98 |
+
generated_blocks[menu_unique_key] = menu_block_data
|
99 |
+
|
100 |
+
return generated_blocks, opcode_occurrences
|
101 |
+
|
102 |
+
def interpret_pseudo_code_and_update_blocks(generated_blocks_json, pseudo_code, all_block_definitions, opcode_occurrences):
|
103 |
+
"""
|
104 |
+
Interprets pseudo-code to update the generated Scratch blocks, replacing static values
|
105 |
+
with dynamic values and establishing stacking/nesting logic.
|
106 |
+
|
107 |
+
Args:
|
108 |
+
generated_blocks_json (dict): The JSON object of pre-generated blocks.
|
109 |
+
pseudo_code (str): The pseudo-code string to interpret.
|
110 |
+
all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types.
|
111 |
+
opcode_occurrences (dict): A dictionary to keep track of opcode occurrences for unique key generation.
|
112 |
+
|
113 |
+
Returns:
|
114 |
+
dict: The updated JSON object of Scratch blocks.
|
115 |
+
"""
|
116 |
+
updated_blocks = copy.deepcopy(generated_blocks_json)
|
117 |
+
|
118 |
+
# Helper to find a block by opcode and optionally by a unique part of its key
|
119 |
+
def find_block_by_opcode(opcode_to_find, instance_num=None, parent_key=None):
|
120 |
+
for key, block in updated_blocks.items():
|
121 |
+
if block["opcode"] == opcode_to_find:
|
122 |
+
if instance_num is not None:
|
123 |
+
# Check if the key ends with the instance number
|
124 |
+
if key.endswith(f"_{instance_num}"):
|
125 |
+
return key, block
|
126 |
+
elif parent_key is not None:
|
127 |
+
# For menu blocks, check if their parent matches
|
128 |
+
if block.get("shadow") and block.get("parent") == parent_key:
|
129 |
+
return key, block
|
130 |
+
else:
|
131 |
+
# Return the first one found if no specific instance is needed
|
132 |
+
return key, block
|
133 |
+
return None, None
|
134 |
+
|
135 |
+
# Helper to get a unique key for a new block if needed
|
136 |
+
def get_unique_key(opcode_prefix):
|
137 |
+
count = 1
|
138 |
+
while f"{opcode_prefix}_{count}" in updated_blocks:
|
139 |
+
count += 1
|
140 |
+
return f"{opcode_prefix}_{count}"
|
141 |
+
|
142 |
+
lines = [line.strip() for line in pseudo_code.strip().split('\n') if line.strip()]
|
143 |
+
|
144 |
+
# Track the current script and nesting
|
145 |
+
current_script_head = None
|
146 |
+
current_parent_stack = [] # Stores (parent_block_key, indent_level, last_child_key)
|
147 |
+
indent_level = 0
|
148 |
+
|
149 |
+
# Create a mapping from block name patterns to their opcodes and input details
|
150 |
+
# Prioritize more specific patterns first
|
151 |
+
pseudo_code_to_opcode_map = {
|
152 |
+
re.compile(r"when green flag clicked"): {"opcode": "event_whenflagclicked"},
|
153 |
+
re.compile(r"go to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_gotoxy", "input_names": ["X", "Y"]},
|
154 |
+
re.compile(r"set \[(.+?) v\] to (.+)"): {"opcode": "data_setvariableto", "field_name": "VARIABLE", "input_name": "VALUE"},
|
155 |
+
re.compile(r"change \[(.+?) v\] by \((.+)\)"): {"opcode": "data_changevariableby", "field_name": "VARIABLE", "input_name": "VALUE"},
|
156 |
+
re.compile(r"show variable \[(.+?) v\]"): {"opcode": "data_showvariable", "field_name": "VARIABLE"},
|
157 |
+
re.compile(r"hide variable \[(.+?) v\]"): {"opcode": "data_hidevariable", "field_name": "VARIABLE"},
|
158 |
+
re.compile(r"forever"): {"opcode": "control_forever"},
|
159 |
+
re.compile(r"glide \((.+?)\) seconds to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_glidesecstoxy", "input_names": ["SECS", "X", "Y"]},
|
160 |
+
re.compile(r"if <\((.+)\) < \((.+)\)> then"): {"opcode": "control_if", "input_names": ["OPERAND1", "OPERAND2"], "condition_opcode": "operator_lt"},
|
161 |
+
re.compile(r"if <touching \[(.+?) v\]\?> then"): {"opcode": "control_if", "input_name": "TOUCHINGOBJECTMENU", "condition_opcode": "sensing_touchingobject"},
|
162 |
+
re.compile(r"set x to \((.+?)\)"): {"opcode": "motion_setx", "input_name": "X"},
|
163 |
+
re.compile(r"broadcast \[(.+?) v\]"): {"opcode": "event_broadcast", "input_name": "BROADCAST_INPUT"},
|
164 |
+
re.compile(r"stop \[(.+?) v\]"): {"opcode": "control_stop", "field_name": "STOP_OPTION"},
|
165 |
+
re.compile(r"end"): {"opcode": "end_block"}, # Special marker for script end/C-block end
|
166 |
+
}
|
167 |
+
|
168 |
+
# Create a reverse lookup for reporter block opcodes based on their pseudo-code representation
|
169 |
+
reporter_opcode_lookup = {}
|
170 |
+
for opcode, definition in all_block_definitions.items():
|
171 |
+
if definition.get("block_shape") == "Reporter Block":
|
172 |
+
block_name = definition.get("block_name")
|
173 |
+
if block_name:
|
174 |
+
# Remove parentheses for matching
|
175 |
+
clean_name = block_name.replace("(", "").replace(")", "").strip()
|
176 |
+
reporter_opcode_lookup[clean_name] = opcode
|
177 |
+
# Handle cases like "x position" vs "(x position)"
|
178 |
+
if clean_name not in reporter_opcode_lookup:
|
179 |
+
reporter_opcode_lookup[clean_name] = opcode
|
180 |
+
|
181 |
+
# Function to create a new block instance
|
182 |
+
def create_block_instance(opcode, opcode_occurrences, parent_key=None, is_shadow=False, is_top_level=False):
|
183 |
+
# Ensure unique key generation is consistent
|
184 |
+
opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1
|
185 |
+
unique_key = f"{opcode}_{opcode_occurrences[opcode]}"
|
186 |
+
|
187 |
+
new_block = copy.deepcopy(all_block_definitions.get(opcode, {}))
|
188 |
+
if not new_block:
|
189 |
+
print(f"Error: Definition for opcode '{opcode}' not found.")
|
190 |
+
return None, None
|
191 |
+
|
192 |
+
new_block["parent"] = parent_key
|
193 |
+
new_block["next"] = None # Will be set by stacking logic
|
194 |
+
new_block["topLevel"] = is_top_level
|
195 |
+
new_block["shadow"] = is_shadow
|
196 |
+
|
197 |
+
# Clear inputs/fields to be populated by pseudo-code parsing
|
198 |
+
if "inputs" in new_block:
|
199 |
+
new_block["inputs"] = {k: copy.deepcopy(v) for k, v in new_block["inputs"].items()} # Deep copy inputs
|
200 |
+
for input_name in new_block["inputs"]:
|
201 |
+
if isinstance(new_block["inputs"][input_name], list) and len(new_block["inputs"][input_name]) > 1:
|
202 |
+
# Reset input value, keep type 1 for block reference or type 4/10 for literal
|
203 |
+
if new_block["inputs"][input_name][0] == 1:
|
204 |
+
new_block["inputs"][input_name][1] = None # Placeholder for linked block ID
|
205 |
+
else:
|
206 |
+
new_block["inputs"][input_name][1] = ["", ""] # Default empty value
|
207 |
+
if "fields" in new_block:
|
208 |
+
new_block["fields"] = {k: copy.deepcopy(v) for k, v in new_block["fields"].items()} # Deep copy fields
|
209 |
+
for field_name in new_block["fields"]:
|
210 |
+
if isinstance(new_block["fields"][field_name], list) and len(new_block["fields"][field_name]) > 0:
|
211 |
+
new_block["fields"][field_name][0] = "" # Reset field value
|
212 |
+
|
213 |
+
updated_blocks[unique_key] = new_block
|
214 |
+
return unique_key, new_block
|
215 |
+
|
216 |
+
# Helper to parse input values
|
217 |
+
def parse_input_value(value_str):
|
218 |
+
value_str = value_str.strip()
|
219 |
+
# Handle numeric values (including those with + or - prefix)
|
220 |
+
if re.fullmatch(r"[-+]?\d+(\.\d+)?", value_str):
|
221 |
+
return [4, value_str] # Type 4 for number
|
222 |
+
# Handle string literals (e.g., "Hello!")
|
223 |
+
if value_str.startswith('"') and value_str.endswith('"'):
|
224 |
+
return [10, value_str.strip('"')] # Type 10 for string
|
225 |
+
# Handle variable/list names (e.g., [score v], [my list v])
|
226 |
+
if value_str.startswith('[') and value_str.endswith(']'):
|
227 |
+
var_name = value_str[1:-1].replace(' v', '').strip()
|
228 |
+
# For inputs that expect a variable, we might need a data_variable block
|
229 |
+
# For now, if it's a variable reference in an input, we'll return its name.
|
230 |
+
# The calling context (e.g., set variable's field vs. an input) will determine type.
|
231 |
+
return [12, var_name] # Custom type 12 for variable name, to be resolved later
|
232 |
+
|
233 |
+
# Handle nested reporter blocks (e.g., (x position))
|
234 |
+
if value_str.startswith('(') and value_str.endswith(')'):
|
235 |
+
inner_content = value_str[1:-1].strip()
|
236 |
+
# Check if it's a known reporter block
|
237 |
+
if inner_content in reporter_opcode_lookup:
|
238 |
+
return [3, reporter_opcode_lookup[inner_content]] # Type 3 for reporter block reference
|
239 |
+
# If not a known reporter, treat as a number or string
|
240 |
+
if re.fullmatch(r"[-+]?\d+(\.\d+)?", inner_content):
|
241 |
+
return [4, inner_content]
|
242 |
+
return [10, inner_content] # Default to string if not found in reporters
|
243 |
+
|
244 |
+
# Handle boolean conditions (e.g., <(x position) < (-235)>) - these are usually handled by parent regex
|
245 |
+
if value_str.startswith('<') and value_str.endswith('>'):
|
246 |
+
inner_condition = value_str[1:-1].strip()
|
247 |
+
# This is typically handled by the regex that matched the 'if' block itself.
|
248 |
+
# If this is called for a standalone boolean, it would be a reporter.
|
249 |
+
for op, def_ in all_block_definitions.items():
|
250 |
+
if def_.get("block_shape") == "Boolean Block" and def_.get("block_name") and \
|
251 |
+
def_["block_name"].replace("<", "").replace(">", "").strip() == inner_condition:
|
252 |
+
return [2, op] # Type 2 for boolean block reference
|
253 |
+
return [10, inner_condition] # Default to string if not found
|
254 |
+
|
255 |
+
return [10, value_str] # Default to string literal
|
256 |
+
|
257 |
+
|
258 |
+
# Main parsing loop
|
259 |
+
block_stack = [] # (block_key, indent_level, last_child_key_in_scope) for tracking nesting
|
260 |
+
|
261 |
+
for line_idx, raw_line in enumerate(lines):
|
262 |
+
current_line_indent = len(raw_line) - len(raw_line.lstrip())
|
263 |
+
line = raw_line.strip()
|
264 |
+
|
265 |
+
# Adjust block_stack based on current indent level
|
266 |
+
while block_stack and current_line_indent <= block_stack[-1][1]:
|
267 |
+
block_stack.pop()
|
268 |
+
|
269 |
+
matched_block_info = None
|
270 |
+
matched_values = None
|
271 |
+
|
272 |
+
# Try to match the line against known block patterns
|
273 |
+
for pattern_regex, info in pseudo_code_to_opcode_map.items():
|
274 |
+
match = pattern_regex.match(line)
|
275 |
+
if match:
|
276 |
+
matched_block_info = info
|
277 |
+
matched_values = match.groups()
|
278 |
+
break
|
279 |
+
|
280 |
+
if not matched_block_info:
|
281 |
+
print(f"Warning: Could not interpret line: '{line}'")
|
282 |
+
continue
|
283 |
+
|
284 |
+
opcode = matched_block_info["opcode"]
|
285 |
+
|
286 |
+
# Handle 'end' block separately as it signifies closing a C-block
|
287 |
+
if opcode == "end_block":
|
288 |
+
if block_stack:
|
289 |
+
block_stack.pop() # Pop the C-block parent
|
290 |
+
continue
|
291 |
+
|
292 |
+
parent_key = None
|
293 |
+
if block_stack:
|
294 |
+
parent_key = block_stack[-1][0] # The last block on the stack is the parent
|
295 |
+
|
296 |
+
# Create the new block instance
|
297 |
+
new_block_key, new_block_data = create_block_instance(
|
298 |
+
opcode,
|
299 |
+
opcode_occurrences,
|
300 |
+
parent_key=parent_key,
|
301 |
+
is_top_level=(parent_key is None)
|
302 |
+
)
|
303 |
+
if not new_block_key:
|
304 |
+
continue
|
305 |
+
|
306 |
+
# Link to previous block in the same script/nesting level
|
307 |
+
if block_stack:
|
308 |
+
# Update the 'next' of the previous block in the current scope
|
309 |
+
last_child_key_in_scope = block_stack[-1][2] if len(block_stack[-1]) > 2 else None
|
310 |
+
if last_child_key_in_scope and last_child_key_in_scope in updated_blocks:
|
311 |
+
updated_blocks[last_child_key_in_scope]["next"] = new_block_key
|
312 |
+
|
313 |
+
# Update the last child in the current scope
|
314 |
+
block_stack[-1] = (block_stack[-1][0], block_stack[-1][1], new_block_key)
|
315 |
+
|
316 |
+
# Populate inputs and fields
|
317 |
+
if matched_values:
|
318 |
+
# Handle specific block types with their inputs/fields
|
319 |
+
if opcode == "motion_gotoxy":
|
320 |
+
x_val = parse_input_value(matched_values[0])
|
321 |
+
y_val = parse_input_value(matched_values[1])
|
322 |
+
new_block_data["inputs"]["X"][1] = x_val[1]
|
323 |
+
new_block_data["inputs"]["Y"][1] = y_val[1]
|
324 |
+
new_block_data["inputs"]["X"][0] = x_val[0]
|
325 |
+
new_block_data["inputs"]["Y"][0] = y_val[0]
|
326 |
+
elif opcode == "data_setvariableto":
|
327 |
+
var_name = matched_values[0].replace(' v', '').strip()
|
328 |
+
value_parsed = parse_input_value(matched_values[1])
|
329 |
+
new_block_data["fields"]["VARIABLE"][0] = var_name
|
330 |
+
# Assuming variable ID is generated elsewhere or can be looked up
|
331 |
+
new_block_data["fields"]["VARIABLE"][1] = f"`var_{var_name}" # Placeholder for variable ID
|
332 |
+
new_block_data["inputs"]["VALUE"][0] = value_parsed[0]
|
333 |
+
new_block_data["inputs"]["VALUE"][1] = value_parsed[1]
|
334 |
+
elif opcode == "data_showvariable":
|
335 |
+
var_name = matched_values[0].replace(' v', '').strip()
|
336 |
+
new_block_data["fields"]["VARIABLE"][0] = var_name
|
337 |
+
new_block_data["fields"]["VARIABLE"][1] = f"`var_{var_name}" # Placeholder for variable ID
|
338 |
+
elif opcode == "motion_glidesecstoxy":
|
339 |
+
secs_val = parse_input_value(matched_values[0])
|
340 |
+
x_val = parse_input_value(matched_values[1])
|
341 |
+
y_val = parse_input_value(matched_values[2])
|
342 |
+
new_block_data["inputs"]["SECS"][1] = secs_val[1]
|
343 |
+
new_block_data["inputs"]["X"][1] = x_val[1]
|
344 |
+
new_block_data["inputs"]["Y"][1] = y_val[1]
|
345 |
+
new_block_data["inputs"]["SECS"][0] = secs_val[0]
|
346 |
+
new_block_data["inputs"]["X"][0] = x_val[0]
|
347 |
+
new_block_data["inputs"]["Y"][0] = y_val[0]
|
348 |
+
elif opcode == "motion_setx":
|
349 |
+
x_val = parse_input_value(matched_values[0])
|
350 |
+
new_block_data["inputs"]["X"][1] = x_val[1]
|
351 |
+
new_block_data["inputs"]["X"][0] = x_val[0]
|
352 |
+
elif opcode == "control_if":
|
353 |
+
condition_opcode = matched_block_info["condition_opcode"]
|
354 |
+
|
355 |
+
if condition_opcode == "operator_lt":
|
356 |
+
op1_str = matched_values[0].strip()
|
357 |
+
op2_str = matched_values[1].strip()
|
358 |
+
|
359 |
+
op1_parsed = parse_input_value(op1_str)
|
360 |
+
op2_parsed = parse_input_value(op2_str)
|
361 |
+
|
362 |
+
# Create operator_lt block as a shadow input for the IF condition
|
363 |
+
lt_block_key, lt_block_data = create_block_instance(
|
364 |
+
"operator_lt",
|
365 |
+
opcode_occurrences,
|
366 |
+
parent_key=new_block_key,
|
367 |
+
is_shadow=True,
|
368 |
+
is_top_level=False
|
369 |
+
)
|
370 |
+
if lt_block_key:
|
371 |
+
new_block_data["inputs"]["CONDITION"][1] = lt_block_key
|
372 |
+
new_block_data["inputs"]["CONDITION"][0] = 2 # Type 2 for boolean block reference
|
373 |
+
|
374 |
+
# Populate operator_lt inputs
|
375 |
+
if op1_parsed[0] == 3: # If it's a reporter block
|
376 |
+
op1_reporter_key, op1_reporter_data = create_block_instance(
|
377 |
+
op1_parsed[1], # Opcode of the reporter
|
378 |
+
opcode_occurrences,
|
379 |
+
parent_key=lt_block_key,
|
380 |
+
is_shadow=True,
|
381 |
+
is_top_level=False
|
382 |
+
)
|
383 |
+
if op1_reporter_key:
|
384 |
+
lt_block_data["inputs"]["OPERAND1"][1] = op1_reporter_key
|
385 |
+
lt_block_data["inputs"]["OPERAND1"][0] = 3
|
386 |
+
else: # Literal value
|
387 |
+
lt_block_data["inputs"]["OPERAND1"][1] = op1_parsed[1]
|
388 |
+
lt_block_data["inputs"]["OPERAND1"][0] = op1_parsed[0]
|
389 |
+
|
390 |
+
lt_block_data["inputs"]["OPERAND2"][1] = op2_parsed[1]
|
391 |
+
lt_block_data["inputs"]["OPERAND2"][0] = op2_parsed[0]
|
392 |
+
|
393 |
+
elif condition_opcode == "sensing_touchingobject":
|
394 |
+
sprite_name = matched_values[0].replace(' v', '').strip()
|
395 |
+
touching_opcode = "sensing_touchingobject"
|
396 |
+
|
397 |
+
touching_block_key, touching_block_data = create_block_instance(
|
398 |
+
touching_opcode,
|
399 |
+
opcode_occurrences,
|
400 |
+
parent_key=new_block_key,
|
401 |
+
is_shadow=True,
|
402 |
+
is_top_level=False
|
403 |
+
)
|
404 |
+
if touching_block_key:
|
405 |
+
new_block_data["inputs"]["CONDITION"][1] = touching_block_key
|
406 |
+
new_block_data["inputs"]["CONDITION"][0] = 2 # Type 2 for boolean block reference
|
407 |
+
|
408 |
+
# Create the menu block for TOUCHINGOBJECTMENU
|
409 |
+
menu_opcode = "sensing_touchingobjectmenu"
|
410 |
+
menu_key, menu_data = create_block_instance(
|
411 |
+
menu_opcode,
|
412 |
+
opcode_occurrences,
|
413 |
+
parent_key=touching_block_key,
|
414 |
+
is_shadow=True,
|
415 |
+
is_top_level=False
|
416 |
+
)
|
417 |
+
if menu_key:
|
418 |
+
touching_block_data["inputs"]["TOUCHINGOBJECTMENU"][1] = menu_key
|
419 |
+
touching_block_data["inputs"]["TOUCHINGOBJECTMENU"][0] = 1 # Type 1 for block reference
|
420 |
+
menu_data["fields"]["TOUCHINGOBJECTMENU"][0] = sprite_name
|
421 |
+
else:
|
422 |
+
print(f"Warning: Could not create touching object block for condition: '{line}'")
|
423 |
+
|
424 |
+
|
425 |
+
elif opcode == "control_forever":
|
426 |
+
# Forever blocks are C-blocks, so push them onto the stack
|
427 |
+
block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key)
|
428 |
+
elif opcode == "control_stop":
|
429 |
+
option = matched_values[0].replace(' v', '').strip()
|
430 |
+
new_block_data["fields"]["STOP_OPTION"][0] = option
|
431 |
+
elif opcode == "event_broadcast":
|
432 |
+
message = matched_values[0].replace(' v', '').strip()
|
433 |
+
# For broadcast, the input is usually a string literal or a variable
|
434 |
+
new_block_data["inputs"]["BROADCAST_INPUT"][0] = 11 # Type 11 for broadcast input (string or variable)
|
435 |
+
new_block_data["inputs"]["BROADCAST_INPUT"][1] = [10, message] # Assume string literal for now
|
436 |
+
# A more robust solution would create a data_variable block if it's a variable.
|
437 |
+
|
438 |
+
# For C-blocks, push onto stack to track nesting
|
439 |
+
if all_block_definitions[opcode].get("block_shape") == "C-Block" and opcode != "control_if": # if is handled above
|
440 |
+
block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key)
|
441 |
+
|
442 |
+
|
443 |
+
return updated_blocks
|
444 |
+
|
445 |
+
# --- Consolidated Block Definitions from all provided JSONs ---
|
446 |
+
all_block_definitions = {
|
447 |
+
# motion_block.json
|
448 |
+
"motion_movesteps": {
|
449 |
+
"opcode": "motion_movesteps", "next": None, "parent": None,
|
450 |
+
"inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
451 |
+
"x": 464, "y": -416
|
452 |
+
},
|
453 |
+
"motion_turnright": {
|
454 |
+
"opcode": "motion_turnright", "next": None, "parent": None,
|
455 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
456 |
+
"x": 467, "y": -316
|
457 |
+
},
|
458 |
+
"motion_turnleft": {
|
459 |
+
"opcode": "motion_turnleft", "next": None, "parent": None,
|
460 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
461 |
+
"x": 464, "y": -210
|
462 |
+
},
|
463 |
+
"motion_goto": {
|
464 |
+
"opcode": "motion_goto", "next": None, "parent": None,
|
465 |
+
"inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
466 |
+
"x": 465, "y": -95
|
467 |
+
},
|
468 |
+
"motion_goto_menu": {
|
469 |
+
"opcode": "motion_goto_menu", "next": None, "parent": "motion_goto",
|
470 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
471 |
+
},
|
472 |
+
"motion_gotoxy": {
|
473 |
+
"opcode": "motion_gotoxy", "next": None, "parent": None,
|
474 |
+
"inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
475 |
+
"x": 468, "y": 12
|
476 |
+
},
|
477 |
+
"motion_glideto": {
|
478 |
+
"opcode": "motion_glideto", "next": None, "parent": None,
|
479 |
+
"inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
480 |
+
"x": 470, "y": 129
|
481 |
+
},
|
482 |
+
"motion_glideto_menu": {
|
483 |
+
"opcode": "motion_glideto_menu", "next": None, "parent": "motion_glideto",
|
484 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
485 |
+
},
|
486 |
+
"motion_glidesecstoxy": {
|
487 |
+
"opcode": "motion_glidesecstoxy", "next": None, "parent": None,
|
488 |
+
"inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
489 |
+
"x": 476, "y": 239
|
490 |
+
},
|
491 |
+
"motion_pointindirection": {
|
492 |
+
"opcode": "motion_pointindirection", "next": None, "parent": None,
|
493 |
+
"inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
494 |
+
"x": 493, "y": 361
|
495 |
+
},
|
496 |
+
"motion_pointtowards": {
|
497 |
+
"opcode": "motion_pointtowards", "next": None, "parent": None,
|
498 |
+
"inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
499 |
+
"x": 492, "y": 463
|
500 |
+
},
|
501 |
+
"motion_pointtowards_menu": {
|
502 |
+
"opcode": "motion_pointtowards_menu", "next": None, "parent": "motion_pointtowards",
|
503 |
+
"inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
504 |
+
},
|
505 |
+
"motion_changexby": {
|
506 |
+
"opcode": "motion_changexby", "next": None, "parent": None,
|
507 |
+
"inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
508 |
+
"x": 851, "y": -409
|
509 |
+
},
|
510 |
+
"motion_setx": {
|
511 |
+
"opcode": "motion_setx", "next": None, "parent": None,
|
512 |
+
"inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
513 |
+
"x": 864, "y": -194
|
514 |
+
},
|
515 |
+
"motion_changeyby": {
|
516 |
+
"opcode": "motion_changeyby", "next": None, "parent": None,
|
517 |
+
"inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
518 |
+
"x": 861, "y": -61
|
519 |
+
},
|
520 |
+
"motion_sety": {
|
521 |
+
"opcode": "motion_sety", "next": None, "parent": None,
|
522 |
+
"inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
523 |
+
"x": 864, "y": 66
|
524 |
+
},
|
525 |
+
"motion_ifonedgebounce": {
|
526 |
+
"opcode": "motion_ifonedgebounce", "next": None, "parent": None,
|
527 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
528 |
+
"x": 1131, "y": -397
|
529 |
+
},
|
530 |
+
"motion_setrotationstyle": {
|
531 |
+
"opcode": "motion_setrotationstyle", "next": None, "parent": None,
|
532 |
+
"inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True,
|
533 |
+
"x": 1128, "y": -287
|
534 |
+
},
|
535 |
+
"motion_xposition": {
|
536 |
+
"opcode": "motion_xposition", "next": None, "parent": None,
|
537 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
538 |
+
"x": 1193, "y": -136
|
539 |
+
},
|
540 |
+
"motion_yposition": {
|
541 |
+
"opcode": "motion_yposition", "next": None, "parent": None,
|
542 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
543 |
+
"x": 1181, "y": -64
|
544 |
+
},
|
545 |
+
"motion_direction": {
|
546 |
+
"opcode": "motion_direction", "next": None, "parent": None,
|
547 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
548 |
+
"x": 1188, "y": 21
|
549 |
+
},
|
550 |
+
|
551 |
+
# control_block.json
|
552 |
+
"control_wait": {
|
553 |
+
"opcode": "control_wait", "next": None, "parent": None,
|
554 |
+
"inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
555 |
+
"x": 337, "y": 129
|
556 |
+
},
|
557 |
+
"control_repeat": {
|
558 |
+
"opcode": "control_repeat", "next": None, "parent": None,
|
559 |
+
"inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
560 |
+
"x": 348, "y": 265
|
561 |
+
},
|
562 |
+
"control_forever": {
|
563 |
+
"opcode": "control_forever", "next": None, "parent": None,
|
564 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
565 |
+
"x": 334, "y": 439
|
566 |
+
},
|
567 |
+
"control_if": {
|
568 |
+
"opcode": "control_if", "next": None, "parent": None,
|
569 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
570 |
+
"x": 331, "y": 597
|
571 |
+
},
|
572 |
+
"control_if_else": {
|
573 |
+
"opcode": "control_if_else", "next": None, "parent": None,
|
574 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
575 |
+
"x": 335, "y": 779
|
576 |
+
},
|
577 |
+
"control_wait_until": {
|
578 |
+
"opcode": "control_wait_until", "next": None, "parent": None,
|
579 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
580 |
+
"x": 676, "y": 285
|
581 |
+
},
|
582 |
+
"control_repeat_until": {
|
583 |
+
"opcode": "control_repeat_until", "next": None, "parent": None,
|
584 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
585 |
+
"x": 692, "y": 381
|
586 |
+
},
|
587 |
+
"control_stop": {
|
588 |
+
"opcode": "control_stop", "next": None, "parent": None,
|
589 |
+
"inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True,
|
590 |
+
"x": 708, "y": 545, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"}
|
591 |
+
},
|
592 |
+
"control_start_as_clone": {
|
593 |
+
"opcode": "control_start_as_clone", "next": None, "parent": None,
|
594 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
595 |
+
"x": 665, "y": 672
|
596 |
+
},
|
597 |
+
"control_create_clone_of": {
|
598 |
+
"opcode": "control_create_clone_of", "next": None, "parent": None,
|
599 |
+
"inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
600 |
+
"x": 648, "y": 797
|
601 |
+
},
|
602 |
+
"control_create_clone_of_menu": {
|
603 |
+
"opcode": "control_create_clone_of_menu", "next": None, "parent": "control_create_clone_of",
|
604 |
+
"inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False
|
605 |
+
},
|
606 |
+
"control_delete_this_clone": {
|
607 |
+
"opcode": "control_delete_this_clone", "next": None, "parent": None,
|
608 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
609 |
+
"x": 642, "y": 914
|
610 |
+
},
|
611 |
+
|
612 |
+
# data_block.json
|
613 |
+
"data_setvariableto": {
|
614 |
+
"opcode": "data_setvariableto", "next": None, "parent": None,
|
615 |
+
"inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
616 |
+
"x": 348, "y": 241
|
617 |
+
},
|
618 |
+
"data_changevariableby": {
|
619 |
+
"opcode": "data_changevariableby", "next": None, "parent": None,
|
620 |
+
"inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
621 |
+
"x": 313, "y": 363
|
622 |
+
},
|
623 |
+
"data_showvariable": {
|
624 |
+
"opcode": "data_showvariable", "next": None, "parent": None,
|
625 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
626 |
+
"x": 415, "y": 473
|
627 |
+
},
|
628 |
+
"data_hidevariable": {
|
629 |
+
"opcode": "data_hidevariable", "next": None, "parent": None,
|
630 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
631 |
+
"x": 319, "y": 587
|
632 |
+
},
|
633 |
+
"data_addtolist": {
|
634 |
+
"opcode": "data_addtolist", "next": None, "parent": None,
|
635 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
636 |
+
"x": 385, "y": 109
|
637 |
+
},
|
638 |
+
"data_deleteoflist": {
|
639 |
+
"opcode": "data_deleteoflist", "next": None, "parent": None,
|
640 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
641 |
+
"x": 384, "y": 244
|
642 |
+
},
|
643 |
+
"data_deletealloflist": {
|
644 |
+
"opcode": "data_deletealloflist", "next": None, "parent": None,
|
645 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
646 |
+
"x": 387, "y": 374
|
647 |
+
},
|
648 |
+
"data_insertatlist": {
|
649 |
+
"opcode": "data_insertatlist", "next": None, "parent": None,
|
650 |
+
"inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
651 |
+
"x": 366, "y": 527
|
652 |
+
},
|
653 |
+
"data_replaceitemoflist": {
|
654 |
+
"opcode": "data_replaceitemoflist", "next": None, "parent": None,
|
655 |
+
"inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
656 |
+
"x": 365, "y": 657
|
657 |
+
},
|
658 |
+
"data_itemoflist": {
|
659 |
+
"opcode": "data_itemoflist", "next": None, "parent": None,
|
660 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
661 |
+
"x": 862, "y": 117
|
662 |
+
},
|
663 |
+
"data_itemnumoflist": {
|
664 |
+
"opcode": "data_itemnumoflist", "next": None, "parent": None,
|
665 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
666 |
+
"x": 883, "y": 238
|
667 |
+
},
|
668 |
+
"data_lengthoflist": {
|
669 |
+
"opcode": "data_lengthoflist", "next": None, "parent": None,
|
670 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
671 |
+
"x": 876, "y": 342
|
672 |
+
},
|
673 |
+
"data_listcontainsitem": {
|
674 |
+
"opcode": "data_listcontainsitem", "next": None, "parent": None,
|
675 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
676 |
+
"x": 871, "y": 463
|
677 |
+
},
|
678 |
+
"data_showlist": {
|
679 |
+
"opcode": "data_showlist", "next": None, "parent": None,
|
680 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
681 |
+
"x": 931, "y": 563
|
682 |
+
},
|
683 |
+
"data_hidelist": {
|
684 |
+
"opcode": "data_hidelist", "next": None, "parent": None,
|
685 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
686 |
+
"x": 962, "y": 716
|
687 |
+
},
|
688 |
+
|
689 |
+
# event_block.json
|
690 |
+
"event_whenflagclicked": {
|
691 |
+
"opcode": "event_whenflagclicked", "next": None, "parent": None,
|
692 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
693 |
+
"x": 166, "y": -422
|
694 |
+
},
|
695 |
+
"event_whenkeypressed": {
|
696 |
+
"opcode": "event_whenkeypressed", "next": None, "parent": None,
|
697 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True,
|
698 |
+
"x": 151, "y": -329
|
699 |
+
},
|
700 |
+
"event_whenthisspriteclicked": {
|
701 |
+
"opcode": "event_whenthisspriteclicked", "next": None, "parent": None,
|
702 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
703 |
+
"x": 156, "y": -223
|
704 |
+
},
|
705 |
+
"event_whenbackdropswitchesto": {
|
706 |
+
"opcode": "event_whenbackdropswitchesto", "next": None, "parent": None,
|
707 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True,
|
708 |
+
"x": 148, "y": -101
|
709 |
+
},
|
710 |
+
"event_whengreaterthan": {
|
711 |
+
"opcode": "event_whengreaterthan", "next": None, "parent": None,
|
712 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True,
|
713 |
+
"x": 150, "y": 10
|
714 |
+
},
|
715 |
+
"event_whenbroadcastreceived": {
|
716 |
+
"opcode": "event_whenbroadcastreceived", "next": None, "parent": None,
|
717 |
+
"inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True,
|
718 |
+
"x": 141, "y": 118
|
719 |
+
},
|
720 |
+
"event_broadcast": {
|
721 |
+
"opcode": "event_broadcast", "next": None, "parent": None,
|
722 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
723 |
+
"x": 151, "y": 229
|
724 |
+
},
|
725 |
+
"event_broadcastandwait": {
|
726 |
+
"opcode": "event_broadcastandwait", "next": None, "parent": None,
|
727 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
728 |
+
"x": 157, "y": 340
|
729 |
+
},
|
730 |
+
|
731 |
+
# look_block.json
|
732 |
+
"looks_sayforsecs": {
|
733 |
+
"opcode": "looks_sayforsecs", "next": None, "parent": None,
|
734 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
735 |
+
"x": 408, "y": 91
|
736 |
+
},
|
737 |
+
"looks_say": {
|
738 |
+
"opcode": "looks_say", "next": None, "parent": None,
|
739 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
740 |
+
"x": 413, "y": 213
|
741 |
+
},
|
742 |
+
"looks_thinkforsecs": {
|
743 |
+
"opcode": "looks_thinkforsecs", "next": None, "parent": None,
|
744 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
745 |
+
"x": 413, "y": 317
|
746 |
+
},
|
747 |
+
"looks_think": {
|
748 |
+
"opcode": "looks_think", "next": None, "parent": None,
|
749 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True,
|
750 |
+
"x": 412, "y": 432
|
751 |
+
},
|
752 |
+
"looks_switchcostumeto": {
|
753 |
+
"opcode": "looks_switchcostumeto", "next": None, "parent": None,
|
754 |
+
"inputs": {"COSTUME": [1, "8;bti4wv(iH9nkOacCJ|"]}, "fields": {}, "shadow": False, "topLevel": True,
|
755 |
+
"x": 411, "y": 555
|
756 |
+
},
|
757 |
+
"looks_costume": {
|
758 |
+
"opcode": "looks_costume", "next": None, "parent": "Q#a,6LPWHqo9-0Nu*[SV",
|
759 |
+
"inputs": {}, "fields": {"COSTUME": ["costume2", None]}, "shadow": True, "topLevel": False
|
760 |
+
},
|
761 |
+
"looks_nextcostume": {
|
762 |
+
"opcode": "looks_nextcostume", "next": None, "parent": None,
|
763 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
764 |
+
"x": 419, "y": 687
|
765 |
+
},
|
766 |
+
"looks_switchbackdropto": {
|
767 |
+
"opcode": "looks_switchbackdropto", "next": None, "parent": None,
|
768 |
+
"inputs": {"BACKDROP": [1, "-?yeX}29V*wd6W:unW0i"]}, "fields": {}, "shadow": False, "topLevel": True,
|
769 |
+
"x": 901, "y": 91
|
770 |
+
},
|
771 |
+
"looks_backdrops": {
|
772 |
+
"opcode": "looks_backdrops", "next": None, "parent": "`Wm^p~l[(IWzc1|wNv*.",
|
773 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False
|
774 |
+
},
|
775 |
+
"looks_changesizeby": {
|
776 |
+
"opcode": "looks_changesizeby", "next": None, "parent": None,
|
777 |
+
"inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
778 |
+
"x": 895, "y": 192
|
779 |
+
},
|
780 |
+
"looks_setsizeto": {
|
781 |
+
"opcode": "looks_setsizeto", "next": None, "parent": None,
|
782 |
+
"inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
783 |
+
"x": 896, "y": 303
|
784 |
+
},
|
785 |
+
"looks_changeeffectby": {
|
786 |
+
"opcode": "looks_changeeffectby", "next": None, "parent": None,
|
787 |
+
"inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True,
|
788 |
+
"x": 892, "y": 416
|
789 |
+
},
|
790 |
+
"looks_seteffectto": {
|
791 |
+
"opcode": "looks_seteffectto", "next": None, "parent": None,
|
792 |
+
"inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True,
|
793 |
+
"x": 902, "y": 527
|
794 |
+
},
|
795 |
+
"looks_cleargraphiceffects": {
|
796 |
+
"opcode": "looks_cleargraphiceffects", "next": None, "parent": None,
|
797 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
798 |
+
"x": 902, "y": 638
|
799 |
+
},
|
800 |
+
"looks_show": {
|
801 |
+
"opcode": "looks_show", "next": None, "parent": None,
|
802 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
803 |
+
"x": 908, "y": 758
|
804 |
+
},
|
805 |
+
"looks_hide": {
|
806 |
+
"opcode": "looks_hide", "next": None, "parent": None,
|
807 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
808 |
+
"x": 455, "y": 861
|
809 |
+
},
|
810 |
+
"looks_gotofrontback": {
|
811 |
+
"opcode": "looks_gotofrontback", "next": None, "parent": None,
|
812 |
+
"inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True,
|
813 |
+
"x": 853, "y": 878
|
814 |
+
},
|
815 |
+
"looks_goforwardbackwardlayers": {
|
816 |
+
"opcode": "looks_goforwardbackwardlayers", "next": None, "parent": None,
|
817 |
+
"inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True,
|
818 |
+
"x": 851, "y": 999
|
819 |
+
},
|
820 |
+
"looks_costumenumbername": {
|
821 |
+
"opcode": "looks_costumenumbername", "next": None, "parent": None,
|
822 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True,
|
823 |
+
"x": 458, "y": 1007
|
824 |
+
},
|
825 |
+
"looks_backdropnumbername": {
|
826 |
+
"opcode": "looks_backdropnumbername", "next": None, "parent": None,
|
827 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True,
|
828 |
+
"x": 1242, "y": 753
|
829 |
+
},
|
830 |
+
"looks_size": {
|
831 |
+
"opcode": "looks_size", "next": None, "parent": None,
|
832 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
833 |
+
"x": 1249, "y": 876
|
834 |
+
},
|
835 |
+
|
836 |
+
# operator_block.json
|
837 |
+
"operator_add": {
|
838 |
+
"opcode": "operator_add", "next": None, "parent": None,
|
839 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
840 |
+
"x": 128, "y": 153
|
841 |
+
},
|
842 |
+
"operator_subtract": {
|
843 |
+
"opcode": "operator_subtract", "next": None, "parent": None,
|
844 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
845 |
+
"x": 134, "y": 214
|
846 |
+
},
|
847 |
+
"operator_multiply": {
|
848 |
+
"opcode": "operator_multiply", "next": None, "parent": None,
|
849 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
850 |
+
"x": 134, "y": 278
|
851 |
+
},
|
852 |
+
"operator_divide": {
|
853 |
+
"opcode": "operator_divide", "next": None, "parent": None,
|
854 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
855 |
+
"x": 138, "y": 359
|
856 |
+
},
|
857 |
+
"operator_random": {
|
858 |
+
"opcode": "operator_random", "next": None, "parent": None,
|
859 |
+
"inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
860 |
+
"x": 311, "y": 157
|
861 |
+
},
|
862 |
+
"operator_gt": {
|
863 |
+
"opcode": "operator_gt", "next": None, "parent": None,
|
864 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
865 |
+
"x": 348, "y": 217
|
866 |
+
},
|
867 |
+
"operator_lt": {
|
868 |
+
"opcode": "operator_lt", "next": None, "parent": None,
|
869 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
870 |
+
"x": 345, "y": 286
|
871 |
+
},
|
872 |
+
"operator_equals": {
|
873 |
+
"opcode": "operator_equals", "next": None, "parent": None,
|
874 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
875 |
+
"x": 345, "y": 372
|
876 |
+
},
|
877 |
+
"operator_and": {
|
878 |
+
"opcode": "operator_and", "next": None, "parent": None,
|
879 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
880 |
+
"x": 701, "y": 158
|
881 |
+
},
|
882 |
+
"operator_or": {
|
883 |
+
"opcode": "operator_or", "next": None, "parent": None,
|
884 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
885 |
+
"x": 705, "y": 222
|
886 |
+
},
|
887 |
+
"operator_not": {
|
888 |
+
"opcode": "operator_not", "next": None, "parent": None,
|
889 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
890 |
+
"x": 734, "y": 283
|
891 |
+
},
|
892 |
+
"operator_join": {
|
893 |
+
"opcode": "operator_join", "next": None, "parent": None,
|
894 |
+
"inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
895 |
+
"x": 663, "y": 378
|
896 |
+
},
|
897 |
+
"operator_letter_of": {
|
898 |
+
"opcode": "operator_letter_of", "next": None, "parent": None,
|
899 |
+
"inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
900 |
+
"x": 664, "y": 445
|
901 |
+
},
|
902 |
+
"operator_length": {
|
903 |
+
"opcode": "operator_length", "next": None, "parent": None,
|
904 |
+
"inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
905 |
+
"x": 664, "y": 521
|
906 |
+
},
|
907 |
+
"operator_contains": {
|
908 |
+
"opcode": "operator_contains", "next": None, "parent": None,
|
909 |
+
"inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
910 |
+
"x": 634, "y": 599
|
911 |
+
},
|
912 |
+
"operator_mod": {
|
913 |
+
"opcode": "operator_mod", "next": None, "parent": None,
|
914 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
915 |
+
"x": 295, "y": 594
|
916 |
+
},
|
917 |
+
"operator_round": {
|
918 |
+
"opcode": "operator_round", "next": None, "parent": None,
|
919 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
920 |
+
"x": 307, "y": 674
|
921 |
+
},
|
922 |
+
"operator_mathop": {
|
923 |
+
"opcode": "operator_mathop", "next": None, "parent": None,
|
924 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True,
|
925 |
+
"x": 280, "y": 754
|
926 |
+
},
|
927 |
+
|
928 |
+
# sensing_block.json
|
929 |
+
"sensing_touchingobject": {
|
930 |
+
"opcode": "sensing_touchingobject", "next": None, "parent": None,
|
931 |
+
"inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
932 |
+
"x": 359, "y": 116
|
933 |
+
},
|
934 |
+
"sensing_touchingobjectmenu": {
|
935 |
+
"opcode": "sensing_touchingobjectmenu", "next": None, "parent": "sensing_touchingobject",
|
936 |
+
"inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
937 |
+
},
|
938 |
+
"sensing_touchingcolor": {
|
939 |
+
"opcode": "sensing_touchingcolor", "next": None, "parent": None,
|
940 |
+
"inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
941 |
+
"x": 360, "y": 188
|
942 |
+
},
|
943 |
+
"sensing_coloristouchingcolor": {
|
944 |
+
"opcode": "sensing_coloristouchingcolor", "next": None, "parent": None,
|
945 |
+
"inputs": {"COLOR": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
946 |
+
"x": 348, "y": 277
|
947 |
+
},
|
948 |
+
"sensing_askandwait": {
|
949 |
+
"opcode": "sensing_askandwait", "next": None, "parent": None,
|
950 |
+
"inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
951 |
+
"x": 338, "y": 354
|
952 |
+
},
|
953 |
+
"sensing_answer": {
|
954 |
+
"opcode": "sensing_answer", "next": None, "parent": None,
|
955 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
956 |
+
"x": 782, "y": 111
|
957 |
+
},
|
958 |
+
"sensing_keypressed": {
|
959 |
+
"opcode": "sensing_keypressed", "next": None, "parent": None,
|
960 |
+
"inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True,
|
961 |
+
"x": 762, "y": 207
|
962 |
+
},
|
963 |
+
"sensing_keyoptions": {
|
964 |
+
"opcode": "sensing_keyoptions", "next": None, "parent": "sensing_keypressed",
|
965 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False
|
966 |
+
},
|
967 |
+
"sensing_mousedown": {
|
968 |
+
"opcode": "sensing_mousedown", "next": None, "parent": None,
|
969 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
970 |
+
"x": 822, "y": 422
|
971 |
+
},
|
972 |
+
"sensing_mousex": {
|
973 |
+
"opcode": "sensing_mousex", "next": None, "parent": None,
|
974 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
975 |
+
"x": 302, "y": 528
|
976 |
+
},
|
977 |
+
"sensing_mousey": {
|
978 |
+
"opcode": "sensing_mousey", "next": None, "parent": None,
|
979 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
980 |
+
"x": 668, "y": 547
|
981 |
+
},
|
982 |
+
"sensing_setdragmode": {
|
983 |
+
"opcode": "sensing_setdragmode", "next": None, "parent": None,
|
984 |
+
"inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True,
|
985 |
+
"x": 950, "y": 574
|
986 |
+
},
|
987 |
+
"sensing_loudness": {
|
988 |
+
"opcode": "sensing_loudness", "next": None, "parent": None,
|
989 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
990 |
+
"x": 658, "y": 703
|
991 |
+
},
|
992 |
+
"sensing_timer": {
|
993 |
+
"opcode": "sensing_timer", "next": None, "parent": None,
|
994 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
995 |
+
"x": 459, "y": 671
|
996 |
+
},
|
997 |
+
"sensing_resettimer": {
|
998 |
+
"opcode": "sensing_resettimer", "next": None, "parent": None,
|
999 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1000 |
+
"x": 462, "y": 781
|
1001 |
+
},
|
1002 |
+
"sensing_of": {
|
1003 |
+
"opcode": "sensing_of", "next": None, "parent": None,
|
1004 |
+
"inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True,
|
1005 |
+
"x": 997, "y": 754
|
1006 |
+
},
|
1007 |
+
"sensing_of_object_menu": {
|
1008 |
+
"opcode": "sensing_of_object_menu", "next": None, "parent": "sensing_of",
|
1009 |
+
"inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False
|
1010 |
+
},
|
1011 |
+
"sensing_current": {
|
1012 |
+
"opcode": "sensing_current", "next": None, "parent": None,
|
1013 |
+
"inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True,
|
1014 |
+
"x": 627, "y": 884
|
1015 |
+
},
|
1016 |
+
"sensing_dayssince2000": {
|
1017 |
+
"opcode": "sensing_dayssince2000", "next": None, "parent": None,
|
1018 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1019 |
+
"x": 959, "y": 903
|
1020 |
+
},
|
1021 |
+
"sensing_username": {
|
1022 |
+
"opcode": "sensing_username", "next": None, "parent": None,
|
1023 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1024 |
+
"x": 833, "y": 757
|
1025 |
+
},
|
1026 |
+
|
1027 |
+
# sound_block.json
|
1028 |
+
"sound_playuntildone": {
|
1029 |
+
"opcode": "sound_playuntildone", "next": None, "parent": None,
|
1030 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
1031 |
+
"x": 253, "y": 17
|
1032 |
+
},
|
1033 |
+
"sound_sounds_menu": {
|
1034 |
+
"opcode": "sound_sounds_menu", "next": None, "parent": "sound_playuntildone and sound_play",
|
1035 |
+
"inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False
|
1036 |
+
},
|
1037 |
+
"sound_play": {
|
1038 |
+
"opcode": "sound_play", "next": None, "parent": None,
|
1039 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
1040 |
+
"x": 245, "y": 122
|
1041 |
+
},
|
1042 |
+
"sound_stopallsounds": {
|
1043 |
+
"opcode": "sound_stopallsounds", "next": None, "parent": None,
|
1044 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1045 |
+
"x": 253, "y": 245
|
1046 |
+
},
|
1047 |
+
"sound_changeeffectby": {
|
1048 |
+
"opcode": "sound_changeeffectby", "next": None, "parent": None,
|
1049 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True,
|
1050 |
+
"x": 653, "y": 14
|
1051 |
+
},
|
1052 |
+
"sound_seteffectto": {
|
1053 |
+
"opcode": "sound_seteffectto", "next": None, "parent": None,
|
1054 |
+
"inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True,
|
1055 |
+
"x": 653, "y": 139
|
1056 |
+
},
|
1057 |
+
"sound_cleareffects": {
|
1058 |
+
"opcode": "sound_cleareffects", "next": None, "parent": None,
|
1059 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1060 |
+
"x": 651, "y": 242
|
1061 |
+
},
|
1062 |
+
"sound_changevolumeby": {
|
1063 |
+
"opcode": "sound_changevolumeby", "next": None, "parent": None,
|
1064 |
+
"inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
1065 |
+
"x": 645, "y": 353
|
1066 |
+
},
|
1067 |
+
"sound_setvolumeto": {
|
1068 |
+
"opcode": "sound_setvolumeto", "next": None, "parent": None,
|
1069 |
+
"inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
1070 |
+
"x": 1108, "y": 5
|
1071 |
+
},
|
1072 |
+
"sound_volume": {
|
1073 |
+
"opcode": "sound_volume", "next": None, "parent": None,
|
1074 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
1075 |
+
"x": 1136, "y": 123
|
1076 |
+
},
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
# #Example input with opcodes from various categories
|
1080 |
+
# input_opcodes = [
|
1081 |
+
# {"opcode": "sound_play", "count": 2}, # New: Sound block with menu
|
1082 |
+
# {"opcode": "sound_playuntildone", "count": 2}, # New: Sound block with menu
|
1083 |
+
# ]
|
1084 |
+
|
1085 |
+
# Example input with opcodes from various categories
|
1086 |
+
# input_opcodes = [
|
1087 |
+
# {"opcode": "sound_play", "count": 2},
|
1088 |
+
# {"opcode": "sound_playuntildone", "count": 2},
|
1089 |
+
# {"opcode":"motion_goto","count":2},
|
1090 |
+
# {"opcode":"motion_glideto","count":2},
|
1091 |
+
# {"opcode":"looks_switchbackdropto","count":2},
|
1092 |
+
# {"opcode":"looks_switchcostumeto","count":2},
|
1093 |
+
# {"opcode":"control_create_clone_of","count":2},
|
1094 |
+
# {"opcode":"sensing_touchingobject","count":2},
|
1095 |
+
# {"opcode":"sensing_of","count":2},
|
1096 |
+
# {"opcode":"sensing_keypressed","count":2},
|
1097 |
+
# {"opcode":"motion_pointtowards","count":2},
|
1098 |
+
# ]
|
1099 |
+
|
1100 |
+
# generated_output = generate_blocks_from_opcodes(input_opcodes, all_block_definitions)
|
1101 |
+
# print(json.dumps(generated_output, indent=2))
|
1102 |
+
|
1103 |
+
initial_opcode_counts = [
|
1104 |
+
{"opcode":"event_whenflagclicked","count":1},
|
1105 |
+
{"opcode":"motion_gotoxy","count":1},
|
1106 |
+
{"opcode":"motion_glidesecstoxy","count":1},
|
1107 |
+
{"opcode":"motion_xposition","count":1},
|
1108 |
+
{"opcode":"motion_setx","count":1},
|
1109 |
+
{"opcode":"control_forever","count":1},
|
1110 |
+
{"opcode":"control_if","count":1},
|
1111 |
+
{"opcode":"control_stop","count":1},
|
1112 |
+
{"opcode":"operator_lt","count":1},
|
1113 |
+
{"opcode":"sensing_istouching","count":1},
|
1114 |
+
{"opcode":"sensing_touchingobjectmenu","count":1}, # This will now be generated as a child of sensing_touchingobject
|
1115 |
+
{"opcode":"event_broadcast","count":1},
|
1116 |
+
{"opcode":"data_setvariableto","count":2},
|
1117 |
+
{"opcode":"data_showvariable","count":2},
|
1118 |
+
]
|
1119 |
+
|
1120 |
+
# Generate the initial blocks and get the opcode_occurrences
|
1121 |
+
generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions)
|
1122 |
+
|
1123 |
+
# Pseudo-code to interpret
|
1124 |
+
pseudo_code_input = """
|
1125 |
+
when green flag clicked
|
1126 |
+
go to x: (240) y: (-135)
|
1127 |
+
set [score v] to +1
|
1128 |
+
set [speed v] to +1
|
1129 |
+
show variable [score v]
|
1130 |
+
show variable [speed v]
|
1131 |
+
forever
|
1132 |
+
glide (2) seconds to x: (-240) y: (-135)
|
1133 |
+
if <((x position)) < (-235)> then
|
1134 |
+
set x to (240)
|
1135 |
+
end
|
1136 |
+
if <touching [Sprite1 v]?> then
|
1137 |
+
broadcast [Game Over v]
|
1138 |
+
stop [all v]
|
1139 |
+
end
|
1140 |
+
end
|
1141 |
+
end
|
1142 |
+
"""
|
1143 |
+
|
1144 |
+
# Interpret the pseudo-code and update the blocks, passing opcode_occurrences
|
1145 |
+
final_generated_blocks = interpret_pseudo_code_and_update_blocks(generated_output_json, pseudo_code_input, all_block_definitions, initial_opcode_occurrences)
|
1146 |
+
|
1147 |
+
print(json.dumps(final_generated_blocks, indent=2))
|
v2/utils/block_function_v1.py
ADDED
@@ -0,0 +1,759 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import copy
|
3 |
+
|
4 |
+
import json
|
5 |
+
import copy
|
6 |
+
|
7 |
+
def generate_blocks_from_opcodes(opcode_counts, all_block_definitions):
|
8 |
+
"""
|
9 |
+
Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition.
|
10 |
+
It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories.
|
11 |
+
It ensures that menu blocks are only generated as children of their respective parent blocks.
|
12 |
+
|
13 |
+
Args:
|
14 |
+
opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property.
|
15 |
+
Example: [{"opcode": "motion_gotoxy", "count": 1}]
|
16 |
+
all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
dict: A JSON object where keys are generated block IDs and values are the block definitions.
|
20 |
+
"""
|
21 |
+
generated_blocks = {}
|
22 |
+
opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys
|
23 |
+
|
24 |
+
# Define explicit parent-menu relationships for linking purposes
|
25 |
+
# This maps main_opcode -> list of (input_field_name, menu_opcode)
|
26 |
+
explicit_menu_links = {
|
27 |
+
"motion_goto": [("TO", "motion_goto_menu")],
|
28 |
+
"motion_glideto": [("TO", "motion_glideto_menu")],
|
29 |
+
"motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
|
30 |
+
"sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
|
31 |
+
"sensing_of": [("OBJECT", "sensing_of_object_menu")],
|
32 |
+
"sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
|
33 |
+
"control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
|
34 |
+
"sound_play": [("SOUND_MENU", "sound_sounds_menu")],
|
35 |
+
"sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
|
36 |
+
"looks_switchcostumeto": [("COSTUME", "looks_costume")],
|
37 |
+
"looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
|
38 |
+
}
|
39 |
+
|
40 |
+
# --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus ---
|
41 |
+
for item in opcode_counts:
|
42 |
+
opcode = item.get("opcode")
|
43 |
+
count = item.get("count", 1)
|
44 |
+
|
45 |
+
if not opcode:
|
46 |
+
print("Warning: Skipping item with missing 'opcode'.")
|
47 |
+
continue
|
48 |
+
|
49 |
+
if opcode not in all_block_definitions:
|
50 |
+
print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.")
|
51 |
+
continue
|
52 |
+
|
53 |
+
for _ in range(count):
|
54 |
+
# Increment occurrence count for the current main opcode
|
55 |
+
opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1
|
56 |
+
main_block_instance_num = opcode_occurrences[opcode]
|
57 |
+
|
58 |
+
main_block_unique_key = f"{opcode}_{main_block_instance_num}"
|
59 |
+
|
60 |
+
# Create a deep copy of the main block definition
|
61 |
+
main_block_data = copy.deepcopy(all_block_definitions[opcode])
|
62 |
+
|
63 |
+
# Set properties for a top-level main block
|
64 |
+
main_block_data["parent"] = None
|
65 |
+
main_block_data["next"] = None
|
66 |
+
main_block_data["topLevel"] = True
|
67 |
+
main_block_data["shadow"] = False # Main blocks are typically not shadows
|
68 |
+
|
69 |
+
generated_blocks[main_block_unique_key] = main_block_data
|
70 |
+
|
71 |
+
# If this main block has associated menus, generate and link them now
|
72 |
+
if opcode in explicit_menu_links:
|
73 |
+
for input_field_name, menu_opcode_type in explicit_menu_links[opcode]:
|
74 |
+
if menu_opcode_type in all_block_definitions:
|
75 |
+
# Increment the occurrence for the menu block type
|
76 |
+
opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1
|
77 |
+
menu_block_instance_num = opcode_occurrences[menu_opcode_type]
|
78 |
+
|
79 |
+
menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type])
|
80 |
+
|
81 |
+
# Generate a unique key for this specific menu instance
|
82 |
+
menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}"
|
83 |
+
|
84 |
+
# Set properties for a shadow menu block
|
85 |
+
menu_block_data["shadow"] = True
|
86 |
+
menu_block_data["topLevel"] = False
|
87 |
+
menu_block_data["next"] = None
|
88 |
+
menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance
|
89 |
+
|
90 |
+
# Update the main block's input to point to this unique menu instance
|
91 |
+
if input_field_name in main_block_data.get("inputs", {}) and \
|
92 |
+
isinstance(main_block_data["inputs"][input_field_name], list) and \
|
93 |
+
len(main_block_data["inputs"][input_field_name]) > 1 and \
|
94 |
+
main_block_data["inputs"][input_field_name][0] == 1:
|
95 |
+
|
96 |
+
main_block_data["inputs"][input_field_name][1] = menu_unique_key
|
97 |
+
|
98 |
+
generated_blocks[menu_unique_key] = menu_block_data
|
99 |
+
|
100 |
+
return generated_blocks
|
101 |
+
|
102 |
+
|
103 |
+
# --- Consolidated Block Definitions from all provided JSONs ---
|
104 |
+
all_block_definitions = {
|
105 |
+
# motion_block.json
|
106 |
+
"motion_movesteps": {
|
107 |
+
"opcode": "motion_movesteps", "next": None, "parent": None,
|
108 |
+
"inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
109 |
+
"x": 464, "y": -416
|
110 |
+
},
|
111 |
+
"motion_turnright": {
|
112 |
+
"opcode": "motion_turnright", "next": None, "parent": None,
|
113 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
114 |
+
"x": 467, "y": -316
|
115 |
+
},
|
116 |
+
"motion_turnleft": {
|
117 |
+
"opcode": "motion_turnleft", "next": None, "parent": None,
|
118 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
119 |
+
"x": 464, "y": -210
|
120 |
+
},
|
121 |
+
"motion_goto": {
|
122 |
+
"opcode": "motion_goto", "next": None, "parent": None,
|
123 |
+
"inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
124 |
+
"x": 465, "y": -95
|
125 |
+
},
|
126 |
+
"motion_goto_menu": {
|
127 |
+
"opcode": "motion_goto_menu", "next": None, "parent": "motion_goto",
|
128 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
129 |
+
},
|
130 |
+
"motion_gotoxy": {
|
131 |
+
"opcode": "motion_gotoxy", "next": None, "parent": None,
|
132 |
+
"inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
133 |
+
"x": 468, "y": 12
|
134 |
+
},
|
135 |
+
"motion_glideto": {
|
136 |
+
"opcode": "motion_glideto", "next": None, "parent": None,
|
137 |
+
"inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
138 |
+
"x": 470, "y": 129
|
139 |
+
},
|
140 |
+
"motion_glideto_menu": {
|
141 |
+
"opcode": "motion_glideto_menu", "next": None, "parent": "motion_glideto",
|
142 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
143 |
+
},
|
144 |
+
"motion_glidesecstoxy": {
|
145 |
+
"opcode": "motion_glidesecstoxy", "next": None, "parent": None,
|
146 |
+
"inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
147 |
+
"x": 476, "y": 239
|
148 |
+
},
|
149 |
+
"motion_pointindirection": {
|
150 |
+
"opcode": "motion_pointindirection", "next": None, "parent": None,
|
151 |
+
"inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
152 |
+
"x": 493, "y": 361
|
153 |
+
},
|
154 |
+
"motion_pointtowards": {
|
155 |
+
"opcode": "motion_pointtowards", "next": None, "parent": None,
|
156 |
+
"inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
157 |
+
"x": 492, "y": 463
|
158 |
+
},
|
159 |
+
"motion_pointtowards_menu": {
|
160 |
+
"opcode": "motion_pointtowards_menu", "next": None, "parent": "motion_pointtowards",
|
161 |
+
"inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
162 |
+
},
|
163 |
+
"motion_changexby": {
|
164 |
+
"opcode": "motion_changexby", "next": None, "parent": None,
|
165 |
+
"inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
166 |
+
"x": 851, "y": -409
|
167 |
+
},
|
168 |
+
"motion_setx": {
|
169 |
+
"opcode": "motion_setx", "next": None, "parent": None,
|
170 |
+
"inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
171 |
+
"x": 864, "y": -194
|
172 |
+
},
|
173 |
+
"motion_changeyby": {
|
174 |
+
"opcode": "motion_changeyby", "next": None, "parent": None,
|
175 |
+
"inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
176 |
+
"x": 861, "y": -61
|
177 |
+
},
|
178 |
+
"motion_sety": {
|
179 |
+
"opcode": "motion_sety", "next": None, "parent": None,
|
180 |
+
"inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
181 |
+
"x": 864, "y": 66
|
182 |
+
},
|
183 |
+
"motion_ifonedgebounce": {
|
184 |
+
"opcode": "motion_ifonedgebounce", "next": None, "parent": None,
|
185 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
186 |
+
"x": 1131, "y": -397
|
187 |
+
},
|
188 |
+
"motion_setrotationstyle": {
|
189 |
+
"opcode": "motion_setrotationstyle", "next": None, "parent": None,
|
190 |
+
"inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True,
|
191 |
+
"x": 1128, "y": -287
|
192 |
+
},
|
193 |
+
"motion_xposition": {
|
194 |
+
"opcode": "motion_xposition", "next": None, "parent": None,
|
195 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
196 |
+
"x": 1193, "y": -136
|
197 |
+
},
|
198 |
+
"motion_yposition": {
|
199 |
+
"opcode": "motion_yposition", "next": None, "parent": None,
|
200 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
201 |
+
"x": 1181, "y": -64
|
202 |
+
},
|
203 |
+
"motion_direction": {
|
204 |
+
"opcode": "motion_direction", "next": None, "parent": None,
|
205 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
206 |
+
"x": 1188, "y": 21
|
207 |
+
},
|
208 |
+
|
209 |
+
# control_block.json
|
210 |
+
"control_wait": {
|
211 |
+
"opcode": "control_wait", "next": None, "parent": None,
|
212 |
+
"inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
213 |
+
"x": 337, "y": 129
|
214 |
+
},
|
215 |
+
"control_repeat": {
|
216 |
+
"opcode": "control_repeat", "next": None, "parent": None,
|
217 |
+
"inputs": {"TIMES": [1, [6, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
218 |
+
"x": 348, "y": 265
|
219 |
+
},
|
220 |
+
"control_forever": {
|
221 |
+
"opcode": "control_forever", "next": None, "parent": None,
|
222 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
223 |
+
"x": 334, "y": 439
|
224 |
+
},
|
225 |
+
"control_if": {
|
226 |
+
"opcode": "control_if", "next": None, "parent": None,
|
227 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
228 |
+
"x": 331, "y": 597
|
229 |
+
},
|
230 |
+
"control_if_else": {
|
231 |
+
"opcode": "control_if_else", "next": None, "parent": None,
|
232 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
233 |
+
"x": 335, "y": 779
|
234 |
+
},
|
235 |
+
"control_wait_until": {
|
236 |
+
"opcode": "control_wait_until", "next": None, "parent": None,
|
237 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
238 |
+
"x": 676, "y": 285
|
239 |
+
},
|
240 |
+
"control_repeat_until": {
|
241 |
+
"opcode": "control_repeat_until", "next": None, "parent": None,
|
242 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
243 |
+
"x": 692, "y": 381
|
244 |
+
},
|
245 |
+
"control_stop": {
|
246 |
+
"opcode": "control_stop", "next": None, "parent": None,
|
247 |
+
"inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True,
|
248 |
+
"x": 708, "y": 545, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"}
|
249 |
+
},
|
250 |
+
"control_start_as_clone": {
|
251 |
+
"opcode": "control_start_as_clone", "next": None, "parent": None,
|
252 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
253 |
+
"x": 665, "y": 672
|
254 |
+
},
|
255 |
+
"control_create_clone_of": {
|
256 |
+
"opcode": "control_create_clone_of", "next": None, "parent": None,
|
257 |
+
"inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
258 |
+
"x": 648, "y": 797
|
259 |
+
},
|
260 |
+
"control_create_clone_of_menu": {
|
261 |
+
"opcode": "control_create_clone_of_menu", "next": None, "parent": "control_create_clone_of",
|
262 |
+
"inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False
|
263 |
+
},
|
264 |
+
"control_delete_this_clone": {
|
265 |
+
"opcode": "control_delete_this_clone", "next": None, "parent": None,
|
266 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
267 |
+
"x": 642, "y": 914
|
268 |
+
},
|
269 |
+
|
270 |
+
# data_block.json
|
271 |
+
"data_setvariableto": {
|
272 |
+
"opcode": "data_setvariableto", "next": None, "parent": None,
|
273 |
+
"inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
274 |
+
"x": 348, "y": 241
|
275 |
+
},
|
276 |
+
"data_changevariableby": {
|
277 |
+
"opcode": "data_changevariableby", "next": None, "parent": None,
|
278 |
+
"inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
279 |
+
"x": 313, "y": 363
|
280 |
+
},
|
281 |
+
"data_showvariable": {
|
282 |
+
"opcode": "data_showvariable", "next": None, "parent": None,
|
283 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
284 |
+
"x": 415, "y": 473
|
285 |
+
},
|
286 |
+
"data_hidevariable": {
|
287 |
+
"opcode": "data_hidevariable", "next": None, "parent": None,
|
288 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True,
|
289 |
+
"x": 319, "y": 587
|
290 |
+
},
|
291 |
+
"data_addtolist": {
|
292 |
+
"opcode": "data_addtolist", "next": None, "parent": None,
|
293 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
294 |
+
"x": 385, "y": 109
|
295 |
+
},
|
296 |
+
"data_deleteoflist": {
|
297 |
+
"opcode": "data_deleteoflist", "next": None, "parent": None,
|
298 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
299 |
+
"x": 384, "y": 244
|
300 |
+
},
|
301 |
+
"data_deletealloflist": {
|
302 |
+
"opcode": "data_deletealloflist", "next": None, "parent": None,
|
303 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
304 |
+
"x": 387, "y": 374
|
305 |
+
},
|
306 |
+
"data_insertatlist": {
|
307 |
+
"opcode": "data_insertatlist", "next": None, "parent": None,
|
308 |
+
"inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
309 |
+
"x": 366, "y": 527
|
310 |
+
},
|
311 |
+
"data_replaceitemoflist": {
|
312 |
+
"opcode": "data_replaceitemoflist", "next": None, "parent": None,
|
313 |
+
"inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
314 |
+
"x": 365, "y": 657
|
315 |
+
},
|
316 |
+
"data_itemoflist": {
|
317 |
+
"opcode": "data_itemoflist", "next": None, "parent": None,
|
318 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
319 |
+
"x": 862, "y": 117
|
320 |
+
},
|
321 |
+
"data_itemnumoflist": {
|
322 |
+
"opcode": "data_itemnumoflist", "next": None, "parent": None,
|
323 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
324 |
+
"x": 883, "y": 238
|
325 |
+
},
|
326 |
+
"data_lengthoflist": {
|
327 |
+
"opcode": "data_lengthoflist", "next": None, "parent": None,
|
328 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
329 |
+
"x": 876, "y": 342
|
330 |
+
},
|
331 |
+
"data_listcontainsitem": {
|
332 |
+
"opcode": "data_listcontainsitem", "next": None, "parent": None,
|
333 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
334 |
+
"x": 871, "y": 463
|
335 |
+
},
|
336 |
+
"data_showlist": {
|
337 |
+
"opcode": "data_showlist", "next": None, "parent": None,
|
338 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
339 |
+
"x": 931, "y": 563
|
340 |
+
},
|
341 |
+
"data_hidelist": {
|
342 |
+
"opcode": "data_hidelist", "next": None, "parent": None,
|
343 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True,
|
344 |
+
"x": 962, "y": 716
|
345 |
+
},
|
346 |
+
|
347 |
+
# event_block.json
|
348 |
+
"event_whenflagclicked": {
|
349 |
+
"opcode": "event_whenflagclicked", "next": None, "parent": None,
|
350 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
351 |
+
"x": 166, "y": -422
|
352 |
+
},
|
353 |
+
"event_whenkeypressed": {
|
354 |
+
"opcode": "event_whenkeypressed", "next": None, "parent": None,
|
355 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True,
|
356 |
+
"x": 151, "y": -329
|
357 |
+
},
|
358 |
+
"event_whenthisspriteclicked": {
|
359 |
+
"opcode": "event_whenthisspriteclicked", "next": None, "parent": None,
|
360 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
361 |
+
"x": 156, "y": -223
|
362 |
+
},
|
363 |
+
"event_whenbackdropswitchesto": {
|
364 |
+
"opcode": "event_whenbackdropswitchesto", "next": None, "parent": None,
|
365 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True,
|
366 |
+
"x": 148, "y": -101
|
367 |
+
},
|
368 |
+
"event_whengreaterthan": {
|
369 |
+
"opcode": "event_whengreaterthan", "next": None, "parent": None,
|
370 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True,
|
371 |
+
"x": 150, "y": 10
|
372 |
+
},
|
373 |
+
"event_whenbroadcastreceived": {
|
374 |
+
"opcode": "event_whenbroadcastreceived", "next": None, "parent": None,
|
375 |
+
"inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True,
|
376 |
+
"x": 141, "y": 118
|
377 |
+
},
|
378 |
+
"event_broadcast": {
|
379 |
+
"opcode": "event_broadcast", "next": None, "parent": None,
|
380 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
381 |
+
"x": 151, "y": 229
|
382 |
+
},
|
383 |
+
"event_broadcastandwait": {
|
384 |
+
"opcode": "event_broadcastandwait", "next": None, "parent": None,
|
385 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
386 |
+
"x": 157, "y": 340
|
387 |
+
},
|
388 |
+
|
389 |
+
# look_block.json
|
390 |
+
"looks_sayforsecs": {
|
391 |
+
"opcode": "looks_sayforsecs", "next": None, "parent": None,
|
392 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
393 |
+
"x": 408, "y": 91
|
394 |
+
},
|
395 |
+
"looks_say": {
|
396 |
+
"opcode": "looks_say", "next": None, "parent": None,
|
397 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
398 |
+
"x": 413, "y": 213
|
399 |
+
},
|
400 |
+
"looks_thinkforsecs": {
|
401 |
+
"opcode": "looks_thinkforsecs", "next": None, "parent": None,
|
402 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
403 |
+
"x": 413, "y": 317
|
404 |
+
},
|
405 |
+
"looks_think": {
|
406 |
+
"opcode": "looks_think", "next": None, "parent": None,
|
407 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True,
|
408 |
+
"x": 412, "y": 432
|
409 |
+
},
|
410 |
+
"looks_switchcostumeto": {
|
411 |
+
"opcode": "looks_switchcostumeto", "next": None, "parent": None,
|
412 |
+
"inputs": {"COSTUME": [1, "8;bti4wv(iH9nkOacCJ|"]}, "fields": {}, "shadow": False, "topLevel": True,
|
413 |
+
"x": 411, "y": 555
|
414 |
+
},
|
415 |
+
"looks_costume": {
|
416 |
+
"opcode": "looks_costume", "next": None, "parent": "Q#a,6LPWHqo9-0Nu*[SV",
|
417 |
+
"inputs": {}, "fields": {"COSTUME": ["costume2", None]}, "shadow": True, "topLevel": False
|
418 |
+
},
|
419 |
+
"looks_nextcostume": {
|
420 |
+
"opcode": "looks_nextcostume", "next": None, "parent": None,
|
421 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
422 |
+
"x": 419, "y": 687
|
423 |
+
},
|
424 |
+
"looks_switchbackdropto": {
|
425 |
+
"opcode": "looks_switchbackdropto", "next": None, "parent": None,
|
426 |
+
"inputs": {"BACKDROP": [1, "-?yeX}29V*wd6W:unW0i"]}, "fields": {}, "shadow": False, "topLevel": True,
|
427 |
+
"x": 901, "y": 91
|
428 |
+
},
|
429 |
+
"looks_backdrops": {
|
430 |
+
"opcode": "looks_backdrops", "next": None, "parent": "`Wm^p~l[(IWzc1|wNv*.",
|
431 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False
|
432 |
+
},
|
433 |
+
"looks_changesizeby": {
|
434 |
+
"opcode": "looks_changesizeby", "next": None, "parent": None,
|
435 |
+
"inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
436 |
+
"x": 895, "y": 192
|
437 |
+
},
|
438 |
+
"looks_setsizeto": {
|
439 |
+
"opcode": "looks_setsizeto", "next": None, "parent": None,
|
440 |
+
"inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
441 |
+
"x": 896, "y": 303
|
442 |
+
},
|
443 |
+
"looks_changeeffectby": {
|
444 |
+
"opcode": "looks_changeeffectby", "next": None, "parent": None,
|
445 |
+
"inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True,
|
446 |
+
"x": 892, "y": 416
|
447 |
+
},
|
448 |
+
"looks_seteffectto": {
|
449 |
+
"opcode": "looks_seteffectto", "next": None, "parent": None,
|
450 |
+
"inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True,
|
451 |
+
"x": 902, "y": 527
|
452 |
+
},
|
453 |
+
"looks_cleargraphiceffects": {
|
454 |
+
"opcode": "looks_cleargraphiceffects", "next": None, "parent": None,
|
455 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
456 |
+
"x": 902, "y": 638
|
457 |
+
},
|
458 |
+
"looks_show": {
|
459 |
+
"opcode": "looks_show", "next": None, "parent": None,
|
460 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
461 |
+
"x": 908, "y": 758
|
462 |
+
},
|
463 |
+
"looks_hide": {
|
464 |
+
"opcode": "looks_hide", "next": None, "parent": None,
|
465 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
466 |
+
"x": 455, "y": 861
|
467 |
+
},
|
468 |
+
"looks_gotofrontback": {
|
469 |
+
"opcode": "looks_gotofrontback", "next": None, "parent": None,
|
470 |
+
"inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True,
|
471 |
+
"x": 853, "y": 878
|
472 |
+
},
|
473 |
+
"looks_goforwardbackwardlayers": {
|
474 |
+
"opcode": "looks_goforwardbackwardlayers", "next": None, "parent": None,
|
475 |
+
"inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True,
|
476 |
+
"x": 851, "y": 999
|
477 |
+
},
|
478 |
+
"looks_costumenumbername": {
|
479 |
+
"opcode": "looks_costumenumbername", "next": None, "parent": None,
|
480 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True,
|
481 |
+
"x": 458, "y": 1007
|
482 |
+
},
|
483 |
+
"looks_backdropnumbername": {
|
484 |
+
"opcode": "looks_backdropnumbername", "next": None, "parent": None,
|
485 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True,
|
486 |
+
"x": 1242, "y": 753
|
487 |
+
},
|
488 |
+
"looks_size": {
|
489 |
+
"opcode": "looks_size", "next": None, "parent": None,
|
490 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
491 |
+
"x": 1249, "y": 876
|
492 |
+
},
|
493 |
+
|
494 |
+
# operator_block.json
|
495 |
+
"operator_add": {
|
496 |
+
"opcode": "operator_add", "next": None, "parent": None,
|
497 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
498 |
+
"x": 128, "y": 153
|
499 |
+
},
|
500 |
+
"operator_subtract": {
|
501 |
+
"opcode": "operator_subtract", "next": None, "parent": None,
|
502 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
503 |
+
"x": 134, "y": 214
|
504 |
+
},
|
505 |
+
"operator_multiply": {
|
506 |
+
"opcode": "operator_multiply", "next": None, "parent": None,
|
507 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
508 |
+
"x": 134, "y": 278
|
509 |
+
},
|
510 |
+
"operator_divide": {
|
511 |
+
"opcode": "operator_divide", "next": None, "parent": None,
|
512 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
513 |
+
"x": 138, "y": 359
|
514 |
+
},
|
515 |
+
"operator_random": {
|
516 |
+
"opcode": "operator_random", "next": None, "parent": None,
|
517 |
+
"inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
518 |
+
"x": 311, "y": 157
|
519 |
+
},
|
520 |
+
"operator_gt": {
|
521 |
+
"opcode": "operator_gt", "next": None, "parent": None,
|
522 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
523 |
+
"x": 348, "y": 217
|
524 |
+
},
|
525 |
+
"operator_lt": {
|
526 |
+
"opcode": "operator_lt", "next": None, "parent": None,
|
527 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
528 |
+
"x": 345, "y": 286
|
529 |
+
},
|
530 |
+
"operator_equals": {
|
531 |
+
"opcode": "operator_equals", "next": None, "parent": None,
|
532 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
533 |
+
"x": 345, "y": 372
|
534 |
+
},
|
535 |
+
"operator_and": {
|
536 |
+
"opcode": "operator_and", "next": None, "parent": None,
|
537 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
538 |
+
"x": 701, "y": 158
|
539 |
+
},
|
540 |
+
"operator_or": {
|
541 |
+
"opcode": "operator_or", "next": None, "parent": None,
|
542 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
543 |
+
"x": 705, "y": 222
|
544 |
+
},
|
545 |
+
"operator_not": {
|
546 |
+
"opcode": "operator_not", "next": None, "parent": None,
|
547 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
548 |
+
"x": 734, "y": 283
|
549 |
+
},
|
550 |
+
"operator_join": {
|
551 |
+
"opcode": "operator_join", "next": None, "parent": None,
|
552 |
+
"inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
553 |
+
"x": 663, "y": 378
|
554 |
+
},
|
555 |
+
"operator_letter_of": {
|
556 |
+
"opcode": "operator_letter_of", "next": None, "parent": None,
|
557 |
+
"inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
558 |
+
"x": 664, "y": 445
|
559 |
+
},
|
560 |
+
"operator_length": {
|
561 |
+
"opcode": "operator_length", "next": None, "parent": None,
|
562 |
+
"inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
563 |
+
"x": 664, "y": 521
|
564 |
+
},
|
565 |
+
"operator_contains": {
|
566 |
+
"opcode": "operator_contains", "next": None, "parent": None,
|
567 |
+
"inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
568 |
+
"x": 634, "y": 599
|
569 |
+
},
|
570 |
+
"operator_mod": {
|
571 |
+
"opcode": "operator_mod", "next": None, "parent": None,
|
572 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
573 |
+
"x": 295, "y": 594
|
574 |
+
},
|
575 |
+
"operator_round": {
|
576 |
+
"opcode": "operator_round", "next": None, "parent": None,
|
577 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True,
|
578 |
+
"x": 307, "y": 674
|
579 |
+
},
|
580 |
+
"operator_mathop": {
|
581 |
+
"opcode": "operator_mathop", "next": None, "parent": None,
|
582 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True,
|
583 |
+
"x": 280, "y": 754
|
584 |
+
},
|
585 |
+
|
586 |
+
# sensing_block.json
|
587 |
+
"sensing_touchingobject": {
|
588 |
+
"opcode": "sensing_touchingobject", "next": None, "parent": None,
|
589 |
+
"inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
590 |
+
"x": 359, "y": 116
|
591 |
+
},
|
592 |
+
"sensing_touchingobjectmenu": {
|
593 |
+
"opcode": "sensing_touchingobjectmenu", "next": None, "parent": "sensing_touchingobject",
|
594 |
+
"inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
595 |
+
},
|
596 |
+
"sensing_touchingcolor": {
|
597 |
+
"opcode": "sensing_touchingcolor", "next": None, "parent": None,
|
598 |
+
"inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
599 |
+
"x": 360, "y": 188
|
600 |
+
},
|
601 |
+
"sensing_coloristouchingcolor": {
|
602 |
+
"opcode": "sensing_coloristouchingcolor", "next": None, "parent": None,
|
603 |
+
"inputs": {"COLOR": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
604 |
+
"x": 348, "y": 277
|
605 |
+
},
|
606 |
+
"sensing_askandwait": {
|
607 |
+
"opcode": "sensing_askandwait", "next": None, "parent": None,
|
608 |
+
"inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
609 |
+
"x": 338, "y": 354
|
610 |
+
},
|
611 |
+
"sensing_answer": {
|
612 |
+
"opcode": "sensing_answer", "next": None, "parent": None,
|
613 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
614 |
+
"x": 782, "y": 111
|
615 |
+
},
|
616 |
+
"sensing_keypressed": {
|
617 |
+
"opcode": "sensing_keypressed", "next": None, "parent": None,
|
618 |
+
"inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True,
|
619 |
+
"x": 762, "y": 207
|
620 |
+
},
|
621 |
+
"sensing_keyoptions": {
|
622 |
+
"opcode": "sensing_keyoptions", "next": None, "parent": "sensing_keypressed",
|
623 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False
|
624 |
+
},
|
625 |
+
"sensing_mousedown": {
|
626 |
+
"opcode": "sensing_mousedown", "next": None, "parent": None,
|
627 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
628 |
+
"x": 822, "y": 422
|
629 |
+
},
|
630 |
+
"sensing_mousex": {
|
631 |
+
"opcode": "sensing_mousex", "next": None, "parent": None,
|
632 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
633 |
+
"x": 302, "y": 528
|
634 |
+
},
|
635 |
+
"sensing_mousey": {
|
636 |
+
"opcode": "sensing_mousey", "next": None, "parent": None,
|
637 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
638 |
+
"x": 668, "y": 547
|
639 |
+
},
|
640 |
+
"sensing_setdragmode": {
|
641 |
+
"opcode": "sensing_setdragmode", "next": None, "parent": None,
|
642 |
+
"inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True,
|
643 |
+
"x": 950, "y": 574
|
644 |
+
},
|
645 |
+
"sensing_loudness": {
|
646 |
+
"opcode": "sensing_loudness", "next": None, "parent": None,
|
647 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
648 |
+
"x": 658, "y": 703
|
649 |
+
},
|
650 |
+
"sensing_timer": {
|
651 |
+
"opcode": "sensing_timer", "next": None, "parent": None,
|
652 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
653 |
+
"x": 459, "y": 671
|
654 |
+
},
|
655 |
+
"sensing_resettimer": {
|
656 |
+
"opcode": "sensing_resettimer", "next": None, "parent": None,
|
657 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
658 |
+
"x": 462, "y": 781
|
659 |
+
},
|
660 |
+
"sensing_of": {
|
661 |
+
"opcode": "sensing_of", "next": None, "parent": None,
|
662 |
+
"inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True,
|
663 |
+
"x": 997, "y": 754
|
664 |
+
},
|
665 |
+
"sensing_of_object_menu": {
|
666 |
+
"opcode": "sensing_of_object_menu", "next": None, "parent": "sensing_of",
|
667 |
+
"inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False
|
668 |
+
},
|
669 |
+
"sensing_current": {
|
670 |
+
"opcode": "sensing_current", "next": None, "parent": None,
|
671 |
+
"inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True,
|
672 |
+
"x": 627, "y": 884
|
673 |
+
},
|
674 |
+
"sensing_dayssince2000": {
|
675 |
+
"opcode": "sensing_dayssince2000", "next": None, "parent": None,
|
676 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
677 |
+
"x": 959, "y": 903
|
678 |
+
},
|
679 |
+
"sensing_username": {
|
680 |
+
"opcode": "sensing_username", "next": None, "parent": None,
|
681 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
682 |
+
"x": 833, "y": 757
|
683 |
+
},
|
684 |
+
|
685 |
+
# sound_block.json
|
686 |
+
"sound_playuntildone": {
|
687 |
+
"opcode": "sound_playuntildone", "next": None, "parent": None,
|
688 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
689 |
+
"x": 253, "y": 17
|
690 |
+
},
|
691 |
+
"sound_sounds_menu": {
|
692 |
+
"opcode": "sound_sounds_menu", "next": None, "parent": "sound_playuntildone and sound_play",
|
693 |
+
"inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False
|
694 |
+
},
|
695 |
+
"sound_play": {
|
696 |
+
"opcode": "sound_play", "next": None, "parent": None,
|
697 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True,
|
698 |
+
"x": 245, "y": 122
|
699 |
+
},
|
700 |
+
"sound_stopallsounds": {
|
701 |
+
"opcode": "sound_stopallsounds", "next": None, "parent": None,
|
702 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
703 |
+
"x": 253, "y": 245
|
704 |
+
},
|
705 |
+
"sound_changeeffectby": {
|
706 |
+
"opcode": "sound_changeeffectby", "next": None, "parent": None,
|
707 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True,
|
708 |
+
"x": 653, "y": 14
|
709 |
+
},
|
710 |
+
"sound_seteffectto": {
|
711 |
+
"opcode": "sound_seteffectto", "next": None, "parent": None,
|
712 |
+
"inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True,
|
713 |
+
"x": 653, "y": 139
|
714 |
+
},
|
715 |
+
"sound_cleareffects": {
|
716 |
+
"opcode": "sound_cleareffects", "next": None, "parent": None,
|
717 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
718 |
+
"x": 651, "y": 242
|
719 |
+
},
|
720 |
+
"sound_changevolumeby": {
|
721 |
+
"opcode": "sound_changevolumeby", "next": None, "parent": None,
|
722 |
+
"inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
723 |
+
"x": 645, "y": 353
|
724 |
+
},
|
725 |
+
"sound_setvolumeto": {
|
726 |
+
"opcode": "sound_setvolumeto", "next": None, "parent": None,
|
727 |
+
"inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True,
|
728 |
+
"x": 1108, "y": 5
|
729 |
+
},
|
730 |
+
"sound_volume": {
|
731 |
+
"opcode": "sound_volume", "next": None, "parent": None,
|
732 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True,
|
733 |
+
"x": 1136, "y": 123
|
734 |
+
},
|
735 |
+
}
|
736 |
+
|
737 |
+
# #Example input with opcodes from various categories
|
738 |
+
# input_opcodes = [
|
739 |
+
# {"opcode": "sound_play", "count": 2}, # New: Sound block with menu
|
740 |
+
# {"opcode": "sound_playuntildone", "count": 2}, # New: Sound block with menu
|
741 |
+
# ]
|
742 |
+
|
743 |
+
# Example input with opcodes from various categories
|
744 |
+
input_opcodes = [
|
745 |
+
{"opcode": "sound_play", "count": 2},
|
746 |
+
{"opcode": "sound_playuntildone", "count": 2},
|
747 |
+
{"opcode":"motion_goto","count":2},
|
748 |
+
{"opcode":"motion_glideto","count":2},
|
749 |
+
{"opcode":"looks_switchbackdropto","count":2},
|
750 |
+
{"opcode":"looks_switchcostumeto","count":2},
|
751 |
+
{"opcode":"control_create_clone_of","count":2},
|
752 |
+
{"opcode":"sensing_touchingobject","count":2},
|
753 |
+
{"opcode":"sensing_of","count":2},
|
754 |
+
{"opcode":"sensing_keypressed","count":2},
|
755 |
+
{"opcode":"motion_pointtowards","count":2},
|
756 |
+
]
|
757 |
+
|
758 |
+
generated_output = generate_blocks_from_opcodes(input_opcodes, all_block_definitions)
|
759 |
+
print(json.dumps(generated_output, indent=2))
|
v2/utils/block_naming.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import secrets
|
2 |
+
import string
|
3 |
+
from typing import Dict, Any, TypedDict
|
4 |
+
|
5 |
+
def generate_secure_token(length=20):
|
6 |
+
charset = string.ascii_letters + string.digits + "!@#$%^&*()[]{}=+-_~"
|
7 |
+
return ''.join(secrets.choice(charset) for _ in range(length))
|
8 |
+
|
9 |
+
def rename_blocks(block_json: dict, opcode_count: dict) -> tuple[dict, dict]:
|
10 |
+
"""
|
11 |
+
Replace each block key in block_json and each identifier in opcode_count
|
12 |
+
with a newly generated secure token.
|
13 |
+
|
14 |
+
Args:
|
15 |
+
block_json: Mapping of block_key -> block_data.
|
16 |
+
opcode_count: Mapping of opcode -> list of block_keys.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
A tuple of (new_block_json, new_opcode_count) with updated keys.
|
20 |
+
"""
|
21 |
+
# Step 1: Generate a secure token mapping for every existing block key
|
22 |
+
token_map = {}
|
23 |
+
for old_key in block_json.keys():
|
24 |
+
# Ensure uniqueness in the unlikely event of a collision
|
25 |
+
while True:
|
26 |
+
new_key = generate_secure_token()
|
27 |
+
if new_key not in token_map.values():
|
28 |
+
break
|
29 |
+
token_map[old_key] = new_key
|
30 |
+
|
31 |
+
# Step 2: Rebuild block_json with new keys
|
32 |
+
new_block_json = {}
|
33 |
+
for old_key, block in block_json.items():
|
34 |
+
new_key = token_map[old_key]
|
35 |
+
new_block_json[new_key] = block.copy()
|
36 |
+
|
37 |
+
# Update parent and next references
|
38 |
+
if 'parent' in block and block['parent'] in token_map:
|
39 |
+
new_block_json[new_key]['parent'] = token_map[block['parent']]
|
40 |
+
if 'next' in block and block['next'] in token_map:
|
41 |
+
new_block_json[new_key]['next'] = token_map[block['next']]
|
42 |
+
|
43 |
+
# Update inputs if they reference blocks
|
44 |
+
for inp_key, inp_val in block.get('inputs', {}).items():
|
45 |
+
if isinstance(inp_val, list) and len(inp_val) == 2:
|
46 |
+
idx, ref = inp_val
|
47 |
+
if idx in (2, 3) and isinstance(ref, str) and ref in token_map:
|
48 |
+
new_block_json[new_key]['inputs'][inp_key] = [idx, token_map[ref]]
|
49 |
+
|
50 |
+
# Step 3: Update opcode count map
|
51 |
+
new_opcode_count = {}
|
52 |
+
for opcode, key_list in opcode_count.items():
|
53 |
+
new_opcode_count[opcode] = [token_map.get(k, k) for k in key_list]
|
54 |
+
|
55 |
+
return new_block_json, new_opcode_count
|
56 |
+
|
57 |
+
# Example usage:
|
58 |
+
if __name__ == "__main__":
|
59 |
+
blocks = {'event_whenflagclicked_1': {'opcode': 'event_whenflagclicked', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': True, 'parent': None, 'next': 'motion_gotoxy_1'}, 'motion_gotoxy_1': {'opcode': 'motion_gotoxy', 'inputs': {'X': [1, [4, '240']], 'Y': [1, [4, '-135']]}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'data_setvariableto_1'}, 'motion_xposition_1': {'opcode': 'motion_xposition', 'inputs': {}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'operator_lt_1', 'next': None}, 'motion_setx_1': {'opcode': 'motion_setx', 'inputs': {'X': [1, [4, '240']]}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_if_1', 'next': None}, 'control_forever_1': {'opcode': 'control_forever', 'inputs': {'SUBSTACK': [2, 'control_if_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': None}, 'control_if_1': {'opcode': 'control_if', 'inputs': {'CONDITION': [2, 'operator_lt_1'], 'SUBSTACK': [2, 'motion_setx_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_forever_1', 'next': 'control_if_2'}, 'control_if_2': {'opcode': 'control_if', 'inputs': {'CONDITION': [2, 'sensing_touchingobject_1'], 'SUBSTACK': [2, 'event_broadcast_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_forever_1', 'next': None}, 'control_stop_1': {'opcode': 'control_stop', 'inputs': {}, 'fields': {'STOP_OPTION': ['all ', None]}, 'shadow': False, 'topLevel': False, 'parent': 'control_if_2', 'next': None, 'mutation': {'tagName': 'mutation', 'children': [], 'hasnext': 'false'}}, 'operator_lt_1': {'opcode': 'operator_lt', 'inputs': {'OPERAND1': [3, 'motion_xposition_1', [10, '']], 'OPERAND2': [1, [4, '-235']]}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_if_1', 'next': None}, 'sensing_touchingobject_1': {'opcode': 'sensing_touchingobject', 'inputs': {'TOUCHINGOBJECTMENU': [1, 'sensing_touchingobjectmenu_1']}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_if_2', 'next': None}, 'sensing_touchingobjectmenu_1': {'opcode': 'sensing_touchingobjectmenu', 'inputs': {}, 'fields': {'TOUCHINGOBJECTMENU': ['sprite1', None]}, 'shadow': True, 'topLevel': False, 'parent': 'sensing_touchingobject_1', 'next': None}, 'sensing_touchingobjectmenu_2': {'opcode': 'sensing_touchingobjectmenu', 'inputs': {}, 'fields': {'TOUCHINGOBJECTMENU': ['_mouse_', None]}, 'shadow': False, 'topLevel': False, 'parent': None, 'next': None}, 'event_broadcast_1': {'opcode': 'event_broadcast', 'inputs': {'BROADCAST_INPUT': [1, [11, 'Game Over', '<Game Over_unique_id>']]}, 'fields': {}, 'shadow': False, 'topLevel': False, 'parent': 'control_if_2', 'next': 'control_stop_1'}, 'data_setvariableto_1': {'opcode': 'data_setvariableto', 'inputs': {'VALUE': [1, [4, '1']]}, 'fields': {'VARIABLE': ['score', 'score']}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'data_setvariableto_2'}, 'data_setvariableto_2': {'opcode': 'data_setvariableto', 'inputs': {'VALUE': [1, [4, '1']]}, 'fields': {'VARIABLE': ['speed', 'speed']}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'data_showvariable_1'}, 'data_showvariable_1': {'opcode': 'data_showvariable', 'inputs': {}, 'fields': {'VARIABLE': ['score', 'score']}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'data_showvariable_2'}, 'data_showvariable_2': {'opcode': 'data_showvariable', 'inputs': {}, 'fields': {'VARIABLE': ['speed', 'speed']}, 'shadow': False, 'topLevel': False, 'parent': 'event_whenflagclicked_1', 'next': 'control_forever_1'}} # your first JSON
|
60 |
+
opcode_keys = {'event_whenflagclicked': ['event_whenflagclicked_1'], 'motion_gotoxy': ['motion_gotoxy_1'], 'motion_xposition': ['motion_xposition_1'], 'motion_setx': ['motion_setx_1'], 'control_forever': ['control_forever_1'], 'control_if': ['control_if_1', 'control_if_2'], 'control_stop': ['control_stop_1'], 'operator_lt': ['operator_lt_1'], 'sensing_touchingobject': ['sensing_touchingobject_1'], 'sensing_touchingobjectmenu': ['sensing_touchingobjectmenu_1', 'sensing_touchingobjectmenu_2'], 'event_broadcast': ['event_broadcast_1'], 'data_setvariableto': ['data_setvariableto_1', 'data_setvariableto_2'], 'data_showvariable': ['data_showvariable_1', 'data_showvariable_2']} # your second JSON
|
61 |
+
renamed_blocks, renamed_counts = rename_blocks(blocks, opcode_keys)
|
62 |
+
print(renamed_blocks)
|
v2/utils/block_relation_builder.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/block_relation_builder_v2.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/block_var_setter.py
ADDED
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
def update_scratch_project_data(project_data):
|
4 |
+
"""
|
5 |
+
Updates variable and broadcast definitions in a Scratch project JSON,
|
6 |
+
populating the 'variables' and 'broadcasts' sections of the Stage target
|
7 |
+
and extracting initial values for variables.
|
8 |
+
|
9 |
+
Args:
|
10 |
+
project_data (dict): The loaded JSON data of the Scratch project.
|
11 |
+
|
12 |
+
Returns:
|
13 |
+
dict: The updated project JSON data.
|
14 |
+
"""
|
15 |
+
|
16 |
+
stage_target = None
|
17 |
+
for target in project_data['targets']:
|
18 |
+
if target.get('isStage'):
|
19 |
+
stage_target = target
|
20 |
+
break
|
21 |
+
|
22 |
+
if stage_target is None:
|
23 |
+
print("Error: Stage target not found in the project data.")
|
24 |
+
return project_data
|
25 |
+
|
26 |
+
# Ensure 'variables' and 'broadcasts' exist in the Stage target
|
27 |
+
if "variables" not in stage_target:
|
28 |
+
stage_target["variables"] = {}
|
29 |
+
if "broadcasts" not in stage_target:
|
30 |
+
stage_target["broadcasts"] = {}
|
31 |
+
|
32 |
+
# Helper function to recursively find and update variable/broadcast fields
|
33 |
+
def process_dict(obj):
|
34 |
+
if isinstance(obj, dict):
|
35 |
+
# Check for "data_setvariableto" opcode to extract initial values
|
36 |
+
if obj.get("opcode") == "data_setvariableto":
|
37 |
+
variable_field = obj.get("fields", {}).get("VARIABLE")
|
38 |
+
value_input = obj.get("inputs", {}).get("VALUE")
|
39 |
+
|
40 |
+
if variable_field and isinstance(variable_field, list) and len(variable_field) == 2:
|
41 |
+
var_name = variable_field[0]
|
42 |
+
var_id = variable_field[1]
|
43 |
+
|
44 |
+
initial_value = ""
|
45 |
+
if value_input and isinstance(value_input, list) and len(value_input) > 1 and \
|
46 |
+
isinstance(value_input[1], list) and len(value_input[1]) > 1:
|
47 |
+
# Extract value from various formats, e.g., [1, [10, "0"]] or [3, [12, "score", "id"], [10, "0"]]
|
48 |
+
if value_input[1][0] == 10: # Direct value like [10, "0"]
|
49 |
+
initial_value = str(value_input[1][1])
|
50 |
+
elif value_input[1][0] == 12 and len(value_input) > 2 and isinstance(value_input[2], list) and value_input[2][0] == 10: # Variable reference with initial value block
|
51 |
+
initial_value = str(value_input[2][1])
|
52 |
+
elif isinstance(value_input[1], (str, int, float)): # For direct number/string inputs
|
53 |
+
initial_value = str(value_input[1])
|
54 |
+
|
55 |
+
|
56 |
+
# Add/update the variable in the Stage's 'variables' with its initial value
|
57 |
+
stage_target["variables"][var_id] = [var_name, initial_value]
|
58 |
+
|
59 |
+
|
60 |
+
for key, value in obj.items():
|
61 |
+
# Process variable definitions in 'fields' (for blocks that define variables like 'show variable')
|
62 |
+
if key == "VARIABLE" and isinstance(value, list) and len(value) == 2:
|
63 |
+
var_name = value[0]
|
64 |
+
var_id = value[1]
|
65 |
+
# Only add if not already defined with an initial value from set_variableto
|
66 |
+
if var_id not in stage_target["variables"]:
|
67 |
+
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
|
68 |
+
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
|
69 |
+
stage_target["variables"][var_id][0] = var_name
|
70 |
+
|
71 |
+
|
72 |
+
# Process broadcast definitions in 'inputs' (BROADCAST_INPUT)
|
73 |
+
elif key == "BROADCAST_INPUT" and isinstance(value, list) and len(value) == 2 and \
|
74 |
+
isinstance(value[1], list) and len(value[1]) == 3 and value[1][0] == 11:
|
75 |
+
broadcast_name = value[1][1]
|
76 |
+
broadcast_id = value[1][2]
|
77 |
+
# Add/update the broadcast in the Stage's 'broadcasts'
|
78 |
+
stage_target["broadcasts"][broadcast_id] = broadcast_name
|
79 |
+
|
80 |
+
# Process broadcast definitions in 'fields' (BROADCAST_OPTION)
|
81 |
+
elif key == "BROADCAST_OPTION" and isinstance(value, list) and len(value) == 2:
|
82 |
+
broadcast_name = value[0]
|
83 |
+
broadcast_id = value[1]
|
84 |
+
# Add/update the broadcast in the Stage's 'broadcasts'
|
85 |
+
stage_target["broadcasts"][broadcast_id] = broadcast_name
|
86 |
+
|
87 |
+
# Recursively call for nested dictionaries or lists
|
88 |
+
process_dict(value)
|
89 |
+
elif isinstance(obj, list):
|
90 |
+
for i, item in enumerate(obj):
|
91 |
+
# Process variable references in 'inputs' (like [12, "score", "id"])
|
92 |
+
if isinstance(item, list) and len(item) == 3 and item[0] == 12:
|
93 |
+
var_name = item[1]
|
94 |
+
var_id = item[2]
|
95 |
+
# Only add if not already defined with an initial value from set_variableto
|
96 |
+
if var_id not in stage_target["variables"]:
|
97 |
+
stage_target["variables"][var_id] = [var_name, ""] # Default to empty string if no initial value found yet
|
98 |
+
elif stage_target["variables"][var_id][0] != var_name: # Update name if ID exists but name is different
|
99 |
+
stage_target["variables"][var_id][0] = var_name
|
100 |
+
|
101 |
+
process_dict(item)
|
102 |
+
|
103 |
+
# Iterate through all targets to process their blocks
|
104 |
+
for target in project_data['targets']:
|
105 |
+
if "blocks" in target:
|
106 |
+
for block_id, block_data in target["blocks"].items():
|
107 |
+
process_dict(block_data)
|
108 |
+
|
109 |
+
return project_data
|
110 |
+
|
111 |
+
def deduplicate_variables(project_data):
|
112 |
+
"""
|
113 |
+
Removes duplicate variable entries in the 'variables' dictionary of the Stage target,
|
114 |
+
prioritizing entries with non-empty values.
|
115 |
+
|
116 |
+
Args:
|
117 |
+
project_data (dict): The loaded JSON data of the Scratch project.
|
118 |
+
|
119 |
+
Returns:
|
120 |
+
dict: The updated project JSON data with deduplicated variables.
|
121 |
+
"""
|
122 |
+
|
123 |
+
stage_target = None
|
124 |
+
for target in project_data['targets']:
|
125 |
+
if target.get('isStage'):
|
126 |
+
stage_target = target
|
127 |
+
break
|
128 |
+
|
129 |
+
if stage_target is None:
|
130 |
+
print("Error: Stage target not found in the project data.")
|
131 |
+
return project_data
|
132 |
+
|
133 |
+
if "variables" not in stage_target:
|
134 |
+
return project_data # No variables to deduplicate
|
135 |
+
|
136 |
+
# Use a temporary dictionary to store the preferred variable entry by name
|
137 |
+
# Format: {variable_name: [variable_id, variable_name, variable_value]}
|
138 |
+
resolved_variables = {}
|
139 |
+
|
140 |
+
for var_id, var_info in stage_target["variables"].items():
|
141 |
+
var_name = var_info[0]
|
142 |
+
var_value = var_info[1]
|
143 |
+
|
144 |
+
if var_name not in resolved_variables:
|
145 |
+
# If the variable name is not yet seen, add it
|
146 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
147 |
+
else:
|
148 |
+
# If the variable name is already seen, decide which one to keep
|
149 |
+
existing_id, existing_name, existing_value = resolved_variables[var_name]
|
150 |
+
|
151 |
+
# Prioritize the entry with a non-empty value
|
152 |
+
if var_value != "" and existing_value == "":
|
153 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
154 |
+
# If both have non-empty values, or both are empty, keep the current one (arbitrary choice, but consistent)
|
155 |
+
# The current logic will effectively keep the last one encountered that has a value,
|
156 |
+
# or the very last one if all are empty.
|
157 |
+
elif var_value != "" and existing_value != "":
|
158 |
+
# If there are multiple non-empty values for the same variable name
|
159 |
+
# this keeps the one from the most recent iteration.
|
160 |
+
# For the given example, this will correctly keep "5".
|
161 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
162 |
+
elif var_value == "" and existing_value == "":
|
163 |
+
# If both are empty, just keep the current one (arbitrary)
|
164 |
+
resolved_variables[var_name] = [var_id, var_name, var_value]
|
165 |
+
|
166 |
+
|
167 |
+
# Reconstruct the 'variables' dictionary using the resolved entries
|
168 |
+
new_variables_dict = {}
|
169 |
+
for var_name, var_data in resolved_variables.items():
|
170 |
+
var_id_to_keep = var_data[0]
|
171 |
+
var_name_to_keep = var_data[1]
|
172 |
+
var_value_to_keep = var_data[2]
|
173 |
+
new_variables_dict[var_id_to_keep] = [var_name_to_keep, var_value_to_keep]
|
174 |
+
|
175 |
+
stage_target["variables"] = new_variables_dict
|
176 |
+
|
177 |
+
return project_data
|
178 |
+
|
179 |
+
# Example usage with your provided JSON:
|
180 |
+
if __name__ == "__main__":
|
181 |
+
json_data = {
|
182 |
+
"targets": [
|
183 |
+
{"isStage": True, "name": "Stage", "variables": {},"lists": {}, "broadcasts": {}, "blocks": {
|
184 |
+
"event_whenflagclicked_1": {
|
185 |
+
"opcode": "event_whenflagclicked",
|
186 |
+
"inputs": {},
|
187 |
+
"fields": {},
|
188 |
+
"shadow": False,
|
189 |
+
"topLevel": True,
|
190 |
+
"parent": None,
|
191 |
+
"next": "data_setvariableto_1"
|
192 |
+
},
|
193 |
+
"data_setvariableto_1": {
|
194 |
+
"opcode": "data_setvariableto",
|
195 |
+
"inputs": {
|
196 |
+
"VALUE": [
|
197 |
+
1,
|
198 |
+
[
|
199 |
+
4,
|
200 |
+
"0"
|
201 |
+
]
|
202 |
+
]
|
203 |
+
},
|
204 |
+
"fields": {
|
205 |
+
"VARIABLE": [
|
206 |
+
"score",
|
207 |
+
"$ELgBAKpb[&l+DX$EMK4"
|
208 |
+
]
|
209 |
+
},
|
210 |
+
"shadow": False,
|
211 |
+
"topLevel": False,
|
212 |
+
"parent": "event_whenflagclicked_1",
|
213 |
+
"next": "data_setvariableto_2"
|
214 |
+
},
|
215 |
+
"data_setvariableto_2": {
|
216 |
+
"opcode": "data_setvariableto",
|
217 |
+
"inputs": {
|
218 |
+
"VALUE": [
|
219 |
+
1,
|
220 |
+
[
|
221 |
+
4,
|
222 |
+
"3"
|
223 |
+
]
|
224 |
+
]
|
225 |
+
},
|
226 |
+
"fields": {
|
227 |
+
"VARIABLE": [
|
228 |
+
"lives",
|
229 |
+
"Gb]1jA+H{h_6z1^Fn!-a"
|
230 |
+
]
|
231 |
+
},
|
232 |
+
"shadow": False,
|
233 |
+
"topLevel": False,
|
234 |
+
"parent": "data_setvariableto_1",
|
235 |
+
"next": "data_showvariable_1"
|
236 |
+
},
|
237 |
+
"data_showvariable_1": {
|
238 |
+
"opcode": "data_showvariable",
|
239 |
+
"inputs": {},
|
240 |
+
"fields": {
|
241 |
+
"VARIABLE": [
|
242 |
+
"score",
|
243 |
+
"$ELgBAKpb[&l+DX$EMK4"
|
244 |
+
]
|
245 |
+
},
|
246 |
+
"shadow": False,
|
247 |
+
"topLevel": False,
|
248 |
+
"parent": "data_setvariableto_2",
|
249 |
+
"next": "data_showvariable_2"
|
250 |
+
},
|
251 |
+
"data_showvariable_2": {
|
252 |
+
"opcode": "data_showvariable",
|
253 |
+
"inputs": {},
|
254 |
+
"fields": {
|
255 |
+
"VARIABLE": [
|
256 |
+
"lives",
|
257 |
+
"Gb]1jA+H{h_6z1^Fn!-a"
|
258 |
+
]
|
259 |
+
},
|
260 |
+
"shadow": False,
|
261 |
+
"topLevel": False,
|
262 |
+
"parent": "data_showvariable_1",
|
263 |
+
"next": "event_broadcast_1"
|
264 |
+
},
|
265 |
+
"event_broadcast_1": {
|
266 |
+
"opcode": "event_broadcast",
|
267 |
+
"inputs": {
|
268 |
+
"BROADCAST_INPUT": [
|
269 |
+
1,
|
270 |
+
[
|
271 |
+
11,
|
272 |
+
"Game Start",
|
273 |
+
"hN1d@^S}e)H~8(qp)rGN"
|
274 |
+
]
|
275 |
+
]
|
276 |
+
},
|
277 |
+
"fields": {},
|
278 |
+
"shadow": False,
|
279 |
+
"topLevel": False,
|
280 |
+
"parent": "data_showvariable_2",
|
281 |
+
"next": None
|
282 |
+
}
|
283 |
+
}, "comments": {}, "currentCostume": 1, "costumes": [{"name": "backdrop1", "dataFormat": "svg", "assetId": "cd21514d0531fdffb22204e0ec5ed84a", "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", "rotationCenterX": 240, "rotationCenterY": 180}, {"name": "Blue Sky", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "e7c147730f19d284bcd7b3f00af19bb6", "rotationCenterX": 240, "rotationCenterY": 180}], "sounds": [{"name": "pop", "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 1123, "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav"}], "volume": 100, "layerOrder": 0, "tempo": 60, "videoTransparency": 50, "videoState": "on", "textToSpeechLanguage": None},
|
284 |
+
{"isStage": False, "name": "Sprite1", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "costume1", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "bcf454acf82e4504149f7ffe07081dbc", "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", "rotationCenterX": 48, "rotationCenterY": 50}, {"name": "costume2", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "0fb9be3e8397c983338cb71dc84d0b25", "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", "rotationCenterX": 46, "rotationCenterY": 53}], "sounds": [{"name": "Meow", "assetId": "83c36d806dc92327b9e7049a565c6bff", "dataFormat": "wav", "format": "", "rate": 48000, "sampleCount": 40681, "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav"}], "volume": 100, "layerOrder": 1, "visible": True, "x": 0, "y": -120, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"},
|
285 |
+
{"isStage": False, "name": "Soccer Ball", "variables": {}, "lists": {}, "broadcasts": {}, "blocks": {}, "comments": {}, "currentCostume": 0, "costumes": [{"name": "soccer ball", "bitmapResolution": 1, "dataFormat": "svg", "assetId": "5d973d7a3a8be3f3bd6e1cd0f73c32b5", "md5ext": "5d973d7a3a8be3f3bd6e1cd0f73c32b5.svg", "rotationCenterX": 23, "rotationCenterY": 22}], "sounds": [{"name": "basketball bounce", "assetId": "1727f65b5f22d151685b8e5917456a60", "dataFormat": "wav", "format": "adpcm", "rate": 22050, "sampleCount": 8129, "md5ext": "1727f65b5f22d151685b8e5917456a60.wav"}], "volume": 100, "layerOrder": 2, "visible": True, "x": -130, "y": -60, "size": 100, "direction": 90, "draggable": False, "rotationStyle": "all around"}], "monitors": [{"id": "76*2udMupx@8!=)9Uqvj", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "score"}, "spriteName": None, "value": "0", "width": 0, "height": 0, "x": 5, "y": 5, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "YiV;2%+lgjnJ$|*Jy.{H", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "cloud Var"}, "spriteName": None, "value": 0, "width": 0, "height": 0, "x": 5, "y": 32, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "@D/}fdt{0XaJ`kRE0t~F", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "lives"}, "spriteName": None, "value": "3", "width": 0, "height": 0, "x": 5, "y": 59, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}, {"id": "^R,uz7yRjtK`=uP~6SN4", "mode": "default", "opcode": "data_variable", "params": {"VARIABLE": "speed"}, "spriteName": None, "value": "5", "width": 0, "height": 0, "x": 5, "y": 86, "visible": True, "sliderMin": 0, "sliderMax": 100, "isDiscrete": True}], "extensions": [], "meta": {"semver": "3.0.0", "vm": "11.3.0", "agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"}}
|
286 |
+
|
287 |
+
|
288 |
+
updated_json_data = update_scratch_project_data(json_data)
|
289 |
+
updated_json_data = deduplicate_variables(json_data)
|
290 |
+
print(json.dumps(updated_json_data, indent=1))
|
v2/utils/generated_output_json.json
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"event_whenkeypressed_1": {
|
3 |
+
"block_name": "when () key pressed",
|
4 |
+
"block_type": "Events",
|
5 |
+
"op_code": "event_whenkeypressed",
|
6 |
+
"block_shape": "Hat Block",
|
7 |
+
"functionality": "This Hat block initiates the script when a specified keyboard key is pressed.",
|
8 |
+
"inputs": {},
|
9 |
+
"fields": {
|
10 |
+
"KEY_OPTION": [
|
11 |
+
"space",
|
12 |
+
null
|
13 |
+
]
|
14 |
+
},
|
15 |
+
"shadow": false,
|
16 |
+
"topLevel": false,
|
17 |
+
"id": "event_whenkeypressed_1",
|
18 |
+
"parent": null,
|
19 |
+
"next": null
|
20 |
+
},
|
21 |
+
"motion_changeyby_1": {
|
22 |
+
"block_name": "change y by ()",
|
23 |
+
"block_type": "Motion",
|
24 |
+
"block_shape": "Stack Block",
|
25 |
+
"op_code": "motion_changeyby",
|
26 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
27 |
+
"inputs": {
|
28 |
+
"DY": [
|
29 |
+
1,
|
30 |
+
[
|
31 |
+
4,
|
32 |
+
"10"
|
33 |
+
]
|
34 |
+
]
|
35 |
+
},
|
36 |
+
"fields": {},
|
37 |
+
"shadow": false,
|
38 |
+
"topLevel": false,
|
39 |
+
"id": "motion_changeyby_1",
|
40 |
+
"parent": null,
|
41 |
+
"next": null
|
42 |
+
},
|
43 |
+
"motion_changeyby_2": {
|
44 |
+
"block_name": "change y by ()",
|
45 |
+
"block_type": "Motion",
|
46 |
+
"block_shape": "Stack Block",
|
47 |
+
"op_code": "motion_changeyby",
|
48 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
49 |
+
"inputs": {
|
50 |
+
"DY": [
|
51 |
+
1,
|
52 |
+
[
|
53 |
+
4,
|
54 |
+
"10"
|
55 |
+
]
|
56 |
+
]
|
57 |
+
},
|
58 |
+
"fields": {},
|
59 |
+
"shadow": false,
|
60 |
+
"topLevel": false,
|
61 |
+
"id": "motion_changeyby_2",
|
62 |
+
"parent": null,
|
63 |
+
"next": null
|
64 |
+
},
|
65 |
+
"control_wait_1": {
|
66 |
+
"block_name": "wait () seconds",
|
67 |
+
"block_type": "Control",
|
68 |
+
"block_shape": "Stack Block",
|
69 |
+
"op_code": "control_wait",
|
70 |
+
"functionality": "Pauses the script for a specified duration.",
|
71 |
+
"inputs": {
|
72 |
+
"DURATION": [
|
73 |
+
1,
|
74 |
+
[
|
75 |
+
5,
|
76 |
+
"1"
|
77 |
+
]
|
78 |
+
]
|
79 |
+
},
|
80 |
+
"fields": {},
|
81 |
+
"shadow": false,
|
82 |
+
"topLevel": false,
|
83 |
+
"id": "control_wait_1",
|
84 |
+
"parent": null,
|
85 |
+
"next": null
|
86 |
+
}
|
87 |
+
}
|
v2/utils/half_working_plan.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/helper_function.py
ADDED
File without changes
|
v2/utils/logs.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
the plan_generator_6 is working well but sometime losses the boolean parent and reportor booleans
|
2 |
+
the plan genratr_7 works well then the 6 version.
|
3 |
+
the plan generator_8 works well for parent and child logics.
|
4 |
+
the plan generator_9 has better but lacks menu features.
|
5 |
+
the plan generator_10 worke better for parent and as well as menu features.
|
6 |
+
the plan 11 is for generel case but lackk substack logics.
|
v2/utils/opcode_counter.py
ADDED
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import re
|
3 |
+
from typing import Any, Dict
|
4 |
+
import logging
|
5 |
+
|
6 |
+
logger = logging.getLogger(__name__)
|
7 |
+
|
8 |
+
# Dummy data for demonstration. You should replace this with your actual opcode data.
|
9 |
+
# Each item should have at least an 'opcode' and 'text' field.
|
10 |
+
hat_block_data = [
|
11 |
+
{"opcode": "event_whenflagclicked", "text": "when green flag clicked"},
|
12 |
+
{"opcode": "event_whenkeypressed", "text": "when [key] pressed"},
|
13 |
+
{"opcode": "event_whenbroadcastreceived", "text": "when I receive [message]"},
|
14 |
+
]
|
15 |
+
boolean_block_data = [
|
16 |
+
{"opcode": "operator_gt", "text": "< ( ) > ( ) >"},
|
17 |
+
{"opcode": "sensing_touchingobject", "text": "<touching [object]?>"},
|
18 |
+
{"opcode": "operator_equals", "text": "< ( ) = ( ) >"},
|
19 |
+
]
|
20 |
+
c_block_data = [
|
21 |
+
{"opcode": "control_forever", "text": "forever"},
|
22 |
+
{"opcode": "control_if", "text": "if < > then"},
|
23 |
+
{"opcode": "control_repeat", "text": "repeat ( )"},
|
24 |
+
]
|
25 |
+
cap_block_data = [
|
26 |
+
{"opcode": "control_stop", "text": "stop [all]"},
|
27 |
+
]
|
28 |
+
reporter_block_data = [
|
29 |
+
{"opcode": "motion_xposition", "text": "(x position)"},
|
30 |
+
{"opcode": "motion_yposition", "text": "(y position)"},
|
31 |
+
{"opcode": "data_variable", "text": "(variable)"},
|
32 |
+
{"opcode": "sensing_answer", "text": "(answer)"},
|
33 |
+
]
|
34 |
+
stack_block_data = [
|
35 |
+
{"opcode": "motion_gotoxy", "text": "go to x: ( ) y: ( )"},
|
36 |
+
{"opcode": "motion_changeyby", "text": "change y by ( )"},
|
37 |
+
{"opcode": "motion_setx", "text": "set x to ( )"},
|
38 |
+
{"opcode": "motion_glidesecstoxy", "text": "glide ( ) secs to x: ( ) y: ( )"},
|
39 |
+
{"opcode": "data_setvariableto", "text": "set [variable] to ( )"},
|
40 |
+
{"opcode": "looks_hide", "text": "hide"},
|
41 |
+
{"opcode": "looks_show", "text": "show"},
|
42 |
+
{"opcode": "event_broadcast", "text": "broadcast [message]"},
|
43 |
+
]
|
44 |
+
|
45 |
+
# Combine all block data into a single list for easier lookup
|
46 |
+
all_opcodes_list = []
|
47 |
+
for category_data in [
|
48 |
+
hat_block_data,
|
49 |
+
boolean_block_data,
|
50 |
+
c_block_data,
|
51 |
+
cap_block_data,
|
52 |
+
reporter_block_data,
|
53 |
+
stack_block_data,
|
54 |
+
]:
|
55 |
+
all_opcodes_list.extend(category_data)
|
56 |
+
|
57 |
+
|
58 |
+
def extract_json_from_llm_response(response_text: str) -> Dict[str, Any]:
|
59 |
+
"""Extracts JSON from an LLM response string."""
|
60 |
+
try:
|
61 |
+
json_match = re.search(r"```json\n(.*)\n```", response_text, re.DOTALL)
|
62 |
+
if json_match:
|
63 |
+
return json.loads(json_match.group(1))
|
64 |
+
return json.loads(response_text) # Try parsing directly if no code block
|
65 |
+
except json.JSONDecodeError as e:
|
66 |
+
logger.error(f"Failed to decode JSON: {e} from response: {response_text}")
|
67 |
+
raise
|
68 |
+
|
69 |
+
# Node 9:plan with exact count of the opcode used per logic
|
70 |
+
def plan_opcode_counter_node(state: Dict[str, Any]) -> Dict[str, Any]:
|
71 |
+
"""
|
72 |
+
For each plan in state["action_plan"]["action_overall_flow"], calls the LLM agent
|
73 |
+
to analyze the `logic` string and return a list of {opcode, count} for each category.
|
74 |
+
"""
|
75 |
+
logger.info("=== Running OPCODE COUTER LOGIC with LLM counts ===")
|
76 |
+
game_description = state.get("description", "No game description provided.")
|
77 |
+
sprite_name = {target["name"]: target["name"] for target in state["project_json"]["targets"]} # Adjusted for direct use
|
78 |
+
|
79 |
+
action_flow = state.get("action_plan", {}).get("action_overall_flow", {})
|
80 |
+
if not action_flow:
|
81 |
+
logger.warning("No action_overall_flow found; skipping.")
|
82 |
+
return state
|
83 |
+
|
84 |
+
# Prepare block reference strings for the prompt
|
85 |
+
hat_description = "Blocks that start a script when an event happens."
|
86 |
+
hat_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in hat_block_data])
|
87 |
+
|
88 |
+
boolean_description = "Blocks that report a true or false value and fit into hexagonal inputs."
|
89 |
+
boolean_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in boolean_block_data])
|
90 |
+
|
91 |
+
c_description = "Blocks that run scripts inside them repeatedly or conditionally."
|
92 |
+
c_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in c_block_data])
|
93 |
+
|
94 |
+
cap_description = "Blocks that end a script."
|
95 |
+
cap_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in cap_block_data])
|
96 |
+
|
97 |
+
reporter_description = "Blocks that report a value (number or string) and fit into rounded inputs."
|
98 |
+
reporter_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in reporter_block_data])
|
99 |
+
|
100 |
+
stack_description = "Blocks that perform a main action in a script."
|
101 |
+
stack_opcodes_functionalities = "\n".join([f"- {block['opcode']}: {block['text']}" for block in stack_block_data])
|
102 |
+
|
103 |
+
refined_flow: Dict[str, Any] = {}
|
104 |
+
for sprite, sprite_data in action_flow.items(): # Use .items() for direct iteration
|
105 |
+
refined_plans = []
|
106 |
+
for plan in sprite_data.get("plans", []):
|
107 |
+
logic = plan.get("logic", "")
|
108 |
+
event = plan.get("event", "")
|
109 |
+
|
110 |
+
# This is where the core change for counting opcodes will happen.
|
111 |
+
# We will use the 'logic' string to determine the actual opcodes and their counts.
|
112 |
+
opcode_counts = {
|
113 |
+
"motion": [],
|
114 |
+
"control": [],
|
115 |
+
"operator": [],
|
116 |
+
"sensing": [],
|
117 |
+
"looks": [],
|
118 |
+
"sounds": [],
|
119 |
+
"events": [],
|
120 |
+
"data": [],
|
121 |
+
}
|
122 |
+
|
123 |
+
# Initialize a dictionary to hold counts for each opcode
|
124 |
+
temp_opcode_counts = {}
|
125 |
+
|
126 |
+
# Add the event block explicitly
|
127 |
+
if event:
|
128 |
+
event_opcode = event.replace('v', '').strip() # Clean the event string
|
129 |
+
temp_opcode_counts[event_opcode] = temp_opcode_counts.get(event_opcode, 0) + 1
|
130 |
+
|
131 |
+
|
132 |
+
# Iterate through all known opcodes and check if their 'text' appears in the logic
|
133 |
+
for block_info in all_opcodes_list:
|
134 |
+
opcode = block_info["opcode"]
|
135 |
+
# Use a more robust regex for matching, accounting for variable names or block inputs
|
136 |
+
# We need to be careful with common words that are also part of opcodes, e.g., "if"
|
137 |
+
# A more robust solution might involve parsing the Scratch-like logic more deeply.
|
138 |
+
# For now, let's try to match the "text" from the block definition.
|
139 |
+
# Escape special characters in the block text for regex
|
140 |
+
block_text_escaped = re.escape(block_info["text"])
|
141 |
+
|
142 |
+
# Replace placeholders like [key], [object], ( ) with regex wildcards
|
143 |
+
block_text_pattern = block_text_escaped.replace(r"\[key\]", r".*?").replace(r"\[message\]", r".*?").replace(r"\[object\]", r".*?").replace(r"\( \)", r".*?")
|
144 |
+
block_text_pattern = block_text_pattern.replace(r"\[variable\]", r".*?")
|
145 |
+
|
146 |
+
# For blocks that might have variations in text (e.g., if-then, if-then-else)
|
147 |
+
if opcode == "control_if":
|
148 |
+
if_regex = r"if <.+?> then"
|
149 |
+
if_else_regex = r"if <.+?> then\n.*else"
|
150 |
+
|
151 |
+
if re.search(if_else_regex, logic, re.DOTALL):
|
152 |
+
temp_opcode_counts["control_if_else"] = temp_opcode_counts.get("control_if_else", 0) + 1
|
153 |
+
elif re.search(if_regex, logic, re.DOTALL):
|
154 |
+
temp_opcode_counts["control_if"] = temp_opcode_counts.get("control_if", 0) + 1
|
155 |
+
continue # Skip general matching for control_if
|
156 |
+
|
157 |
+
if opcode == "control_forever" and "forever" in logic:
|
158 |
+
temp_opcode_counts[opcode] = temp_opcode_counts.get(opcode, 0) + 1
|
159 |
+
continue # Skip general matching
|
160 |
+
|
161 |
+
# General regex match for other blocks
|
162 |
+
# We need to make sure we're not just matching substrings of other blocks
|
163 |
+
# A simple word boundary or line-by-line check might be better
|
164 |
+
# For now, a simple count of occurrences of the "text" within the logic
|
165 |
+
# will be used, but this is a simplification.
|
166 |
+
count = len(re.findall(block_text_pattern, logic))
|
167 |
+
if count > 0:
|
168 |
+
temp_opcode_counts[opcode] = temp_opcode_counts.get(opcode, 0) + count
|
169 |
+
|
170 |
+
# Fill the opcode_counts for each category based on temp_opcode_counts
|
171 |
+
def add_to_category(category_list, opcode_name, count):
|
172 |
+
if count > 0:
|
173 |
+
category_list.append({"opcode": opcode_name, "count": count})
|
174 |
+
|
175 |
+
for opcode, count in temp_opcode_counts.items():
|
176 |
+
if opcode.startswith("motion_"):
|
177 |
+
add_to_category(opcode_counts["motion"], opcode, count)
|
178 |
+
elif opcode.startswith("control_"):
|
179 |
+
add_to_category(opcode_counts["control"], opcode, count)
|
180 |
+
elif opcode.startswith("operator_"):
|
181 |
+
add_to_category(opcode_counts["operator"], opcode, count)
|
182 |
+
elif opcode.startswith("sensing_"):
|
183 |
+
add_to_category(opcode_counts["sensing"], opcode, count)
|
184 |
+
elif opcode.startswith("looks_"):
|
185 |
+
add_to_category(opcode_counts["looks"], opcode, count)
|
186 |
+
elif opcode.startswith("sounds_"):
|
187 |
+
add_to_category(opcode_counts["sounds"], opcode, count)
|
188 |
+
elif opcode.startswith("event_"):
|
189 |
+
add_to_category(opcode_counts["events"], opcode, count)
|
190 |
+
elif opcode.startswith("data_"):
|
191 |
+
add_to_category(opcode_counts["data"], opcode, count)
|
192 |
+
|
193 |
+
# Assign the new opcode_counts to the plan
|
194 |
+
plan["opcode_counts"] = opcode_counts
|
195 |
+
|
196 |
+
# The original plan structure also had categories as direct keys.
|
197 |
+
# You can choose to keep this or remove it, depending on your downstream needs.
|
198 |
+
# If you want to keep it, you'd populate them based on opcode_counts.
|
199 |
+
# For simplicity, let's keep the new 'opcode_counts' key as requested.
|
200 |
+
|
201 |
+
# Clear previous lists if you are relying solely on 'opcode_counts'
|
202 |
+
plan["motion"] = []
|
203 |
+
plan["control"] = []
|
204 |
+
plan["operator"] = []
|
205 |
+
plan["sensing"] = []
|
206 |
+
plan["looks"] = []
|
207 |
+
plan["sounds"] = []
|
208 |
+
plan["events"] = []
|
209 |
+
plan["data"] = []
|
210 |
+
|
211 |
+
# Populate the individual lists based on the newly calculated opcode_counts if needed
|
212 |
+
for category, opcodes_list in opcode_counts.items():
|
213 |
+
for item in opcodes_list:
|
214 |
+
# Append just the opcode string to the category list
|
215 |
+
plan[category].extend([item['opcode']] * item['count'])
|
216 |
+
|
217 |
+
|
218 |
+
refined_plans.append(plan)
|
219 |
+
|
220 |
+
refined_flow[sprite] = {
|
221 |
+
"description": sprite_data.get("description", ""),
|
222 |
+
"plans": refined_plans
|
223 |
+
}
|
224 |
+
|
225 |
+
state["temporary_node"] = refined_flow
|
226 |
+
print(f"[OPCODE COUTER LOGIC]: {refined_flow}")
|
227 |
+
logger.info("=== OPCODE COUTER LOGIC completed ===")
|
228 |
+
return state
|
v2/utils/opcode_occurrence.py
ADDED
@@ -0,0 +1,981 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import copy
|
3 |
+
import re
|
4 |
+
import re
|
5 |
+
from collections import defaultdict
|
6 |
+
#from opcode_occurrence import generated_output_json
|
7 |
+
|
8 |
+
|
9 |
+
def generate_blocks_from_opcodes(opcode_counts, all_block_definitions):
|
10 |
+
"""
|
11 |
+
Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition,
|
12 |
+
and groups all generated block keys by their corresponding opcode.
|
13 |
+
|
14 |
+
Returns:
|
15 |
+
tuple: (generated_blocks, opcode_to_keys)
|
16 |
+
- generated_blocks: dict of block_key -> block_data
|
17 |
+
- opcode_to_keys: dict of opcode -> list of block_keys
|
18 |
+
"""
|
19 |
+
generated_blocks = {}
|
20 |
+
opcode_counts_map = {} # For counting unique suffix per opcode
|
21 |
+
opcode_to_keys = {} # For grouping block keys by opcode
|
22 |
+
|
23 |
+
explicit_menu_links = {
|
24 |
+
"motion_goto": [("TO", "motion_goto_menu")],
|
25 |
+
"motion_glideto": [("TO", "motion_glideto_menu")],
|
26 |
+
"motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
|
27 |
+
"sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
|
28 |
+
"sensing_of": [("OBJECT", "sensing_of_object_menu")],
|
29 |
+
"sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
|
30 |
+
"control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
|
31 |
+
"sound_play": [("SOUND_MENU", "sound_sounds_menu")],
|
32 |
+
"sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
|
33 |
+
"looks_switchcostumeto": [("COSTUME", "looks_costume")],
|
34 |
+
"looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
|
35 |
+
}
|
36 |
+
|
37 |
+
for item in opcode_counts:
|
38 |
+
opcode = item.get("opcode")
|
39 |
+
count = item.get("count", 1)
|
40 |
+
|
41 |
+
if opcode == "sensing_istouching":
|
42 |
+
opcode = "sensing_touchingobject"
|
43 |
+
|
44 |
+
if not opcode or opcode not in all_block_definitions:
|
45 |
+
print(f"Warning: Skipping opcode '{opcode}' (missing or not in definitions).")
|
46 |
+
continue
|
47 |
+
|
48 |
+
for _ in range(count):
|
49 |
+
# Count occurrences per opcode for unique key generation
|
50 |
+
opcode_counts_map[opcode] = opcode_counts_map.get(opcode, 0) + 1
|
51 |
+
instance_num = opcode_counts_map[opcode]
|
52 |
+
main_key = f"{opcode}_{instance_num}"
|
53 |
+
|
54 |
+
# Track the generated key
|
55 |
+
opcode_to_keys.setdefault(opcode, []).append(main_key)
|
56 |
+
|
57 |
+
main_block_data = copy.deepcopy(all_block_definitions[opcode])
|
58 |
+
main_block_data["parent"] = None
|
59 |
+
main_block_data["next"] = None
|
60 |
+
main_block_data["topLevel"] = True
|
61 |
+
main_block_data["shadow"] = False
|
62 |
+
|
63 |
+
generated_blocks[main_key] = main_block_data
|
64 |
+
|
65 |
+
# Handle menus
|
66 |
+
if opcode in explicit_menu_links:
|
67 |
+
for input_name, menu_opcode in explicit_menu_links[opcode]:
|
68 |
+
if menu_opcode not in all_block_definitions:
|
69 |
+
continue
|
70 |
+
|
71 |
+
opcode_counts_map[menu_opcode] = opcode_counts_map.get(menu_opcode, 0) + 1
|
72 |
+
menu_instance_num = opcode_counts_map[menu_opcode]
|
73 |
+
menu_key = f"{menu_opcode}_{menu_instance_num}"
|
74 |
+
|
75 |
+
opcode_to_keys.setdefault(menu_opcode, []).append(menu_key)
|
76 |
+
|
77 |
+
menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode])
|
78 |
+
menu_block_data["shadow"] = True
|
79 |
+
menu_block_data["topLevel"] = False
|
80 |
+
menu_block_data["next"] = None
|
81 |
+
menu_block_data["parent"] = main_key
|
82 |
+
|
83 |
+
if input_name in main_block_data.get("inputs", {}) and \
|
84 |
+
isinstance(main_block_data["inputs"][input_name], list) and \
|
85 |
+
len(main_block_data["inputs"][input_name]) > 1 and \
|
86 |
+
main_block_data["inputs"][input_name][0] == 1:
|
87 |
+
|
88 |
+
main_block_data["inputs"][input_name][1] = menu_key
|
89 |
+
|
90 |
+
generated_blocks[menu_key] = menu_block_data
|
91 |
+
|
92 |
+
return generated_blocks, opcode_to_keys
|
93 |
+
|
94 |
+
all_block_definitions = {
|
95 |
+
# motion_block.json
|
96 |
+
"motion_movesteps": {
|
97 |
+
"block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps",
|
98 |
+
"functionality": "Moves the sprite forward by the specified number of steps in the direction it is currently facing. A positive value moves it forward, and a negative value moves it backward.",
|
99 |
+
"inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
100 |
+
},
|
101 |
+
"motion_turnright": {
|
102 |
+
"block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright",
|
103 |
+
"functionality": "Turns the sprite clockwise by the specified number of degrees.",
|
104 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True
|
105 |
+
},
|
106 |
+
"motion_turnleft": {
|
107 |
+
"block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft",
|
108 |
+
"functionality": "Turns the sprite counter-clockwise by the specified number of degrees.",
|
109 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True
|
110 |
+
},
|
111 |
+
"motion_goto": {
|
112 |
+
"block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto",
|
113 |
+
"functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.",
|
114 |
+
"inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
115 |
+
},
|
116 |
+
"motion_goto_menu": {
|
117 |
+
"block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu",
|
118 |
+
"functionality": "Menu for go to block.",
|
119 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
120 |
+
},
|
121 |
+
"motion_gotoxy": {
|
122 |
+
"block_name": "go to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_gotoxy",
|
123 |
+
"functionality": "Moves the sprite to the specified X and Y coordinates on the stage.",
|
124 |
+
"inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
125 |
+
},
|
126 |
+
"motion_glideto": {
|
127 |
+
"block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto",
|
128 |
+
"functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.",
|
129 |
+
"inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
130 |
+
},
|
131 |
+
"motion_glideto_menu": {
|
132 |
+
"block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu",
|
133 |
+
"functionality": "Menu for glide to block.",
|
134 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
135 |
+
},
|
136 |
+
"motion_glidesecstoxy": {
|
137 |
+
"block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy",
|
138 |
+
"functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.",
|
139 |
+
"inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
140 |
+
},
|
141 |
+
"motion_pointindirection": {
|
142 |
+
"block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection",
|
143 |
+
"functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).",
|
144 |
+
"inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True
|
145 |
+
},
|
146 |
+
"motion_pointtowards": {
|
147 |
+
"block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards",
|
148 |
+
"functionality": "Points the sprite towards the mouse pointer or another specified sprite.",
|
149 |
+
"inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
150 |
+
},
|
151 |
+
"motion_pointtowards_menu": {
|
152 |
+
"block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu",
|
153 |
+
"functionality": "Menu for point towards block.",
|
154 |
+
"inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
155 |
+
},
|
156 |
+
"motion_changexby": {
|
157 |
+
"block_name": "change x by ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_changexby",
|
158 |
+
"functionality": "Changes the sprite's X-coordinate by the specified amount, moving it horizontally.",
|
159 |
+
"inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
160 |
+
},
|
161 |
+
"motion_setx": {
|
162 |
+
"block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx",
|
163 |
+
"functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.",
|
164 |
+
"inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
165 |
+
},
|
166 |
+
"motion_changeyby": {
|
167 |
+
"block_name": "change y by ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_changeyby",
|
168 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
169 |
+
"inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
170 |
+
},
|
171 |
+
"motion_sety": {
|
172 |
+
"block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety",
|
173 |
+
"functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.",
|
174 |
+
"inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
175 |
+
},
|
176 |
+
"motion_ifonedgebounce": {
|
177 |
+
"block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce",
|
178 |
+
"functionality": "Reverses the sprite's direction if it touches the edge of the stage.",
|
179 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
180 |
+
},
|
181 |
+
"motion_setrotationstyle": {
|
182 |
+
"block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle",
|
183 |
+
"functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).",
|
184 |
+
"inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True
|
185 |
+
},
|
186 |
+
"motion_xposition": {
|
187 |
+
"block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition",
|
188 |
+
"functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]",
|
189 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
190 |
+
},
|
191 |
+
"motion_yposition": {
|
192 |
+
"block_name": "(y position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_yposition",
|
193 |
+
"functionality": "Reports the current Y coordinate of the sprite on the stage.[NOTE: not used in stage/backdrops]",
|
194 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
195 |
+
},
|
196 |
+
"motion_direction": {
|
197 |
+
"block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction",
|
198 |
+
"functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]",
|
199 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
200 |
+
},
|
201 |
+
|
202 |
+
# control_block.json
|
203 |
+
"control_wait": {
|
204 |
+
"block_name": "wait () seconds", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait",
|
205 |
+
"functionality": "Pauses the script for a specified duration.",
|
206 |
+
"inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True
|
207 |
+
},
|
208 |
+
"control_repeat": {
|
209 |
+
"block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat",
|
210 |
+
"functionality": "Repeats the blocks inside it a specified number of times.",
|
211 |
+
"inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
212 |
+
},
|
213 |
+
"control_forever": {
|
214 |
+
"block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever",
|
215 |
+
"functionality": "Continuously runs the blocks inside it.",
|
216 |
+
"inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
217 |
+
},
|
218 |
+
"control_if": {
|
219 |
+
"block_name": "if <> then", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if",
|
220 |
+
"functionality": "Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]",
|
221 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
222 |
+
},
|
223 |
+
"control_if_else": {
|
224 |
+
"block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else",
|
225 |
+
"functionality": "Executes one set of blocks if the specified boolean condition is true, and a different set of blocks if the condition is false. [NOTE: it takes boolean blocks as input]",
|
226 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
227 |
+
},
|
228 |
+
"control_wait_until": {
|
229 |
+
"block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until",
|
230 |
+
"functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
231 |
+
"inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
232 |
+
},
|
233 |
+
"control_repeat_until": {
|
234 |
+
"block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until",
|
235 |
+
"functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
236 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
237 |
+
},
|
238 |
+
"control_stop": {
|
239 |
+
"block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop",
|
240 |
+
"functionality": "Halts all scripts, only the current script, or other scripts within the same sprite. Its shape can dynamically change based on the selected option.",
|
241 |
+
"inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"}
|
242 |
+
},
|
243 |
+
"control_start_as_clone": {
|
244 |
+
"block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone",
|
245 |
+
"functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.",
|
246 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
247 |
+
},
|
248 |
+
"control_create_clone_of": {
|
249 |
+
"block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of",
|
250 |
+
"functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).",
|
251 |
+
"inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
252 |
+
},
|
253 |
+
"control_create_clone_of_menu": {
|
254 |
+
"block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu",
|
255 |
+
"functionality": "Menu for create clone of block.",
|
256 |
+
"inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False
|
257 |
+
},
|
258 |
+
"control_delete_this_clone": {
|
259 |
+
"block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone",
|
260 |
+
"functionality": "Removes the clone that is executing it from the stage.",
|
261 |
+
"inputs":None, "fields": {}, "shadow": False, "topLevel": True
|
262 |
+
},
|
263 |
+
|
264 |
+
# data_block.json
|
265 |
+
"data_setvariableto": {
|
266 |
+
"block_name": "set [my variable v] to ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_setvariableto",
|
267 |
+
"functionality": "Assigns a specific value (number, string, or boolean) to a variable.",
|
268 |
+
"inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
269 |
+
},
|
270 |
+
"data_changevariableby": {
|
271 |
+
"block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby",
|
272 |
+
"functionality": "Increases or decreases a variable's numerical value by a specified amount.",
|
273 |
+
"inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
274 |
+
},
|
275 |
+
"data_showvariable": {
|
276 |
+
"block_name": "show variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showvariable",
|
277 |
+
"functionality": "Makes a variable's monitor visible on the stage.",
|
278 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
279 |
+
},
|
280 |
+
"data_hidevariable": {
|
281 |
+
"block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable",
|
282 |
+
"functionality": "Hides a variable's monitor from the stage.",
|
283 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
284 |
+
},
|
285 |
+
"data_addtolist": {
|
286 |
+
"block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist",
|
287 |
+
"functionality": "Appends an item to the end of a list.",
|
288 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
289 |
+
},
|
290 |
+
"data_deleteoflist": {
|
291 |
+
"block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist",
|
292 |
+
"functionality": "Removes an item from a list by its index or by selecting 'all' items.",
|
293 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
294 |
+
},
|
295 |
+
"data_deletealloflist": {
|
296 |
+
"block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist",
|
297 |
+
"functionality": "Removes all items from a list.",
|
298 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
299 |
+
},
|
300 |
+
"data_insertatlist": {
|
301 |
+
"block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist",
|
302 |
+
"functionality": "Inserts an item at a specific position within a list.",
|
303 |
+
"inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
304 |
+
},
|
305 |
+
"data_replaceitemoflist": {
|
306 |
+
"block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist",
|
307 |
+
"functionality": "Replaces an item at a specific position in a list with a new value.",
|
308 |
+
"inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
309 |
+
},
|
310 |
+
"data_itemoflist": {
|
311 |
+
"block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist",
|
312 |
+
"functionality": "Reports the item located at a specific position in a list.",
|
313 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
314 |
+
},
|
315 |
+
"data_itemnumoflist": {
|
316 |
+
"block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist",
|
317 |
+
"functionality": "Reports the index number of the first occurrence of a specified item in a list. If the item is not found, it reports 0.",
|
318 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
319 |
+
},
|
320 |
+
"data_lengthoflist": {
|
321 |
+
"block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist",
|
322 |
+
"functionality": "Provides the total number of items contained in a list.",
|
323 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
324 |
+
},
|
325 |
+
"data_listcontainsitem": {
|
326 |
+
"block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem",
|
327 |
+
"functionality": "Checks if a list includes a specific item.",
|
328 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
329 |
+
},
|
330 |
+
"data_showlist": {
|
331 |
+
"block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist",
|
332 |
+
"functionality": "Makes a list's monitor visible on the stage.",
|
333 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
334 |
+
},
|
335 |
+
"data_hidelist": {
|
336 |
+
"block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist",
|
337 |
+
"functionality": "Hides a list's monitor from the stage.",
|
338 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
339 |
+
},
|
340 |
+
"data_variable": { # This is a reporter block for a variable's value
|
341 |
+
"block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable",
|
342 |
+
"functionality": "Provides the current value stored in a variable.",
|
343 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False
|
344 |
+
},
|
345 |
+
|
346 |
+
# event_block.json
|
347 |
+
"event_whenflagclicked": {
|
348 |
+
"block_name": "when green flag pressed", "block_type": "Events", "op_code": "event_whenflagclicked", "block_shape": "Hat Block",
|
349 |
+
"functionality": "This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.",
|
350 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
351 |
+
},
|
352 |
+
"event_whenkeypressed": {
|
353 |
+
"block_name": "when () key pressed", "block_type": "Events", "op_code": "event_whenkeypressed", "block_shape": "Hat Block",
|
354 |
+
"functionality": "This Hat block initiates the script when a specified keyboard key is pressed.",
|
355 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True
|
356 |
+
},
|
357 |
+
"event_whenthisspriteclicked": {
|
358 |
+
"block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block",
|
359 |
+
"functionality": "This Hat block starts the script when the sprite itself is clicked.",
|
360 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
361 |
+
},
|
362 |
+
"event_whenbackdropswitchesto": {
|
363 |
+
"block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block",
|
364 |
+
"functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.",
|
365 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True
|
366 |
+
},
|
367 |
+
"event_whengreaterthan": {
|
368 |
+
"block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block",
|
369 |
+
"functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.",
|
370 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True
|
371 |
+
},
|
372 |
+
"event_whenbroadcastreceived": {
|
373 |
+
"block_name": "when I receive ()", "block_type": "Events", "op_code": "event_whenbroadcastreceived", "block_shape": "Hat Block",
|
374 |
+
"functionality": "This Hat block initiates the script upon the reception of a specific broadcast message. This mechanism facilitates indirect communication between sprites or the stage.",
|
375 |
+
"inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True
|
376 |
+
},
|
377 |
+
"event_broadcast": {
|
378 |
+
"block_name": "broadcast ()", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcast",
|
379 |
+
"functionality": "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.",
|
380 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
381 |
+
},
|
382 |
+
"event_broadcastandwait": {
|
383 |
+
"block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait",
|
384 |
+
"functionality": "Sends a broadcast message and pauses the current script until all other scripts activated by that broadcast have completed their execution, ensuring sequential coordination.",
|
385 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
386 |
+
},
|
387 |
+
|
388 |
+
# looks_block.json
|
389 |
+
"looks_sayforsecs": {
|
390 |
+
"block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs",
|
391 |
+
"functionality": "Displays a speech bubble containing specified text for a set duration.",
|
392 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True
|
393 |
+
},
|
394 |
+
"looks_say": {
|
395 |
+
"block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say",
|
396 |
+
"functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
397 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True
|
398 |
+
},
|
399 |
+
"looks_thinkforsecs": {
|
400 |
+
"block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs",
|
401 |
+
"functionality": "Displays a thought bubble containing specified text for a set duration.",
|
402 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True
|
403 |
+
},
|
404 |
+
"looks_think": {
|
405 |
+
"block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think",
|
406 |
+
"functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
407 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True
|
408 |
+
},
|
409 |
+
"looks_switchcostumeto": {
|
410 |
+
"block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto",
|
411 |
+
"functionality": "Alters the sprite's appearance to a designated costume.",
|
412 |
+
"inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True
|
413 |
+
},
|
414 |
+
"looks_costume": {
|
415 |
+
"block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume",
|
416 |
+
"functionality": "Menu for switch costume to block.",
|
417 |
+
"inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False
|
418 |
+
},
|
419 |
+
"looks_nextcostume": {
|
420 |
+
"block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume",
|
421 |
+
"functionality": "Switches the sprite's costume to the next one in its costume list. If it's the last costume, it cycles back to the first.",
|
422 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
423 |
+
},
|
424 |
+
"looks_switchbackdropto": {
|
425 |
+
"block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto",
|
426 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop.",
|
427 |
+
"inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True
|
428 |
+
},
|
429 |
+
"looks_backdrops": {
|
430 |
+
"block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops",
|
431 |
+
"functionality": "Menu for switch backdrop to block.",
|
432 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False
|
433 |
+
},
|
434 |
+
"looks_switchbackdroptowait": {
|
435 |
+
"block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait",
|
436 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop and pauses the script until any 'When backdrop switches to' scripts for that backdrop have finished.",
|
437 |
+
"inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True
|
438 |
+
},
|
439 |
+
"looks_nextbackdrop": {
|
440 |
+
"block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop",
|
441 |
+
"functionality": "Switches the stage's backdrop to the next one in its backdrop list. If it's the last backdrop, it cycles back to the first.",
|
442 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
443 |
+
},
|
444 |
+
"looks_changesizeby": {
|
445 |
+
"block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby",
|
446 |
+
"functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.",
|
447 |
+
"inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
448 |
+
},
|
449 |
+
"looks_setsizeto": {
|
450 |
+
"block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto",
|
451 |
+
"functionality": "Sets the sprite's size to a specific percentage of its original size.",
|
452 |
+
"inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True
|
453 |
+
},
|
454 |
+
"looks_changeeffectby": {
|
455 |
+
"block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby",
|
456 |
+
"functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).",
|
457 |
+
"inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True
|
458 |
+
},
|
459 |
+
"looks_seteffectto": {
|
460 |
+
"block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto",
|
461 |
+
"functionality": "Sets a visual effect on the sprite to a specific value.",
|
462 |
+
"inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True
|
463 |
+
},
|
464 |
+
"looks_cleargraphiceffects": {
|
465 |
+
"block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects",
|
466 |
+
"functionality": "Removes all visual effects applied to the sprite.",
|
467 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
468 |
+
},
|
469 |
+
"looks_show": {
|
470 |
+
"block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show",
|
471 |
+
"functionality": "Makes the sprite visible on the stage.",
|
472 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
473 |
+
},
|
474 |
+
"looks_hide": {
|
475 |
+
"block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide",
|
476 |
+
"functionality": "Makes the sprite invisible on the stage.",
|
477 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
478 |
+
},
|
479 |
+
"looks_gotofrontback": {
|
480 |
+
"block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback",
|
481 |
+
"functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.",
|
482 |
+
"inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True
|
483 |
+
},
|
484 |
+
"looks_goforwardbackwardlayers": {
|
485 |
+
"block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers",
|
486 |
+
"functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.",
|
487 |
+
"inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True
|
488 |
+
},
|
489 |
+
"looks_costumenumbername": {
|
490 |
+
"block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername",
|
491 |
+
"functionality": "Reports the current costume's number or name.",
|
492 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True
|
493 |
+
},
|
494 |
+
"looks_backdropnumbername": {
|
495 |
+
"block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername",
|
496 |
+
"functionality": "Reports the current backdrop's number or name.",
|
497 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True
|
498 |
+
},
|
499 |
+
"looks_size": {
|
500 |
+
"block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size",
|
501 |
+
"functionality": "Reports the current size of the sprite as a percentage.",
|
502 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
503 |
+
},
|
504 |
+
|
505 |
+
# operator_block.json
|
506 |
+
"operator_add": {
|
507 |
+
"block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add",
|
508 |
+
"functionality": "Adds two numerical values.",
|
509 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
510 |
+
},
|
511 |
+
"operator_subtract": {
|
512 |
+
"block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract",
|
513 |
+
"functionality": "Subtracts the second numerical value from the first.",
|
514 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
515 |
+
},
|
516 |
+
"operator_multiply": {
|
517 |
+
"block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply",
|
518 |
+
"functionality": "Multiplies two numerical values.",
|
519 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
520 |
+
},
|
521 |
+
"operator_divide": {
|
522 |
+
"block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide",
|
523 |
+
"functionality": "Divides the first numerical value by the second.",
|
524 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
525 |
+
},
|
526 |
+
"operator_random": {
|
527 |
+
"block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random",
|
528 |
+
"functionality": "Generates a random integer within a specified inclusive range.",
|
529 |
+
"inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
530 |
+
},
|
531 |
+
"operator_gt": {
|
532 |
+
"block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt",
|
533 |
+
"functionality": "Checks if the first value is greater than the second.",
|
534 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
535 |
+
},
|
536 |
+
"operator_lt": {
|
537 |
+
"block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt",
|
538 |
+
"functionality": "Checks if the first value is less than the second.",
|
539 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
540 |
+
},
|
541 |
+
"operator_equals": {
|
542 |
+
"block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals",
|
543 |
+
"functionality": "Checks if two values are equal.",
|
544 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
545 |
+
},
|
546 |
+
"operator_and": {
|
547 |
+
"block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and",
|
548 |
+
"functionality": "Returns 'true' if both provided Boolean conditions are 'true'.",
|
549 |
+
"inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
550 |
+
},
|
551 |
+
"operator_or": {
|
552 |
+
"block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or",
|
553 |
+
"functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.",
|
554 |
+
"inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
555 |
+
},
|
556 |
+
"operator_not": {
|
557 |
+
"block_name": "<not <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not",
|
558 |
+
"functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.",
|
559 |
+
"inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
560 |
+
},
|
561 |
+
"operator_join": {
|
562 |
+
"block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join",
|
563 |
+
"functionality": "Concatenates two strings or values into a single string.",
|
564 |
+
"inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True
|
565 |
+
},
|
566 |
+
"operator_letterof": {
|
567 |
+
"block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof",
|
568 |
+
"functionality": "Reports the character at a specific numerical position within a string.",
|
569 |
+
"inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True
|
570 |
+
},
|
571 |
+
"operator_length": {
|
572 |
+
"block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length",
|
573 |
+
"functionality": "Reports the total number of characters in a given string.",
|
574 |
+
"inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True
|
575 |
+
},
|
576 |
+
"operator_contains": {
|
577 |
+
"block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains",
|
578 |
+
"functionality": "Checks if one string contains another string.",
|
579 |
+
"inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
580 |
+
},
|
581 |
+
"operator_mod": {
|
582 |
+
"block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod",
|
583 |
+
"functionality": "Reports the remainder when the first number is divided by the second.",
|
584 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
585 |
+
},
|
586 |
+
"operator_round": {
|
587 |
+
"block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round",
|
588 |
+
"functionality": "Rounds a numerical value to the nearest integer.",
|
589 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
590 |
+
},
|
591 |
+
"operator_mathop": {
|
592 |
+
"block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop",
|
593 |
+
"functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).",
|
594 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True
|
595 |
+
},
|
596 |
+
|
597 |
+
# sensing_block.json
|
598 |
+
"sensing_touchingobject": {
|
599 |
+
"block_name": "<touching [edge v]?>", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block",
|
600 |
+
"functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.",
|
601 |
+
"inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True
|
602 |
+
},
|
603 |
+
"sensing_touchingobjectmenu": {
|
604 |
+
"block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu",
|
605 |
+
"functionality": "Menu for touching object block.",
|
606 |
+
"inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
607 |
+
},
|
608 |
+
"sensing_touchingcolor": {
|
609 |
+
"block_name": "<touching color ()?>", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block",
|
610 |
+
"functionality": "Checks whether its sprite is touching a specified color.",
|
611 |
+
"inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True
|
612 |
+
},
|
613 |
+
"sensing_coloristouchingcolor": {
|
614 |
+
"block_name": "<color () is touching ()?>", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block",
|
615 |
+
"functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.",
|
616 |
+
"inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True
|
617 |
+
},
|
618 |
+
"sensing_askandwait": {
|
619 |
+
"block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait",
|
620 |
+
"functionality": "Displays an input box with specified text at the bottom of the screen, allowing users to input text, which is stored in the 'Answer' block.",
|
621 |
+
"inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True
|
622 |
+
},
|
623 |
+
"sensing_answer": {
|
624 |
+
"block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer",
|
625 |
+
"functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.",
|
626 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
627 |
+
},
|
628 |
+
"sensing_keypressed": {
|
629 |
+
"block_name": "<key () pressed?>", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block",
|
630 |
+
"functionality": "Checks if a specified keyboard key is currently being pressed.",
|
631 |
+
"inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True
|
632 |
+
},
|
633 |
+
"sensing_keyoptions": {
|
634 |
+
"block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions",
|
635 |
+
"functionality": "Menu for key pressed block.",
|
636 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False
|
637 |
+
},
|
638 |
+
"sensing_mousedown": {
|
639 |
+
"block_name": "<mouse down?>", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block",
|
640 |
+
"functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.",
|
641 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
642 |
+
},
|
643 |
+
"sensing_mousex": {
|
644 |
+
"block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex",
|
645 |
+
"functionality": "Reports the mouse-pointer’s current X position on the stage.",
|
646 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
647 |
+
},
|
648 |
+
"sensing_mousey": {
|
649 |
+
"block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey",
|
650 |
+
"functionality": "Reports the mouse-pointer’s current Y position on the stage.",
|
651 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
652 |
+
},
|
653 |
+
"sensing_setdragmode": {
|
654 |
+
"block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode",
|
655 |
+
"functionality": "Sets whether the sprite can be dragged by the mouse on the stage.",
|
656 |
+
"inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True
|
657 |
+
},
|
658 |
+
"sensing_loudness": {
|
659 |
+
"block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness",
|
660 |
+
"functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.",
|
661 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
662 |
+
},
|
663 |
+
"sensing_timer": {
|
664 |
+
"block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer",
|
665 |
+
"functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.",
|
666 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
667 |
+
},
|
668 |
+
"sensing_resettimer": {
|
669 |
+
"block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer",
|
670 |
+
"functionality": "Sets the timer’s value back to 0.0.",
|
671 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
672 |
+
},
|
673 |
+
"sensing_of": {
|
674 |
+
"block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of",
|
675 |
+
"functionality": "Reports a specified value (e.g., x position, direction, costume number) of a specified sprite or the Stage to be accessed in current sprite or stage.",
|
676 |
+
"inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True
|
677 |
+
},
|
678 |
+
"sensing_of_object_menu": {
|
679 |
+
"block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu",
|
680 |
+
"functionality": "Menu for of block.",
|
681 |
+
"inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False
|
682 |
+
},
|
683 |
+
"sensing_current": {
|
684 |
+
"block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current",
|
685 |
+
"functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.",
|
686 |
+
"inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True
|
687 |
+
},
|
688 |
+
"sensing_dayssince2000": {
|
689 |
+
"block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000",
|
690 |
+
"functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.",
|
691 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
692 |
+
},
|
693 |
+
"sensing_username": {
|
694 |
+
"block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username",
|
695 |
+
"functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.",
|
696 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
697 |
+
},
|
698 |
+
|
699 |
+
# sound_block.json
|
700 |
+
"sound_playuntildone": {
|
701 |
+
"block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone",
|
702 |
+
"functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.",
|
703 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
704 |
+
},
|
705 |
+
"sound_sounds_menu": {
|
706 |
+
"block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu",
|
707 |
+
"functionality": "Menu for sound blocks.",
|
708 |
+
"inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False
|
709 |
+
},
|
710 |
+
"sound_play": {
|
711 |
+
"block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play",
|
712 |
+
"functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.",
|
713 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
714 |
+
},
|
715 |
+
"sound_stopallsounds": {
|
716 |
+
"block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds",
|
717 |
+
"functionality": "Stops all currently playing sounds.",
|
718 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
719 |
+
},
|
720 |
+
"sound_changeeffectby": {
|
721 |
+
"block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby",
|
722 |
+
"functionality": "Changes the project's sound effect by a specified amount.",
|
723 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True
|
724 |
+
},
|
725 |
+
"sound_seteffectto": {
|
726 |
+
"block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto",
|
727 |
+
"functionality": "Sets the sound effect to a specific value.",
|
728 |
+
"inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True
|
729 |
+
},
|
730 |
+
"sound_cleareffects": {
|
731 |
+
"block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects",
|
732 |
+
"functionality": "Removes all sound effects applied to the sprite.",
|
733 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
734 |
+
},
|
735 |
+
"sound_changevolumeby": {
|
736 |
+
"block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby",
|
737 |
+
"functionality": "Changes the project's sound volume by a specified amount.",
|
738 |
+
"inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
739 |
+
},
|
740 |
+
"sound_setvolumeto": {
|
741 |
+
"block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto",
|
742 |
+
"functionality": "Sets the sound volume to a specific percentage (0-100).",
|
743 |
+
"inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True
|
744 |
+
},
|
745 |
+
"sound_volume": {
|
746 |
+
"block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume",
|
747 |
+
"functionality": "Reports the current volume level of the sprite.",
|
748 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
749 |
+
},
|
750 |
+
}
|
751 |
+
|
752 |
+
# # Example input with opcodes for the initial generation
|
753 |
+
initial_opcode_counts = [
|
754 |
+
{"opcode":"event_whenflagclicked","count":1},
|
755 |
+
{"opcode":"motion_gotoxy","count":1},
|
756 |
+
{"opcode":"motion_glidesecstoxy","count":1},
|
757 |
+
{"opcode":"motion_xposition","count":1},
|
758 |
+
{"opcode":"motion_setx","count":1},
|
759 |
+
{"opcode":"control_forever","count":1},
|
760 |
+
{"opcode":"control_if","count":1},
|
761 |
+
{"opcode":"control_stop","count":1},
|
762 |
+
{"opcode":"operator_lt","count":1},
|
763 |
+
{"opcode":"sensing_touchingobject","count":1}, # Changed from sensing_istouching
|
764 |
+
{"opcode":"sensing_touchingobjectmenu","count":1},
|
765 |
+
{"opcode":"event_broadcast","count":1},
|
766 |
+
{"opcode":"data_setvariableto","count":2},
|
767 |
+
{"opcode":"data_showvariable","count":2},
|
768 |
+
]
|
769 |
+
|
770 |
+
# initial_opcode_counts = [
|
771 |
+
# {"opcode": "sound_play", "count": 2},
|
772 |
+
# {"opcode": "sound_playuntildone", "count": 2},
|
773 |
+
# {"opcode":"motion_goto","count":2},
|
774 |
+
# {"opcode":"motion_glideto","count":2},
|
775 |
+
# {"opcode":"looks_switchbackdropto","count":2},
|
776 |
+
# {"opcode":"looks_switchcostumeto","count":2},
|
777 |
+
# {"opcode":"control_create_clone_of","count":2},
|
778 |
+
# {"opcode":"sensing_touchingobject","count":2},
|
779 |
+
# {"opcode":"sensing_of","count":2},
|
780 |
+
# {"opcode":"sensing_keypressed","count":2},
|
781 |
+
# {"opcode":"motion_pointtowards","count":2},
|
782 |
+
# ]
|
783 |
+
|
784 |
+
# Generate the initial blocks and get the opcode_occurrences
|
785 |
+
generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions)
|
786 |
+
#print(generated_output_json)
|
787 |
+
#print(initial_opcode_occurrences)
|
788 |
+
|
789 |
+
|
790 |
+
def build_block_patterns(def_list):
|
791 |
+
"""
|
792 |
+
Given a list of block definitions (from one JSON file),
|
793 |
+
return a dict opcode -> { regex:Compiled, inputs:[names], shape, block_name }.
|
794 |
+
"""
|
795 |
+
out = {}
|
796 |
+
for d in def_list:
|
797 |
+
# 1) escape the literal text
|
798 |
+
pattern = re.escape(d['block_name'])
|
799 |
+
# 2) replace the escaped "()" placeholders with a capture for anything inside
|
800 |
+
pattern = pattern.replace(r"\(\)", r"\((.+?)\)")
|
801 |
+
# 3) replace the escaped "<>" placeholders likewise
|
802 |
+
pattern = pattern.replace(r"\<\>", r"<(.+?)>")
|
803 |
+
# 4) allow any whitespace where spaces occur
|
804 |
+
pattern = pattern.replace(r"\ ", r"\s+")
|
805 |
+
# anchor from start to end, ignore case
|
806 |
+
regex = re.compile(r"^\s*" + pattern + r"\s*$", re.IGNORECASE)
|
807 |
+
|
808 |
+
inputs = [inp['name'] for inp in (d.get('inputs') or [])]
|
809 |
+
out[d['op_code']] = {
|
810 |
+
'regex': regex,
|
811 |
+
'inputs': inputs,
|
812 |
+
'shape': d['block_shape'],
|
813 |
+
'block_name':d['block_name'],
|
814 |
+
'definition':d
|
815 |
+
}
|
816 |
+
return out
|
817 |
+
|
818 |
+
def load_all_definitions():
|
819 |
+
# Load your JSON files here; adjust paths as needed
|
820 |
+
hats = json.load(open(r'blocks\hat_blocks.json'))['blocks']
|
821 |
+
c_blocks = json.load(open(r'blocks\c_blocks.json'))['blocks']
|
822 |
+
reporters = json.load(open(r'blocks\reporter_blocks.json'))['blocks']
|
823 |
+
booleans = json.load(open(r'blocks\boolean_blocks.json'))['blocks']
|
824 |
+
# you can also load stack_blocks.json, cap_blocks.json, etc. if you have them
|
825 |
+
merged = hats + c_blocks + reporters + booleans
|
826 |
+
return build_block_patterns(merged)
|
827 |
+
|
828 |
+
def generate_plan(generated_input, opcode_keys, pseudo_code):
|
829 |
+
"""
|
830 |
+
A truly generic plan generator.
|
831 |
+
Inputs:
|
832 |
+
- generated_input: dict of block_key -> block_data
|
833 |
+
- opcode_keys: dict of opcode -> [block_key,...]
|
834 |
+
- pseudo_code: multiline string
|
835 |
+
Returns: { flow: [...] }
|
836 |
+
"""
|
837 |
+
all_defs = load_all_definitions()
|
838 |
+
# pointer into each opcode_keys list
|
839 |
+
ptrs = defaultdict(int)
|
840 |
+
def pick_key(opcode):
|
841 |
+
lst = opcode_keys.get(opcode, [])
|
842 |
+
if ptrs[opcode] >= len(lst):
|
843 |
+
raise KeyError(f"No more generated keys for opcode {opcode!r}")
|
844 |
+
key = lst[ptrs[opcode]]
|
845 |
+
ptrs[opcode] += 1
|
846 |
+
return key
|
847 |
+
|
848 |
+
# Recursively parse an expression fragment like "(x position)" or "<touching [A]?>"
|
849 |
+
def parse_expression(expr_text):
|
850 |
+
expr_text = expr_text.strip()
|
851 |
+
# Try to match every reporter/boolean pattern
|
852 |
+
for op, info in all_defs.items():
|
853 |
+
m = info['regex'].match(expr_text)
|
854 |
+
if not m: continue
|
855 |
+
# Got a match
|
856 |
+
node = {'op_code': op, 'inputs': {}}
|
857 |
+
groups = m.groups()
|
858 |
+
for name, val in zip(info['inputs'], groups):
|
859 |
+
# decide kind by input type in definition
|
860 |
+
inp_def = next(
|
861 |
+
(i for i in info['definition'].get('inputs', []) if i['name']==name),
|
862 |
+
{}
|
863 |
+
)
|
864 |
+
t = inp_def.get('type','any')
|
865 |
+
if t in ('number','any'):
|
866 |
+
try:
|
867 |
+
node['inputs'][name] = {'kind':'value', 'value': float(val) if '.' in val else int(val)}
|
868 |
+
except:
|
869 |
+
node['inputs'][name] = {'kind':'variable', 'name': val.strip()}
|
870 |
+
elif t in ('dropdown','string','string/number'):
|
871 |
+
node['inputs'][name] = {'kind':'menu', 'option': val.strip()}
|
872 |
+
elif t=='boolean':
|
873 |
+
# nested boolean: recurse
|
874 |
+
node['inputs'][name] = {'kind':'nested', 'expr': parse_expression(val)}
|
875 |
+
else:
|
876 |
+
node['inputs'][name] = {'kind':'nested', 'expr': parse_expression(val)}
|
877 |
+
return node
|
878 |
+
# fallback: literal?
|
879 |
+
lit = re.match(r"^\(?\s*(-?\d+(\.\d+)?)\s*\)?$", expr_text)
|
880 |
+
if lit:
|
881 |
+
num = lit.group(1)
|
882 |
+
return {'literal': True, 'kind':'value', 'value': float(num) if '.' in num else int(num)}
|
883 |
+
# else treat as raw string
|
884 |
+
return {'literal':True, 'kind':'variable', 'name':expr_text}
|
885 |
+
|
886 |
+
flow = []
|
887 |
+
# stack of (indent, container)
|
888 |
+
stack = [(-1, flow)]
|
889 |
+
|
890 |
+
for raw in pseudo_code.splitlines():
|
891 |
+
if not raw.strip(): continue
|
892 |
+
indent = (len(raw) - len(raw.lstrip())) // 2
|
893 |
+
line = raw.strip()
|
894 |
+
# drop trailing 'then' or 'end'
|
895 |
+
line_clean = re.sub(r'\s*(then|end)\s*$', '', line, flags=re.IGNORECASE)
|
896 |
+
|
897 |
+
# find a matching block definition
|
898 |
+
for opcode, info in all_defs.items():
|
899 |
+
if not info['regex'].match(line_clean):
|
900 |
+
continue
|
901 |
+
# pop up to correct level
|
902 |
+
while stack and stack[-1][0] >= indent:
|
903 |
+
stack.pop()
|
904 |
+
container = stack[-1][1]
|
905 |
+
|
906 |
+
key = pick_key(opcode)
|
907 |
+
node = {'block_key': key}
|
908 |
+
shape = info['shape']
|
909 |
+
# determine node type
|
910 |
+
if 'Hat Block' in shape:
|
911 |
+
node['type'] = 'hat'
|
912 |
+
node['description'] = info['block_name']
|
913 |
+
node['next'] = []
|
914 |
+
target_list = node['next']
|
915 |
+
elif 'C-Block' in shape:
|
916 |
+
node['type'] = 'c_block'
|
917 |
+
node['description'] = info['block_name']
|
918 |
+
node['condition'] = None
|
919 |
+
node['body'] = []
|
920 |
+
target_list = node['body']
|
921 |
+
# the first input is usually the boolean condition
|
922 |
+
if info['inputs']:
|
923 |
+
cond_name = info['inputs'][0]
|
924 |
+
# extract the <...> portion
|
925 |
+
cond_match = re.search(r'<(.+)>', line)
|
926 |
+
if cond_match:
|
927 |
+
node['condition'] = parse_expression(cond_match.group(1))
|
928 |
+
elif 'Cap Block' in shape:
|
929 |
+
node['type'] = 'cap'
|
930 |
+
# e.g. stop [all v]
|
931 |
+
if 'inputs' in info['definition'] and info['definition']['inputs']:
|
932 |
+
fld = info['definition']['inputs'][0]['name']
|
933 |
+
val = re.search(r'\[\s*([^\]]+)\s*\]', line)
|
934 |
+
if val:
|
935 |
+
node['option'] = val.group(1)
|
936 |
+
target_list = None
|
937 |
+
else:
|
938 |
+
node['type'] = 'stack'
|
939 |
+
# parse any inputs in parentheses or brackets
|
940 |
+
node['inputs'] = {}
|
941 |
+
# for each input name in the definition, find its occurrence
|
942 |
+
for inp_name in info['inputs']:
|
943 |
+
# look for "(...)" after the input name or anywhere
|
944 |
+
# brute-force: find all "(...)" then map in order
|
945 |
+
pass
|
946 |
+
target_list = None
|
947 |
+
|
948 |
+
container.append(node)
|
949 |
+
if target_list is not None:
|
950 |
+
stack.append((indent, target_list))
|
951 |
+
break
|
952 |
+
else:
|
953 |
+
# no matching opcode — you can log or raise here
|
954 |
+
raise ValueError(f"Could not classify line: {line!r}")
|
955 |
+
|
956 |
+
return {'flow': flow}
|
957 |
+
|
958 |
+
pseudo_code_input = """
|
959 |
+
when green flag clicked
|
960 |
+
go to x: (240) y: (-135)
|
961 |
+
set [score v] to (1)
|
962 |
+
set [speed v] to (1)
|
963 |
+
show variable [score v]
|
964 |
+
show variable [speed v]
|
965 |
+
forever
|
966 |
+
glide (2) seconds to x: (-240) y: (-135)
|
967 |
+
if <((x position)) < (-235)> then
|
968 |
+
set x to (240)
|
969 |
+
end
|
970 |
+
if <touching [Sprite1 v]?> then
|
971 |
+
broadcast [Game Over v]
|
972 |
+
stop [all v]
|
973 |
+
end
|
974 |
+
end
|
975 |
+
end
|
976 |
+
"""
|
977 |
+
|
978 |
+
# ── USAGE ──
|
979 |
+
plan = generate_plan(generated_output_json, initial_opcode_occurrences, pseudo_code_input)
|
980 |
+
import json
|
981 |
+
print(json.dumps(plan, indent=2))
|
v2/utils/plan_generator.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_10.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_11.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_12.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_13.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_2.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_3.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_4.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_5.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_6.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_7.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_8.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/plan_generator_9.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
v2/utils/pseudo_exampls.txt
ADDED
@@ -0,0 +1,618 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pseudo_code_examples = [
|
2 |
+
# From motion_block.json
|
3 |
+
"""
|
4 |
+
when green flag clicked
|
5 |
+
go to x: (0) y: (0)
|
6 |
+
point in direction (90)
|
7 |
+
move (50) steps
|
8 |
+
end
|
9 |
+
""",
|
10 |
+
"""
|
11 |
+
when [right arrow v] key pressed
|
12 |
+
turn right (15) degrees
|
13 |
+
end
|
14 |
+
""",
|
15 |
+
"""
|
16 |
+
when this sprite clicked
|
17 |
+
go to [mouse-pointer v]
|
18 |
+
""",
|
19 |
+
"""
|
20 |
+
when green flag clicked
|
21 |
+
glide (2) secs to x: (150) y: (-100)
|
22 |
+
glide (2) secs to x: (-150) y: (100)
|
23 |
+
end
|
24 |
+
""",
|
25 |
+
"""
|
26 |
+
when green flag clicked
|
27 |
+
forever
|
28 |
+
point towards [mouse-pointer v]
|
29 |
+
move (5) steps
|
30 |
+
end
|
31 |
+
end
|
32 |
+
""",
|
33 |
+
"""
|
34 |
+
when [right arrow v] key pressed
|
35 |
+
change x by (10)
|
36 |
+
end
|
37 |
+
""",
|
38 |
+
"""
|
39 |
+
when green flag clicked
|
40 |
+
set x to (0)
|
41 |
+
set y to (0)
|
42 |
+
end
|
43 |
+
""",
|
44 |
+
"""
|
45 |
+
when [up arrow v] key pressed
|
46 |
+
change y by (10)
|
47 |
+
end
|
48 |
+
""",
|
49 |
+
"""
|
50 |
+
when green flag clicked
|
51 |
+
forever
|
52 |
+
move (10) steps
|
53 |
+
if on edge, bounce
|
54 |
+
end
|
55 |
+
end
|
56 |
+
""",
|
57 |
+
"""
|
58 |
+
when green flag clicked
|
59 |
+
set rotation style [left-right v]
|
60 |
+
forever
|
61 |
+
move (10) steps
|
62 |
+
if on edge, bounce
|
63 |
+
end
|
64 |
+
end
|
65 |
+
""",
|
66 |
+
# From looks_block.json
|
67 |
+
"""
|
68 |
+
when green flag clicked
|
69 |
+
say [Grr] for (3) seconds
|
70 |
+
say [Have you seen my honey? v] for (3) seconds
|
71 |
+
end
|
72 |
+
""",
|
73 |
+
"""
|
74 |
+
when green flag clicked
|
75 |
+
say [Welcome to my game! v]
|
76 |
+
wait (2) seconds
|
77 |
+
say []
|
78 |
+
end
|
79 |
+
""",
|
80 |
+
"""
|
81 |
+
when this sprite clicked
|
82 |
+
think [What should I do? v] for (2) seconds
|
83 |
+
end
|
84 |
+
""",
|
85 |
+
"""
|
86 |
+
when I receive [correct answer v]
|
87 |
+
think [That's right! v]
|
88 |
+
wait (1) seconds
|
89 |
+
think [good v]
|
90 |
+
end
|
91 |
+
""",
|
92 |
+
"""
|
93 |
+
when I receive [explosion v]
|
94 |
+
repeat (5)
|
95 |
+
next costume
|
96 |
+
end
|
97 |
+
hide
|
98 |
+
end
|
99 |
+
""",
|
100 |
+
"""
|
101 |
+
when green flag clicked
|
102 |
+
forever
|
103 |
+
next costume
|
104 |
+
wait (0.2) seconds
|
105 |
+
end
|
106 |
+
end
|
107 |
+
""",
|
108 |
+
"""
|
109 |
+
when green flag clicked
|
110 |
+
switch backdrop to [start screen v]
|
111 |
+
end
|
112 |
+
""",
|
113 |
+
"""
|
114 |
+
broadcast [game over v]
|
115 |
+
switch backdrop to [game over v] and wait
|
116 |
+
stop [all v]
|
117 |
+
end
|
118 |
+
""",
|
119 |
+
"""
|
120 |
+
when [space v] key pressed
|
121 |
+
next backdrop
|
122 |
+
end
|
123 |
+
""",
|
124 |
+
"""
|
125 |
+
when green flag clicked
|
126 |
+
repeat (10)
|
127 |
+
change size by (5)
|
128 |
+
wait (0.1) seconds
|
129 |
+
end
|
130 |
+
end
|
131 |
+
""",
|
132 |
+
"""
|
133 |
+
when green flag clicked
|
134 |
+
set size to (50) %
|
135 |
+
wait (1) seconds
|
136 |
+
set size to (100) %
|
137 |
+
end
|
138 |
+
""",
|
139 |
+
"""
|
140 |
+
when green flag clicked
|
141 |
+
forever
|
142 |
+
change [color v] effect by (5)
|
143 |
+
wait (0.1) seconds
|
144 |
+
end
|
145 |
+
end
|
146 |
+
""",
|
147 |
+
"""
|
148 |
+
when green flag clicked
|
149 |
+
set [ghost v] effect to (75)
|
150 |
+
end
|
151 |
+
""",
|
152 |
+
"""
|
153 |
+
when green flag clicked
|
154 |
+
change [color v] effect by (50)
|
155 |
+
wait (2) seconds
|
156 |
+
clear graphic effects
|
157 |
+
end
|
158 |
+
""",
|
159 |
+
"""
|
160 |
+
when green flag clicked
|
161 |
+
hide
|
162 |
+
when I receive [start game v]
|
163 |
+
show
|
164 |
+
end
|
165 |
+
""",
|
166 |
+
"""
|
167 |
+
when green flag clicked
|
168 |
+
hide
|
169 |
+
end
|
170 |
+
""",
|
171 |
+
"""
|
172 |
+
when green flag clicked
|
173 |
+
go to [front v] layer
|
174 |
+
end
|
175 |
+
""",
|
176 |
+
"""
|
177 |
+
when this sprite clicked
|
178 |
+
go [forward v] (1) layers
|
179 |
+
end
|
180 |
+
""",
|
181 |
+
"""
|
182 |
+
say join [I am costume ] (costume [name v])
|
183 |
+
""",
|
184 |
+
"""
|
185 |
+
say join [Current backdrop: ] (backdrop [name v]) for (2) seconds
|
186 |
+
""",
|
187 |
+
"""
|
188 |
+
set size to ( (size) + (10) )
|
189 |
+
""",
|
190 |
+
# From sound_block.json
|
191 |
+
"""
|
192 |
+
when backdrop switches to [winning screen v]
|
193 |
+
play sound [fanfare v] until done
|
194 |
+
say [You won!] for (2) seconds
|
195 |
+
end
|
196 |
+
""",
|
197 |
+
"""
|
198 |
+
forever
|
199 |
+
play sound [Music v] until done
|
200 |
+
end
|
201 |
+
""",
|
202 |
+
"""
|
203 |
+
when this sprite clicked
|
204 |
+
start sound [Pop v]
|
205 |
+
change [score v] by (1)
|
206 |
+
end
|
207 |
+
""",
|
208 |
+
"""
|
209 |
+
when I receive [game over v]
|
210 |
+
stop all sounds
|
211 |
+
end
|
212 |
+
""",
|
213 |
+
"""
|
214 |
+
when [down arrow v] key pressed
|
215 |
+
change volume by (-5)
|
216 |
+
end
|
217 |
+
""",
|
218 |
+
"""
|
219 |
+
when green flag clicked
|
220 |
+
set volume to (50) %
|
221 |
+
end
|
222 |
+
""",
|
223 |
+
"""
|
224 |
+
say join [Current volume: ] (volume)
|
225 |
+
""",
|
226 |
+
# From event_block.json
|
227 |
+
"""
|
228 |
+
when green flag clicked
|
229 |
+
go to x: (0) y: (0)
|
230 |
+
say [Hello!] for (2) seconds
|
231 |
+
end
|
232 |
+
""",
|
233 |
+
"""
|
234 |
+
when [space v] key pressed
|
235 |
+
repeat (10)
|
236 |
+
change y by (10)
|
237 |
+
wait (0.1) seconds
|
238 |
+
change y by (-10)
|
239 |
+
end
|
240 |
+
end
|
241 |
+
""",
|
242 |
+
"""
|
243 |
+
when [right arrow v] key pressed
|
244 |
+
point in direction (90)
|
245 |
+
move (10) steps
|
246 |
+
end
|
247 |
+
""",
|
248 |
+
"""
|
249 |
+
when this sprite clicked
|
250 |
+
say [Ouch!] for (1) seconds
|
251 |
+
change [score v] by (-1)
|
252 |
+
end
|
253 |
+
""",
|
254 |
+
"""
|
255 |
+
when backdrop switches to [game over v]
|
256 |
+
stop [all v]
|
257 |
+
end
|
258 |
+
""",
|
259 |
+
"""
|
260 |
+
when [loudness v] > (70)
|
261 |
+
start sound [scream v]
|
262 |
+
end
|
263 |
+
""",
|
264 |
+
"""
|
265 |
+
when I receive [start game v]
|
266 |
+
show
|
267 |
+
go to x: (0) y: (0)
|
268 |
+
end
|
269 |
+
""",
|
270 |
+
"""
|
271 |
+
when I receive [game over v]
|
272 |
+
set score to 0
|
273 |
+
stop [all v]
|
274 |
+
end
|
275 |
+
""",
|
276 |
+
"""
|
277 |
+
if <key [space v] pressed?> then
|
278 |
+
broadcast [jump v]
|
279 |
+
end
|
280 |
+
""",
|
281 |
+
"""
|
282 |
+
broadcast [initialize sprites v] and wait
|
283 |
+
say [Game Started!] for (2) seconds
|
284 |
+
""",
|
285 |
+
# From control_block.json
|
286 |
+
"""
|
287 |
+
say [Hello!] for (1) seconds
|
288 |
+
wait (0.5) seconds
|
289 |
+
say [Goodbye!] for (1) seconds
|
290 |
+
""",
|
291 |
+
"""
|
292 |
+
when green flag clicked
|
293 |
+
repeat (10)
|
294 |
+
move (10) steps
|
295 |
+
wait (0.1) seconds
|
296 |
+
end
|
297 |
+
end
|
298 |
+
""",
|
299 |
+
"""
|
300 |
+
when green flag clicked
|
301 |
+
forever
|
302 |
+
move (5) steps
|
303 |
+
if on edge, bounce
|
304 |
+
end
|
305 |
+
end
|
306 |
+
""",
|
307 |
+
"""
|
308 |
+
forever
|
309 |
+
if <touching [color (red) v]?> then
|
310 |
+
stop [this script v]
|
311 |
+
end
|
312 |
+
end
|
313 |
+
""",
|
314 |
+
"""
|
315 |
+
if <(score) > (10)> then
|
316 |
+
say [You win!] for (2) seconds
|
317 |
+
else
|
318 |
+
say [Keep trying!] for (2) seconds
|
319 |
+
end
|
320 |
+
""",
|
321 |
+
"""
|
322 |
+
repeat until <touching [edge v]?>
|
323 |
+
move (5) steps
|
324 |
+
end
|
325 |
+
""",
|
326 |
+
"""
|
327 |
+
if <(health) = (0)> then
|
328 |
+
stop [all v]
|
329 |
+
end
|
330 |
+
""",
|
331 |
+
"""
|
332 |
+
when I start as a clone
|
333 |
+
wait until <touching [edge v]?>
|
334 |
+
delete this clone
|
335 |
+
end
|
336 |
+
""",
|
337 |
+
"""
|
338 |
+
when I start as a clone
|
339 |
+
go to x: (pick random -240 to 240) y: (pick random -180 to 180)
|
340 |
+
show
|
341 |
+
forever
|
342 |
+
move (10) steps
|
343 |
+
if on edge, bounce
|
344 |
+
end
|
345 |
+
end
|
346 |
+
""",
|
347 |
+
"""
|
348 |
+
when I start as a clone
|
349 |
+
wait (5) seconds
|
350 |
+
delete this clone
|
351 |
+
end
|
352 |
+
""",
|
353 |
+
"""
|
354 |
+
when green flag clicked
|
355 |
+
hide
|
356 |
+
forever
|
357 |
+
create clone of [myself v]
|
358 |
+
wait (1) seconds
|
359 |
+
end
|
360 |
+
""",
|
361 |
+
# From data_block.json
|
362 |
+
"""
|
363 |
+
when green flag clicked
|
364 |
+
set [score v] to (0)
|
365 |
+
set [player name v] to [Guest]
|
366 |
+
end
|
367 |
+
""",
|
368 |
+
"""
|
369 |
+
when this sprite clicked
|
370 |
+
change [score v] by (1)
|
371 |
+
end
|
372 |
+
""",
|
373 |
+
"""
|
374 |
+
when green flag clicked
|
375 |
+
add [apple] to [shopping list v]
|
376 |
+
add [banana] to [shopping list v]
|
377 |
+
end
|
378 |
+
""",
|
379 |
+
"""
|
380 |
+
when green flag clicked
|
381 |
+
delete (all) of [my list v]
|
382 |
+
end
|
383 |
+
""",
|
384 |
+
"""
|
385 |
+
insert [orange] at (2) of [fruits v]
|
386 |
+
""",
|
387 |
+
"""
|
388 |
+
replace item (1) of [colors v] with [blue]
|
389 |
+
""",
|
390 |
+
"""
|
391 |
+
when green flag clicked
|
392 |
+
show variable [score v]
|
393 |
+
end
|
394 |
+
""",
|
395 |
+
"""
|
396 |
+
when I receive [game over v]
|
397 |
+
hide variable [score v]
|
398 |
+
end
|
399 |
+
""",
|
400 |
+
"""
|
401 |
+
when green flag clicked
|
402 |
+
show list [shopping list v]
|
403 |
+
end
|
404 |
+
""",
|
405 |
+
"""
|
406 |
+
when I receive [game over v]
|
407 |
+
hide list [shopping list v]
|
408 |
+
end
|
409 |
+
""",
|
410 |
+
"""
|
411 |
+
say ([score v]) for (2) seconds
|
412 |
+
""",
|
413 |
+
"""
|
414 |
+
say ([my list v])
|
415 |
+
""",
|
416 |
+
"""
|
417 |
+
say (item (2) of [myList v]) for 2 seconds
|
418 |
+
""",
|
419 |
+
"""
|
420 |
+
say join (length of [shopping list v]) [ items in the list.]
|
421 |
+
""",
|
422 |
+
"""
|
423 |
+
if <(item # of [Dog] in [myList v])> (0)> then
|
424 |
+
say join [Dog found at position ] (item # of [Dog] in [my list v])
|
425 |
+
end
|
426 |
+
""",
|
427 |
+
# From reporter_blocks.json (some already covered by other categories)
|
428 |
+
"""
|
429 |
+
when green flag clicked
|
430 |
+
say (x position) for (2) seconds
|
431 |
+
end
|
432 |
+
""",
|
433 |
+
"""
|
434 |
+
set [worms v] to (y position)
|
435 |
+
""",
|
436 |
+
"""
|
437 |
+
when green flag clicked
|
438 |
+
say (direction) for (2) seconds
|
439 |
+
end
|
440 |
+
""",
|
441 |
+
"""
|
442 |
+
say join [I am costume ] (costume [name v])
|
443 |
+
""",
|
444 |
+
"""
|
445 |
+
set size to ( (size) + (10) )
|
446 |
+
""",
|
447 |
+
"""
|
448 |
+
say join [Current backdrop: ] (backdrop [name v]) for (2) seconds
|
449 |
+
""",
|
450 |
+
"""
|
451 |
+
say join [Current volume: ] (volume)
|
452 |
+
""",
|
453 |
+
"""
|
454 |
+
if <(distance to [Sprite2 v]) < (50)> then
|
455 |
+
say [Too close!]
|
456 |
+
end
|
457 |
+
""",
|
458 |
+
"""
|
459 |
+
ask [What is your name?] and wait
|
460 |
+
say join [Hello ] (answer)
|
461 |
+
""",
|
462 |
+
"""
|
463 |
+
go to x: (mouse x) y: (mouse y)
|
464 |
+
""",
|
465 |
+
"""
|
466 |
+
if <(mouse y) < (0)> then
|
467 |
+
say [Below center]
|
468 |
+
end
|
469 |
+
""",
|
470 |
+
"""
|
471 |
+
when green flag clicked
|
472 |
+
forever
|
473 |
+
if <(loudness) > (30)> then
|
474 |
+
start sound [pop v]
|
475 |
+
end
|
476 |
+
""",
|
477 |
+
"""
|
478 |
+
when green flag clicked
|
479 |
+
reset timer
|
480 |
+
wait (5) seconds
|
481 |
+
say join [Time elapsed: ] (timer)
|
482 |
+
end
|
483 |
+
""",
|
484 |
+
"""
|
485 |
+
set [other sprite X v] to ( (x position) of [Sprite2 v] )
|
486 |
+
""",
|
487 |
+
"""
|
488 |
+
say join [The current hour is ] (current [hour v])
|
489 |
+
""",
|
490 |
+
"""
|
491 |
+
say join [Days passed: ] (days since 2000)
|
492 |
+
""",
|
493 |
+
"""
|
494 |
+
say join [Hello, ] (username)
|
495 |
+
""",
|
496 |
+
"""
|
497 |
+
set [total v] to ( (number 1) + (number 2) )
|
498 |
+
""",
|
499 |
+
"""
|
500 |
+
set [difference v] to ( (number 1) - (number 2) )
|
501 |
+
""",
|
502 |
+
"""
|
503 |
+
set [area v] to ( (length) * (width) )
|
504 |
+
""",
|
505 |
+
"""
|
506 |
+
set [average v] to ( (total score) / (number of students) )
|
507 |
+
""",
|
508 |
+
"""
|
509 |
+
go to x: (pick random -240 to 240) y: (pick random -180 to 180)
|
510 |
+
""",
|
511 |
+
"""
|
512 |
+
say (join [Hello ][World!])
|
513 |
+
""",
|
514 |
+
"""
|
515 |
+
say (letter (1) of [apple])
|
516 |
+
""",
|
517 |
+
"""
|
518 |
+
say (length of [banana])
|
519 |
+
""",
|
520 |
+
"""
|
521 |
+
if <([number v] mod (2) = (0))> then
|
522 |
+
say [Even number]
|
523 |
+
end
|
524 |
+
""",
|
525 |
+
"""
|
526 |
+
set [rounded score v] to (round (score))
|
527 |
+
""",
|
528 |
+
"""
|
529 |
+
set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) ))
|
530 |
+
""",
|
531 |
+
# From boolean_blocks.json (conditions already covered by parse_condition)
|
532 |
+
"""
|
533 |
+
if <(score) < (10)> then
|
534 |
+
say [Keep trying!]
|
535 |
+
end
|
536 |
+
""",
|
537 |
+
"""
|
538 |
+
if <(answer) = (5)> then
|
539 |
+
say [Correct!]
|
540 |
+
end
|
541 |
+
""",
|
542 |
+
"""
|
543 |
+
if <([health v]) > (0)> then
|
544 |
+
move (10) steps
|
545 |
+
else
|
546 |
+
stop [all v]
|
547 |
+
end
|
548 |
+
""",
|
549 |
+
"""
|
550 |
+
if <<mouse down?> and <touching [mouse-pointer]?> > then
|
551 |
+
say [You're clicking me!]
|
552 |
+
end
|
553 |
+
""",
|
554 |
+
"""
|
555 |
+
if <<key [left arrow v] pressed?> or <key [a v] pressed?>> then
|
556 |
+
change x by (-10)
|
557 |
+
end
|
558 |
+
""",
|
559 |
+
"""
|
560 |
+
if <not <touching [Sprite2 v]?>> then
|
561 |
+
say [I'm safe!]
|
562 |
+
end
|
563 |
+
""",
|
564 |
+
"""
|
565 |
+
if <[answer] contains [yes]?> then
|
566 |
+
say [Great!]
|
567 |
+
end
|
568 |
+
""",
|
569 |
+
"""
|
570 |
+
if <touching [Sprite v]?> then
|
571 |
+
broadcast [Game Over v]
|
572 |
+
end
|
573 |
+
""",
|
574 |
+
"""
|
575 |
+
if <touching [edge v]?> then
|
576 |
+
bounce off edge
|
577 |
+
end
|
578 |
+
""",
|
579 |
+
"""
|
580 |
+
if <touching color [#FF0000]?> then
|
581 |
+
change [health v] by (-1)
|
582 |
+
end
|
583 |
+
""",
|
584 |
+
"""
|
585 |
+
if <color [#00FF00] is touching [#FF0000]?> then
|
586 |
+
say [Collision!]
|
587 |
+
end
|
588 |
+
""",
|
589 |
+
"""
|
590 |
+
forever
|
591 |
+
if <key [space v] pressed?> then
|
592 |
+
broadcast [shoot v]
|
593 |
+
end
|
594 |
+
end
|
595 |
+
""",
|
596 |
+
"""
|
597 |
+
if <mouse down?> then
|
598 |
+
go to mouse-pointer
|
599 |
+
end
|
600 |
+
""",
|
601 |
+
"""
|
602 |
+
if <[inventory v] contains [key]?> then
|
603 |
+
say [You have the key!]
|
604 |
+
end
|
605 |
+
""",
|
606 |
+
# Custom block example
|
607 |
+
"""
|
608 |
+
define jump (height)
|
609 |
+
change y by (height)
|
610 |
+
wait (0.5) seconds
|
611 |
+
change y by (0 - (height))
|
612 |
+
end
|
613 |
+
|
614 |
+
when green flag clicked
|
615 |
+
jump (50)
|
616 |
+
end
|
617 |
+
"""
|
618 |
+
]
|
v2/utils/script_plan.py
ADDED
@@ -0,0 +1,1449 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import copy
|
3 |
+
import re
|
4 |
+
|
5 |
+
def generate_blocks_from_opcodes(opcode_counts, all_block_definitions):
|
6 |
+
"""
|
7 |
+
Generates a dictionary of Scratch-like blocks based on a list of opcodes and a reference block definition.
|
8 |
+
It now correctly links parent and menu blocks using their generated unique keys, and handles various block categories.
|
9 |
+
It ensures that menu blocks are only generated as children of their respective parent blocks.
|
10 |
+
|
11 |
+
Args:
|
12 |
+
opcode_counts (list): An array of objects, each with an 'opcode' and 'count' property.
|
13 |
+
Example: [{"opcode": "motion_gotoxy", "count": 1}]
|
14 |
+
all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types.
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
tuple: A tuple containing:
|
18 |
+
- dict: A JSON object where keys are generated block IDs and values are the block definitions.
|
19 |
+
- dict: The opcode_occurrences dictionary for consistent unique key generation across functions.
|
20 |
+
"""
|
21 |
+
generated_blocks = {}
|
22 |
+
opcode_occurrences = {} # To keep track of how many times each opcode (main or menu) has been used for unique keys
|
23 |
+
|
24 |
+
# Define explicit parent-menu relationships for linking purposes
|
25 |
+
# This maps main_opcode -> list of (input_field_name, menu_opcode)
|
26 |
+
explicit_menu_links = {
|
27 |
+
"motion_goto": [("TO", "motion_goto_menu")],
|
28 |
+
"motion_glideto": [("TO", "motion_glideto_menu")],
|
29 |
+
"motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
|
30 |
+
"sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
|
31 |
+
"sensing_of": [("OBJECT", "sensing_of_object_menu")],
|
32 |
+
"sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
|
33 |
+
"control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
|
34 |
+
"sound_play": [("SOUND_MENU", "sound_sounds_menu")],
|
35 |
+
"sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
|
36 |
+
"looks_switchcostumeto": [("COSTUME", "looks_costume")],
|
37 |
+
"looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
|
38 |
+
}
|
39 |
+
|
40 |
+
# --- Step 1: Process explicitly requested opcodes and generate their instances and associated menus ---
|
41 |
+
for item in opcode_counts:
|
42 |
+
opcode = item.get("opcode")
|
43 |
+
count = item.get("count", 1)
|
44 |
+
|
45 |
+
# Special handling for 'sensing_istouching' which maps to 'sensing_touchingobject'
|
46 |
+
if opcode == "sensing_istouching":
|
47 |
+
opcode = "sensing_touchingobject"
|
48 |
+
|
49 |
+
if not opcode:
|
50 |
+
print("Warning: Skipping item with missing 'opcode'.")
|
51 |
+
continue
|
52 |
+
|
53 |
+
if opcode not in all_block_definitions:
|
54 |
+
print(f"Warning: Opcode '{opcode}' not found in all_block_definitions. Skipping.")
|
55 |
+
continue
|
56 |
+
|
57 |
+
for _ in range(count):
|
58 |
+
# Increment occurrence count for the current main opcode
|
59 |
+
opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1
|
60 |
+
main_block_instance_num = opcode_occurrences[opcode]
|
61 |
+
|
62 |
+
main_block_unique_key = f"{opcode}_{main_block_instance_num}"
|
63 |
+
|
64 |
+
# Create a deep copy of the main block definition
|
65 |
+
main_block_data = copy.deepcopy(all_block_definitions[opcode])
|
66 |
+
|
67 |
+
# Set properties for a top-level main block
|
68 |
+
main_block_data["parent"] = None
|
69 |
+
main_block_data["next"] = None
|
70 |
+
main_block_data["topLevel"] = True
|
71 |
+
main_block_data["shadow"] = False # Main blocks are typically not shadows
|
72 |
+
|
73 |
+
generated_blocks[main_block_unique_key] = main_block_data
|
74 |
+
|
75 |
+
# If this main block has associated menus, generate and link them now
|
76 |
+
if opcode in explicit_menu_links:
|
77 |
+
for input_field_name, menu_opcode_type in explicit_menu_links[opcode]:
|
78 |
+
if menu_opcode_type in all_block_definitions:
|
79 |
+
# Increment the occurrence for the menu block type
|
80 |
+
opcode_occurrences[menu_opcode_type] = opcode_occurrences.get(menu_opcode_type, 0) + 1
|
81 |
+
menu_block_instance_num = opcode_occurrences[menu_opcode_type]
|
82 |
+
|
83 |
+
menu_block_data = copy.deepcopy(all_block_definitions[menu_opcode_type])
|
84 |
+
|
85 |
+
# Generate a unique key for this specific menu instance
|
86 |
+
menu_unique_key = f"{menu_opcode_type}_{menu_block_instance_num}"
|
87 |
+
|
88 |
+
# Set properties for a shadow menu block
|
89 |
+
menu_block_data["shadow"] = True
|
90 |
+
menu_block_data["topLevel"] = False
|
91 |
+
menu_block_data["next"] = None
|
92 |
+
menu_block_data["parent"] = main_block_unique_key # Link menu to its parent instance
|
93 |
+
|
94 |
+
# Update the main block's input to point to this unique menu instance
|
95 |
+
if input_field_name in main_block_data.get("inputs", {}) and \
|
96 |
+
isinstance(main_block_data["inputs"][input_field_name], list) and \
|
97 |
+
len(main_block_data["inputs"][input_field_name]) > 1 and \
|
98 |
+
main_block_data["inputs"][input_field_name][0] == 1:
|
99 |
+
|
100 |
+
main_block_data["inputs"][input_field_name][1] = menu_unique_key
|
101 |
+
|
102 |
+
generated_blocks[menu_unique_key] = menu_block_data
|
103 |
+
|
104 |
+
return generated_blocks, opcode_occurrences
|
105 |
+
|
106 |
+
def interpret_pseudo_code_and_update_blocks(generated_blocks_json, pseudo_code, all_block_definitions, opcode_occurrences):
|
107 |
+
"""
|
108 |
+
Interprets pseudo-code to update the generated Scratch blocks, replacing static values
|
109 |
+
with dynamic values and establishing stacking/nesting logic.
|
110 |
+
|
111 |
+
Args:
|
112 |
+
generated_blocks_json (dict): The JSON object of pre-generated blocks.
|
113 |
+
pseudo_code (str): The pseudo-code string to interpret.
|
114 |
+
all_block_definitions (dict): A comprehensive dictionary containing definitions for all block types.
|
115 |
+
opcode_occurrences (dict): A dictionary to keep track of opcode occurrences for unique key generation.
|
116 |
+
|
117 |
+
Returns:
|
118 |
+
dict: The updated JSON object of Scratch blocks.
|
119 |
+
"""
|
120 |
+
updated_blocks = copy.deepcopy(generated_blocks_json)
|
121 |
+
|
122 |
+
# Helper to create a new block instance (used for shadows/nested blocks)
|
123 |
+
def create_block_instance_for_parsing(opcode, parent_key=None, is_shadow=False, is_top_level=False):
|
124 |
+
opcode_occurrences[opcode] = opcode_occurrences.get(opcode, 0) + 1
|
125 |
+
unique_key = f"{opcode}_{opcode_occurrences[opcode]}"
|
126 |
+
|
127 |
+
new_block = copy.deepcopy(all_block_definitions.get(opcode, {}))
|
128 |
+
if not new_block:
|
129 |
+
print(f"Error: Definition for opcode '{opcode}' not found when creating instance for parsing.")
|
130 |
+
return None, None
|
131 |
+
|
132 |
+
new_block["parent"] = parent_key
|
133 |
+
new_block["next"] = None
|
134 |
+
new_block["topLevel"] = is_top_level
|
135 |
+
new_block["shadow"] = is_shadow
|
136 |
+
|
137 |
+
# Initialize inputs/fields to default empty values, but preserve structure
|
138 |
+
if "inputs" in new_block:
|
139 |
+
for input_name, input_data in new_block["inputs"].items():
|
140 |
+
if isinstance(input_data, list) and len(input_data) > 1:
|
141 |
+
if input_data[0] == 1: # Block reference
|
142 |
+
new_block["inputs"][input_name][1] = None # Placeholder for linked block ID
|
143 |
+
elif input_data[0] in [4, 5, 6, 7, 8, 9, 10]: # Literal types
|
144 |
+
new_block["inputs"][input_name][1] = "" # Default empty literal
|
145 |
+
if "fields" in new_block:
|
146 |
+
for field_name, field_data in new_block["fields"].items():
|
147 |
+
if isinstance(field_data, list) and len(field_data) > 0:
|
148 |
+
new_block["fields"][field_name][0] = "" # Default empty field value
|
149 |
+
|
150 |
+
updated_blocks[unique_key] = new_block
|
151 |
+
return unique_key, new_block
|
152 |
+
|
153 |
+
# Helper to parse input values from pseudo-code and create nested blocks if necessary
|
154 |
+
def parse_and_link_input(parent_block_key, input_type, pseudo_value, input_name_in_parent=None, field_name_in_parent=None):
|
155 |
+
pseudo_value = pseudo_value.strip()
|
156 |
+
|
157 |
+
# 1. Handle literal numbers (e.g., "2", "+1", "-135")
|
158 |
+
if re.fullmatch(r"[-+]?\d+(\.\d+)?", pseudo_value):
|
159 |
+
return [4, pseudo_value] # Type 4 for number literal
|
160 |
+
|
161 |
+
# 2. Handle literal strings (e.g., "Game Over") - often in quotes or simply text
|
162 |
+
# If it's a broadcast message, it's typically a string literal.
|
163 |
+
# If it's a 'say' message, it's a string literal.
|
164 |
+
# If it's a variable name, it's handled by specific patterns later.
|
165 |
+
if pseudo_value.startswith('"') and pseudo_value.endswith('"'):
|
166 |
+
return [10, pseudo_value.strip('"')] # Type 10 for string literal
|
167 |
+
|
168 |
+
# 3. Handle variable names (e.g., "[score v]", "[Sprite1 v]", "[Game Over v]")
|
169 |
+
# These can be fields or inputs that expect a variable reporter block.
|
170 |
+
var_match = re.match(r"\[(.+?) v\]", pseudo_value)
|
171 |
+
if var_match:
|
172 |
+
var_name = var_match.group(1).strip()
|
173 |
+
# If it's a field (like for set variable, show variable)
|
174 |
+
if field_name_in_parent:
|
175 |
+
return var_name # Return name, parent block will set its field
|
176 |
+
# If it's an input that expects a variable reporter (e.g., 'say (score)')
|
177 |
+
# Create a data_variable shadow block
|
178 |
+
var_reporter_key, var_reporter_data = create_block_instance_for_parsing(
|
179 |
+
"data_variable", parent_key=parent_block_key, is_shadow=True
|
180 |
+
)
|
181 |
+
if var_reporter_key:
|
182 |
+
var_reporter_data["fields"]["VARIABLE"] = [var_name, f"`var_{var_name}"] # Placeholder ID
|
183 |
+
return [1, var_reporter_key] # Type 1 for block reference
|
184 |
+
else:
|
185 |
+
print(f"Warning: Could not create data_variable block for '{var_name}'")
|
186 |
+
return [10, var_name] # Fallback to string literal
|
187 |
+
|
188 |
+
# 4. Handle nested reporter blocks (e.g., "(x position)")
|
189 |
+
reporter_match = re.match(r"\((.+?)\)", pseudo_value)
|
190 |
+
if reporter_match:
|
191 |
+
inner_content = reporter_match.group(1).strip()
|
192 |
+
# Check if it's a known reporter block (like "x position")
|
193 |
+
if inner_content in reporter_opcode_lookup:
|
194 |
+
reporter_opcode = reporter_opcode_lookup[inner_content]
|
195 |
+
reporter_block_key, reporter_block_data = create_block_instance_for_parsing(
|
196 |
+
reporter_opcode, parent_key=parent_block_key, is_shadow=True
|
197 |
+
)
|
198 |
+
if reporter_block_key:
|
199 |
+
return [1, reporter_block_key] # Type 1 for block reference
|
200 |
+
else:
|
201 |
+
print(f"Warning: Could not create reporter block for '{inner_content}'")
|
202 |
+
return [10, inner_content] # Fallback to string literal
|
203 |
+
else: # It's a literal number or string inside parentheses
|
204 |
+
return parse_and_link_input(parent_block_key, input_type, inner_content) # Recurse for inner content
|
205 |
+
|
206 |
+
# 5. Handle nested boolean blocks (e.g., "<(...) < (...)>")
|
207 |
+
boolean_match = re.match(r"<(.+?)>", pseudo_value)
|
208 |
+
if boolean_match:
|
209 |
+
inner_condition_str = boolean_match.group(1).strip()
|
210 |
+
# This is typically handled by the parent block's parsing (e.g., control_if)
|
211 |
+
# For now, if called directly, it implies a boolean reporter.
|
212 |
+
# We'll need specific logic for operator_lt, operator_and, etc.
|
213 |
+
# This part is complex and often handled by the parent block's specific regex.
|
214 |
+
# For this problem, the 'if' block's logic will create the boolean shadow.
|
215 |
+
print(f"Warning: Direct parsing of standalone boolean '{pseudo_value}' not fully supported here.")
|
216 |
+
return [10, pseudo_value] # Fallback to string literal
|
217 |
+
|
218 |
+
# Default to string literal if no other pattern matches
|
219 |
+
return [10, pseudo_value]
|
220 |
+
|
221 |
+
lines = [line.strip() for line in pseudo_code.strip().split('\n') if line.strip()]
|
222 |
+
|
223 |
+
# Track the current script and nesting
|
224 |
+
current_script_head = None
|
225 |
+
block_stack = [] # Stores (parent_block_key, indent_level, last_child_key_in_scope)
|
226 |
+
|
227 |
+
# Create a mapping from block name patterns to their opcodes and input/field details
|
228 |
+
# The 'input_map' keys are the internal Scratch input names, values are regex group indices.
|
229 |
+
# The 'field_map' keys are the internal Scratch field names, values are regex group indices.
|
230 |
+
# 'condition_opcode' is for 'if' blocks that take a specific boolean reporter.
|
231 |
+
pseudo_code_to_opcode_map = {
|
232 |
+
re.compile(r"when green flag clicked"): {"opcode": "event_whenflagclicked"},
|
233 |
+
re.compile(r"go to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_gotoxy", "input_map": {"X": 0, "Y": 1}},
|
234 |
+
re.compile(r"set \[(.+?) v\] to (.+)"): {"opcode": "data_setvariableto", "field_map": {"VARIABLE": 0}, "input_map": {"VALUE": 1}},
|
235 |
+
re.compile(r"show variable \[(.+?) v\]"): {"opcode": "data_showvariable", "field_map": {"VARIABLE": 0}},
|
236 |
+
re.compile(r"forever"): {"opcode": "control_forever"},
|
237 |
+
re.compile(r"glide \((.+?)\) seconds to x: \((.+?)\) y: \((.+?)\)"): {"opcode": "motion_glidesecstoxy", "input_map": {"SECS": 0, "X": 1, "Y": 2}},
|
238 |
+
re.compile(r"if <\((.+)\) < \((.+)\)> then"): {"opcode": "control_if", "condition_type": "operator_lt", "condition_input_map": {"OPERAND1": 0, "OPERAND2": 1}},
|
239 |
+
re.compile(r"set x to \((.+?)\)"): {"opcode": "motion_setx", "input_map": {"X": 0}},
|
240 |
+
re.compile(r"if <touching \[(.+?) v\]\?> then"): {"opcode": "control_if", "condition_type": "sensing_touchingobject", "condition_field_map": {"TOUCHINGOBJECTMENU": 0}},
|
241 |
+
re.compile(r"broadcast \[(.+?) v\]"): {"opcode": "event_broadcast", "input_map": {"BROADCAST_INPUT": 0}},
|
242 |
+
re.compile(r"stop \[(.+?) v\]"): {"opcode": "control_stop", "field_map": {"STOP_OPTION": 0}},
|
243 |
+
re.compile(r"end"): {"opcode": "end_block"}, # Special marker for script end/C-block end
|
244 |
+
}
|
245 |
+
|
246 |
+
# Create a reverse lookup for reporter block opcodes based on their pseudo-code representation
|
247 |
+
reporter_opcode_lookup = {}
|
248 |
+
for opcode, definition in all_block_definitions.items():
|
249 |
+
if definition.get("block_shape") == "Reporter Block":
|
250 |
+
block_name = definition.get("block_name")
|
251 |
+
if block_name:
|
252 |
+
# Clean up block name for matching: remove parentheses, 'v' for variable, etc.
|
253 |
+
clean_name = block_name.replace("(", "").replace(")", "").replace("[", "").replace("]", "").replace(" v", "").strip()
|
254 |
+
reporter_opcode_lookup[clean_name] = opcode
|
255 |
+
# Add specific entries for common reporters if their pseudo-code differs from clean_name
|
256 |
+
if opcode == "motion_xposition":
|
257 |
+
reporter_opcode_lookup["x position"] = opcode
|
258 |
+
elif opcode == "motion_yposition":
|
259 |
+
reporter_opcode_lookup["y position"] = opcode
|
260 |
+
# Add more as needed based on pseudo-code patterns
|
261 |
+
|
262 |
+
for line_idx, raw_line in enumerate(lines):
|
263 |
+
current_line_indent = len(raw_line) - len(raw_line.lstrip())
|
264 |
+
line = raw_line.strip()
|
265 |
+
|
266 |
+
# Adjust block_stack based on current indent level
|
267 |
+
while block_stack and current_line_indent <= block_stack[-1][1]:
|
268 |
+
block_stack.pop()
|
269 |
+
|
270 |
+
matched_block_info = None
|
271 |
+
matched_values = None
|
272 |
+
|
273 |
+
# Try to match the line against known block patterns
|
274 |
+
for pattern_regex, info in pseudo_code_to_opcode_map.items():
|
275 |
+
match = pattern_regex.match(line)
|
276 |
+
if match:
|
277 |
+
matched_block_info = info
|
278 |
+
matched_values = match.groups()
|
279 |
+
break
|
280 |
+
|
281 |
+
if not matched_block_info:
|
282 |
+
print(f"Warning: Could not interpret line: '{line}' at line {line_idx + 1}")
|
283 |
+
continue
|
284 |
+
|
285 |
+
opcode = matched_block_info["opcode"]
|
286 |
+
|
287 |
+
# Handle 'end' block separately as it signifies closing a C-block
|
288 |
+
if opcode == "end_block":
|
289 |
+
# This 'end' matches the most recent C-block on the stack.
|
290 |
+
# The while loop at the beginning of the iteration already handles popping.
|
291 |
+
continue
|
292 |
+
|
293 |
+
parent_key = None
|
294 |
+
if block_stack:
|
295 |
+
parent_key = block_stack[-1][0] # The last block on the stack is the parent
|
296 |
+
|
297 |
+
# Create the new block instance
|
298 |
+
new_block_key, new_block_data = create_block_instance_for_parsing(
|
299 |
+
opcode,
|
300 |
+
parent_key=parent_key,
|
301 |
+
is_top_level=(parent_key is None)
|
302 |
+
)
|
303 |
+
if not new_block_key:
|
304 |
+
continue
|
305 |
+
|
306 |
+
# Link to previous block in the same script/nesting level
|
307 |
+
if block_stack:
|
308 |
+
# Update the 'next' of the previous block in the current scope
|
309 |
+
last_child_key_in_scope = block_stack[-1][2] if len(block_stack[-1]) > 2 else None
|
310 |
+
if last_child_key_in_scope and last_child_key_in_scope in updated_blocks:
|
311 |
+
updated_blocks[last_child_key_in_scope]["next"] = new_block_key
|
312 |
+
|
313 |
+
# Update the last child in the current scope
|
314 |
+
block_stack[-1] = (block_stack[-1][0], block_stack[-1][1], new_block_key)
|
315 |
+
|
316 |
+
# Populate inputs and fields based on matched_block_info and matched_values
|
317 |
+
if matched_values:
|
318 |
+
# Handle fields
|
319 |
+
if "field_map" in matched_block_info:
|
320 |
+
for field_name, group_idx in matched_block_info["field_map"].items():
|
321 |
+
pseudo_field_value = matched_values[group_idx].replace(' v', '').strip()
|
322 |
+
if field_name == "VARIABLE":
|
323 |
+
# For variable fields, the actual variable ID is often derived or generated.
|
324 |
+
# For now, we use a placeholder and the name.
|
325 |
+
new_block_data["fields"][field_name] = [pseudo_field_value, f"`var_{pseudo_field_value}"]
|
326 |
+
elif field_name == "STOP_OPTION":
|
327 |
+
new_block_data["fields"][field_name] = [pseudo_field_value, None] # No ID needed for dropdown option
|
328 |
+
else:
|
329 |
+
new_block_data["fields"][field_name][0] = pseudo_field_value
|
330 |
+
|
331 |
+
# Handle inputs
|
332 |
+
if "input_map" in matched_block_info:
|
333 |
+
for input_name, group_idx in matched_block_info["input_map"].items():
|
334 |
+
pseudo_input_value = matched_values[group_idx].strip()
|
335 |
+
|
336 |
+
parsed_input_info = parse_and_link_input(new_block_key, new_block_data["inputs"][input_name][0], pseudo_input_value, input_name_in_parent=input_name)
|
337 |
+
|
338 |
+
if parsed_input_info:
|
339 |
+
if parsed_input_info[0] == 1: # It's a linked block (shadow reporter/boolean/menu)
|
340 |
+
new_block_data["inputs"][input_name][1] = parsed_input_info[1] # Link block ID
|
341 |
+
new_block_data["inputs"][input_name][0] = parsed_input_info[0] # Set type to 1 (block)
|
342 |
+
else: # It's a literal value
|
343 |
+
new_block_data["inputs"][input_name][1] = parsed_input_info[1]
|
344 |
+
new_block_data["inputs"][input_name][0] = parsed_input_info[0] # Set appropriate type (4 for number, 10 for string)
|
345 |
+
|
346 |
+
# Special handling for 'if' block conditions
|
347 |
+
if opcode == "control_if":
|
348 |
+
condition_type = matched_block_info.get("condition_type")
|
349 |
+
if condition_type:
|
350 |
+
condition_block_key, condition_block_data = create_block_instance_for_parsing(
|
351 |
+
condition_type, parent_key=new_block_key, is_shadow=True
|
352 |
+
)
|
353 |
+
if condition_block_key:
|
354 |
+
new_block_data["inputs"]["CONDITION"] = [2, condition_block_key] # Type 2 for boolean block reference
|
355 |
+
|
356 |
+
# Populate inputs for the condition block (e.g., operator_lt)
|
357 |
+
if "condition_input_map" in matched_block_info:
|
358 |
+
for cond_input_name, cond_group_idx in matched_block_info["condition_input_map"].items():
|
359 |
+
pseudo_cond_value = matched_values[cond_group_idx].strip()
|
360 |
+
parsed_cond_input_info = parse_and_link_input(condition_block_key, condition_block_data["inputs"][cond_input_name][0], pseudo_cond_value)
|
361 |
+
if parsed_cond_input_info:
|
362 |
+
if parsed_cond_input_info[0] == 1:
|
363 |
+
condition_block_data["inputs"][cond_input_name][1] = parsed_cond_input_info[1]
|
364 |
+
condition_block_data["inputs"][cond_input_name][0] = parsed_cond_input_info[0]
|
365 |
+
else:
|
366 |
+
condition_block_data["inputs"][cond_input_name][1] = parsed_cond_input_info[1]
|
367 |
+
condition_block_data["inputs"][cond_input_name][0] = parsed_cond_input_info[0]
|
368 |
+
|
369 |
+
# Populate fields for the condition block (e.g., sensing_touchingobject's menu)
|
370 |
+
if "condition_field_map" in matched_block_info:
|
371 |
+
for cond_field_name, cond_group_idx in matched_block_info["condition_field_map"].items():
|
372 |
+
pseudo_cond_field_value = matched_values[cond_group_idx].replace(' v', '').strip()
|
373 |
+
if cond_field_name == "TOUCHINGOBJECTMENU":
|
374 |
+
# Create the menu block for TOUCHINGOBJECTMENU
|
375 |
+
menu_opcode = "sensing_touchingobjectmenu"
|
376 |
+
menu_key, menu_data = create_block_instance_for_parsing(
|
377 |
+
menu_opcode,
|
378 |
+
parent_key=condition_block_key,
|
379 |
+
is_shadow=True
|
380 |
+
)
|
381 |
+
if menu_key:
|
382 |
+
condition_block_data["inputs"]["TOUCHINGOBJECTMENU"] = [1, menu_key] # Link to menu block
|
383 |
+
menu_data["fields"]["TOUCHINGOBJECTMENU"] = [pseudo_cond_field_value, None] # Set menu value
|
384 |
+
else:
|
385 |
+
print(f"Warning: Could not create menu block for touching object: '{pseudo_cond_field_value}'")
|
386 |
+
else:
|
387 |
+
condition_block_data["fields"][cond_field_name][0] = pseudo_cond_field_value
|
388 |
+
else:
|
389 |
+
print(f"Warning: Could not create condition block '{condition_type}' for 'if' statement.")
|
390 |
+
|
391 |
+
|
392 |
+
# For C-blocks, push onto stack to track nesting
|
393 |
+
# 'control_if' is a C-block, but its 'next' is inside its substack.
|
394 |
+
# 'control_forever' is also a C-block.
|
395 |
+
if all_block_definitions[opcode].get("block_shape") == "C-Block" and opcode != "control_if":
|
396 |
+
# For C-blocks, the 'next' of the parent is usually null, and children start in 'SUBSTACK'
|
397 |
+
# We add the block to the stack, and its "next" will be its first child.
|
398 |
+
# The 'next' of the *last child* in its substack will point to the block after the C-block.
|
399 |
+
block_stack.append((new_block_key, current_line_indent, None)) # (parent_key, indent, last_child_key_in_substack)
|
400 |
+
|
401 |
+
# For C-blocks, the first child is linked via the 'SUBSTACK' input
|
402 |
+
new_block_data["inputs"]["SUBSTACK"] = [2, None] # Placeholder for the first child block ID
|
403 |
+
|
404 |
+
# For 'if' blocks, the 'next' of the parent is usually null, and children start in 'SUBSTACK'
|
405 |
+
if opcode == "control_if":
|
406 |
+
block_stack.append((new_block_key, current_line_indent, None))
|
407 |
+
new_block_data["inputs"]["SUBSTACK"] = [2, None] # Placeholder for the first child block ID
|
408 |
+
|
409 |
+
|
410 |
+
# Final pass to ensure topLevel is correctly set for the very first block of a script
|
411 |
+
for key, block in updated_blocks.items():
|
412 |
+
if block.get("parent") is None and block.get("next") is not None:
|
413 |
+
block["topLevel"] = True
|
414 |
+
elif block.get("parent") is None and block.get("next") is None and block.get("opcode") == "event_whenflagclicked":
|
415 |
+
block["topLevel"] = True # Ensure hat blocks are always topLevel
|
416 |
+
|
417 |
+
return updated_blocks
|
418 |
+
|
419 |
+
# --- Consolidated Block Definitions from all provided JSONs ---
|
420 |
+
# This dictionary should contain ALL block definitions from your JSON files.
|
421 |
+
# I'm using the provided definitions from the previous turn.
|
422 |
+
all_block_definitions = {
|
423 |
+
# motion_block.json
|
424 |
+
"motion_movesteps": {
|
425 |
+
"block_name": "move () steps", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_movesteps",
|
426 |
+
"functionality": "Moves the sprite forward by the specified number of steps in the direction it is currently facing. A positive value moves it forward, and a negative value moves it backward.",
|
427 |
+
"inputs": {"STEPS": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
428 |
+
},
|
429 |
+
"motion_turnright": {
|
430 |
+
"block_name": "turn right () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnright",
|
431 |
+
"functionality": "Turns the sprite clockwise by the specified number of degrees.",
|
432 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True
|
433 |
+
},
|
434 |
+
"motion_turnleft": {
|
435 |
+
"block_name": "turn left () degrees", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_turnleft",
|
436 |
+
"functionality": "Turns the sprite counter-clockwise by the specified number of degrees.",
|
437 |
+
"inputs": {"DEGREES": [1, [4, "15"]]}, "fields": {}, "shadow": False, "topLevel": True
|
438 |
+
},
|
439 |
+
"motion_goto": {
|
440 |
+
"block_name": "go to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_goto",
|
441 |
+
"functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.",
|
442 |
+
"inputs": {"TO": [1, "motion_goto_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
443 |
+
},
|
444 |
+
"motion_goto_menu": {
|
445 |
+
"block_name": "go to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_goto_menu",
|
446 |
+
"functionality": "Menu for go to block.",
|
447 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
448 |
+
},
|
449 |
+
"motion_gotoxy": {
|
450 |
+
"block_name": "go to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_gotoxy",
|
451 |
+
"functionality": "Moves the sprite to the specified X and Y coordinates on the stage.",
|
452 |
+
"inputs": {"X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
453 |
+
},
|
454 |
+
"motion_glideto": {
|
455 |
+
"block_name": "glide () secs to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glideto",
|
456 |
+
"functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.",
|
457 |
+
"inputs": {"SECS": [1, [4, "1"]], "TO": [1, "motion_glideto_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
458 |
+
},
|
459 |
+
"motion_glideto_menu": {
|
460 |
+
"block_name": "glide to menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_glideto_menu",
|
461 |
+
"functionality": "Menu for glide to block.",
|
462 |
+
"inputs": {}, "fields": {"TO": ["_random_", None]}, "shadow": True, "topLevel": False
|
463 |
+
},
|
464 |
+
"motion_glidesecstoxy": {
|
465 |
+
"block_name": "glide () secs to x: () y: ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_glidesecstoxy",
|
466 |
+
"functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.",
|
467 |
+
"inputs": {"SECS": [1, [4, "1"]], "X": [1, [4, "0"]], "Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
468 |
+
},
|
469 |
+
"motion_pointindirection": {
|
470 |
+
"block_name": "point in direction ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointindirection",
|
471 |
+
"functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).",
|
472 |
+
"inputs": {"DIRECTION": [1, [8, "90"]]}, "fields": {}, "shadow": False, "topLevel": True
|
473 |
+
},
|
474 |
+
"motion_pointtowards": {
|
475 |
+
"block_name": "point towards ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_pointtowards",
|
476 |
+
"functionality": "Points the sprite towards the mouse pointer or another specified sprite.",
|
477 |
+
"inputs": {"TOWARDS": [1, "motion_pointtowards_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
478 |
+
},
|
479 |
+
"motion_pointtowards_menu": {
|
480 |
+
"block_name": "point towards menu", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_pointtowards_menu",
|
481 |
+
"functionality": "Menu for point towards block.",
|
482 |
+
"inputs": {}, "fields": {"TOWARDS": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
483 |
+
},
|
484 |
+
"motion_changexby": {
|
485 |
+
"block_name": "change x by ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_changexby",
|
486 |
+
"functionality": "Changes the sprite's X-coordinate by the specified amount, moving it horizontally.",
|
487 |
+
"inputs": {"DX": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
488 |
+
},
|
489 |
+
"motion_setx": {
|
490 |
+
"block_name": "set x to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setx",
|
491 |
+
"functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.",
|
492 |
+
"inputs": {"X": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
493 |
+
},
|
494 |
+
"motion_changeyby": {
|
495 |
+
"block_name": "change y by ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_changeyby",
|
496 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
497 |
+
"inputs": {"DY": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
498 |
+
},
|
499 |
+
"motion_sety": {
|
500 |
+
"block_name": "set y to ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_sety",
|
501 |
+
"functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.",
|
502 |
+
"inputs": {"Y": [1, [4, "0"]]}, "fields": {}, "shadow": False, "topLevel": True
|
503 |
+
},
|
504 |
+
"motion_ifonedgebounce": {
|
505 |
+
"block_name": "if on edge, bounce", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_ifonedgebounce",
|
506 |
+
"functionality": "Reverses the sprite's direction if it touches the edge of the stage.",
|
507 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
508 |
+
},
|
509 |
+
"motion_setrotationstyle": {
|
510 |
+
"block_name": "set rotation style ()", "block_type": "Motion", "block_shape": "Stack Block", "op_code": "motion_setrotationstyle",
|
511 |
+
"functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).",
|
512 |
+
"inputs": {}, "fields": {"STYLE": ["left-right", None]}, "shadow": False, "topLevel": True
|
513 |
+
},
|
514 |
+
"motion_xposition": {
|
515 |
+
"block_name": "(x position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_xposition",
|
516 |
+
"functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]",
|
517 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
518 |
+
},
|
519 |
+
"motion_yposition": {
|
520 |
+
"block_name": "(y position)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_yposition",
|
521 |
+
"functionality": "Reports the current Y coordinate of the sprite on the stage.[NOTE: not used in stage/backdrops]",
|
522 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
523 |
+
},
|
524 |
+
"motion_direction": {
|
525 |
+
"block_name": "(direction)", "block_type": "Motion", "block_shape": "Reporter Block", "op_code": "motion_direction",
|
526 |
+
"functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]",
|
527 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
528 |
+
},
|
529 |
+
|
530 |
+
# control_block.json
|
531 |
+
"control_wait": {
|
532 |
+
"block_name": "wait () seconds", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait",
|
533 |
+
"functionality": "Pauses the script for a specified duration.",
|
534 |
+
"inputs": {"DURATION": [1, [5, "1"]]}, "fields": {}, "shadow": False, "topLevel": True
|
535 |
+
},
|
536 |
+
"control_repeat": {
|
537 |
+
"block_name": "repeat ()", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat",
|
538 |
+
"functionality": "Repeats the blocks inside it a specified number of times.",
|
539 |
+
"inputs": {"TIMES": [1, [6, "10"]], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
540 |
+
},
|
541 |
+
"control_forever": {
|
542 |
+
"block_name": "forever", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_forever",
|
543 |
+
"functionality": "Continuously runs the blocks inside it.",
|
544 |
+
"inputs": {"SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
545 |
+
},
|
546 |
+
"control_if": {
|
547 |
+
"block_name": "if <> then", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if",
|
548 |
+
"functionality": "Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]",
|
549 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
550 |
+
},
|
551 |
+
"control_if_else": {
|
552 |
+
"block_name": "if <> then else", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_if_else",
|
553 |
+
"functionality": "Executes one set of blocks if the specified boolean condition is true, and a different set of blocks if the condition is false. [NOTE: it takes boolean blocks as input]",
|
554 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None], "SUBSTACK2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
555 |
+
},
|
556 |
+
"control_wait_until": {
|
557 |
+
"block_name": "wait until <>", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_wait_until",
|
558 |
+
"functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
559 |
+
"inputs": {"CONDITION": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
560 |
+
},
|
561 |
+
"control_repeat_until": {
|
562 |
+
"block_name": "repeat until <>", "block_type": "Control", "block_shape": "C-Block", "op_code": "control_repeat_until",
|
563 |
+
"functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
564 |
+
"inputs": {"CONDITION": [2, None], "SUBSTACK": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
565 |
+
},
|
566 |
+
"control_stop": {
|
567 |
+
"block_name": "stop [v]", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_stop",
|
568 |
+
"functionality": "Halts all scripts, only the current script, or other scripts within the same sprite. Its shape can dynamically change based on the selected option.",
|
569 |
+
"inputs": {}, "fields": {"STOP_OPTION": ["all", None]}, "shadow": False, "topLevel": True, "mutation": {"tagName": "mutation", "children": [], "hasnext": "false"}
|
570 |
+
},
|
571 |
+
"control_start_as_clone": {
|
572 |
+
"block_name": "When I Start as a Clone", "block_type": "Control", "block_shape": "Hat Block", "op_code": "control_start_as_clone",
|
573 |
+
"functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.",
|
574 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
575 |
+
},
|
576 |
+
"control_create_clone_of": {
|
577 |
+
"block_name": "create clone of ()", "block_type": "Control", "block_shape": "Stack Block", "op_code": "control_create_clone_of",
|
578 |
+
"functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).",
|
579 |
+
"inputs": {"CLONE_OPTION": [1, "control_create_clone_of_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
580 |
+
},
|
581 |
+
"control_create_clone_of_menu": {
|
582 |
+
"block_name": "create clone of menu", "block_type": "Control", "block_shape": "Reporter Block", "op_code": "control_create_clone_of_menu",
|
583 |
+
"functionality": "Menu for create clone of block.",
|
584 |
+
"inputs": {}, "fields": {"CLONE_OPTION": ["_myself_", None]}, "shadow": True, "topLevel": False
|
585 |
+
},
|
586 |
+
"control_delete_this_clone": {
|
587 |
+
"block_name": "delete this clone", "block_type": "Control", "block_shape": "Cap Block", "op_code": "control_delete_this_clone",
|
588 |
+
"functionality": "Removes the clone that is executing it from the stage.",
|
589 |
+
"inputs":None, "fields": {}, "shadow": False, "topLevel": True
|
590 |
+
},
|
591 |
+
|
592 |
+
# data_block.json
|
593 |
+
"data_setvariableto": {
|
594 |
+
"block_name": "set [my variable v] to ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_setvariableto",
|
595 |
+
"functionality": "Assigns a specific value (number, string, or boolean) to a variable.",
|
596 |
+
"inputs": {"VALUE": [1, [10, "0"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
597 |
+
},
|
598 |
+
"data_changevariableby": {
|
599 |
+
"block_name": "change [my variable v] by ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_changevariableby",
|
600 |
+
"functionality": "Increases or decreases a variable's numerical value by a specified amount.",
|
601 |
+
"inputs": {"VALUE": [1, [4, "1"]]}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
602 |
+
},
|
603 |
+
"data_showvariable": {
|
604 |
+
"block_name": "show variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showvariable",
|
605 |
+
"functionality": "Makes a variable's monitor visible on the stage.",
|
606 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
607 |
+
},
|
608 |
+
"data_hidevariable": {
|
609 |
+
"block_name": "hide variable [my variable v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidevariable",
|
610 |
+
"functionality": "Hides a variable's monitor from the stage.",
|
611 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", "`jEk@4|i[#Fk?(8x)AV.-my variable"]}, "shadow": False, "topLevel": True
|
612 |
+
},
|
613 |
+
"data_addtolist": {
|
614 |
+
"block_name": "add () to [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_addtolist",
|
615 |
+
"functionality": "Appends an item to the end of a list.",
|
616 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
617 |
+
},
|
618 |
+
"data_deleteoflist": {
|
619 |
+
"block_name": "delete () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deleteoflist",
|
620 |
+
"functionality": "Removes an item from a list by its index or by selecting 'all' items.",
|
621 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
622 |
+
},
|
623 |
+
"data_deletealloflist": {
|
624 |
+
"block_name": "delete all of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_deletealloflist",
|
625 |
+
"functionality": "Removes all items from a list.",
|
626 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
627 |
+
},
|
628 |
+
"data_insertatlist": {
|
629 |
+
"block_name": "insert () at () of [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_insertatlist",
|
630 |
+
"functionality": "Inserts an item at a specific position within a list.",
|
631 |
+
"inputs": {"ITEM": [1, [10, "thing"]], "INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
632 |
+
},
|
633 |
+
"data_replaceitemoflist": {
|
634 |
+
"block_name": "replace item () of [my list v] with ()", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_replaceitemoflist",
|
635 |
+
"functionality": "Replaces an item at a specific position in a list with a new value.",
|
636 |
+
"inputs": {"INDEX": [1, [7, "1"]], "ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
637 |
+
},
|
638 |
+
"data_itemoflist": {
|
639 |
+
"block_name": "(item (2) of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemoflist",
|
640 |
+
"functionality": "Reports the item located at a specific position in a list.",
|
641 |
+
"inputs": {"INDEX": [1, [7, "1"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
642 |
+
},
|
643 |
+
"data_itemnumoflist": {
|
644 |
+
"block_name": "(item # of [Dog] in [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_itemnumoflist",
|
645 |
+
"functionality": "Reports the index number of the first occurrence of a specified item in a list. If the item is not found, it reports 0.",
|
646 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
647 |
+
},
|
648 |
+
"data_lengthoflist": {
|
649 |
+
"block_name": "(length of [myList v])", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_lengthoflist",
|
650 |
+
"functionality": "Provides the total number of items contained in a list.",
|
651 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
652 |
+
},
|
653 |
+
"data_listcontainsitem": {
|
654 |
+
"block_name": "<[my list v] contains ()?>", "block_type": "Data", "block_shape": "Boolean Block", "op_code": "data_listcontainsitem",
|
655 |
+
"functionality": "Checks if a list includes a specific item.",
|
656 |
+
"inputs": {"ITEM": [1, [10, "thing"]]}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
657 |
+
},
|
658 |
+
"data_showlist": {
|
659 |
+
"block_name": "show list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_showlist",
|
660 |
+
"functionality": "Makes a list's monitor visible on the stage.",
|
661 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
662 |
+
},
|
663 |
+
"data_hidelist": {
|
664 |
+
"block_name": "hide list [my list v]", "block_type": "Data", "block_shape": "Stack Block", "op_code": "data_hidelist",
|
665 |
+
"functionality": "Hides a list's monitor from the stage.",
|
666 |
+
"inputs": {}, "fields": {"LIST": ["MY_LIST", "o6`kIhtT{xWH+rX(5d,A"]}, "shadow": False, "topLevel": True
|
667 |
+
},
|
668 |
+
"data_variable": { # This is a reporter block for a variable's value
|
669 |
+
"block_name": "[variable v]", "block_type": "Data", "block_shape": "Reporter Block", "op_code": "data_variable",
|
670 |
+
"functionality": "Provides the current value stored in a variable.",
|
671 |
+
"inputs": {}, "fields": {"VARIABLE": ["my variable", None]}, "shadow": True, "topLevel": False
|
672 |
+
},
|
673 |
+
|
674 |
+
# event_block.json
|
675 |
+
"event_whenflagclicked": {
|
676 |
+
"block_name": "when green flag pressed", "block_type": "Events", "op_code": "event_whenflagclicked", "block_shape": "Hat Block",
|
677 |
+
"functionality": "This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.",
|
678 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
679 |
+
},
|
680 |
+
"event_whenkeypressed": {
|
681 |
+
"block_name": "when () key pressed", "block_type": "Events", "op_code": "event_whenkeypressed", "block_shape": "Hat Block",
|
682 |
+
"functionality": "This Hat block initiates the script when a specified keyboard key is pressed.",
|
683 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": False, "topLevel": True
|
684 |
+
},
|
685 |
+
"event_whenthisspriteclicked": {
|
686 |
+
"block_name": "when this sprite clicked", "block_type": "Events", "op_code": "event_whenthisspriteclicked", "block_shape": "Hat Block",
|
687 |
+
"functionality": "This Hat block starts the script when the sprite itself is clicked.",
|
688 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
689 |
+
},
|
690 |
+
"event_whenbackdropswitchesto": {
|
691 |
+
"block_name": "when backdrop switches to ()", "block_type": "Events", "op_code": "event_whenbackdropswitchesto", "block_shape": "Hat Block",
|
692 |
+
"functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.",
|
693 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": False, "topLevel": True
|
694 |
+
},
|
695 |
+
"event_whengreaterthan": {
|
696 |
+
"block_name": "when () > ()", "block_type": "Events", "op_code": "event_whengreaterthan", "block_shape": "Hat Block",
|
697 |
+
"functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.",
|
698 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"WHENGREATERTHANMENU": ["LOUDNESS", None]}, "shadow": False, "topLevel": True
|
699 |
+
},
|
700 |
+
"event_whenbroadcastreceived": {
|
701 |
+
"block_name": "when I receive ()", "block_type": "Events", "op_code": "event_whenbroadcastreceived", "block_shape": "Hat Block",
|
702 |
+
"functionality": "This Hat block initiates the script upon the reception of a specific broadcast message. This mechanism facilitates indirect communication between sprites or the stage.",
|
703 |
+
"inputs": {}, "fields": {"BROADCAST_OPTION": ["message1", "5O!nei;S$!c!=hCT}0:a"]}, "shadow": False, "topLevel": True
|
704 |
+
},
|
705 |
+
"event_broadcast": {
|
706 |
+
"block_name": "broadcast ()", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcast",
|
707 |
+
"functionality": "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.",
|
708 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
709 |
+
},
|
710 |
+
"event_broadcastandwait": {
|
711 |
+
"block_name": "broadcast () and wait", "block_type": "Events", "block_shape": "Stack Block", "op_code": "event_broadcastandwait",
|
712 |
+
"functionality": "Sends a broadcast message and pauses the current script until all other scripts activated by that broadcast have completed their execution, ensuring sequential coordination.",
|
713 |
+
"inputs": {"BROADCAST_INPUT": [1, [11, "message1", "5O!nei;S$!c!=hCT}0:a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
714 |
+
},
|
715 |
+
|
716 |
+
# looks_block.json
|
717 |
+
"looks_sayforsecs": {
|
718 |
+
"block_name": "say () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_sayforsecs",
|
719 |
+
"functionality": "Displays a speech bubble containing specified text for a set duration.",
|
720 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True
|
721 |
+
},
|
722 |
+
"looks_say": {
|
723 |
+
"block_name": "say ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_say",
|
724 |
+
"functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
725 |
+
"inputs": {"MESSAGE": [1, [10, "Hello!"]]}, "fields": {}, "shadow": False, "topLevel": True
|
726 |
+
},
|
727 |
+
"looks_thinkforsecs": {
|
728 |
+
"block_name": "think () for () seconds", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_thinkforsecs",
|
729 |
+
"functionality": "Displays a thought bubble containing specified text for a set duration.",
|
730 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]], "SECS": [1, [4, "2"]]}, "fields": {}, "shadow": False, "topLevel": True
|
731 |
+
},
|
732 |
+
"looks_think": {
|
733 |
+
"block_name": "think ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_think",
|
734 |
+
"functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
735 |
+
"inputs": {"MESSAGE": [1, [10, "Hmm..."]]}, "fields": {}, "shadow": False, "topLevel": True
|
736 |
+
},
|
737 |
+
"looks_switchcostumeto": {
|
738 |
+
"block_name": "switch costume to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchcostumeto",
|
739 |
+
"functionality": "Alters the sprite's appearance to a designated costume.",
|
740 |
+
"inputs": {"COSTUME": [1, "looks_costume"]}, "fields": {}, "shadow": False, "topLevel": True
|
741 |
+
},
|
742 |
+
"looks_costume": {
|
743 |
+
"block_name": "costume menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costume",
|
744 |
+
"functionality": "Menu for switch costume to block.",
|
745 |
+
"inputs": {}, "fields": {"COSTUME": ["costume1", None]}, "shadow": True, "topLevel": False
|
746 |
+
},
|
747 |
+
"looks_nextcostume": {
|
748 |
+
"block_name": "next costume", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextcostume",
|
749 |
+
"functionality": "Switches the sprite's costume to the next one in its costume list. If it's the last costume, it cycles back to the first.",
|
750 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
751 |
+
},
|
752 |
+
"looks_switchbackdropto": {
|
753 |
+
"block_name": "switch backdrop to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdropto",
|
754 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop.",
|
755 |
+
"inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True
|
756 |
+
},
|
757 |
+
"looks_backdrops": {
|
758 |
+
"block_name": "backdrop menu", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdrops",
|
759 |
+
"functionality": "Menu for switch backdrop to block.",
|
760 |
+
"inputs": {}, "fields": {"BACKDROP": ["backdrop1", None]}, "shadow": True, "topLevel": False
|
761 |
+
},
|
762 |
+
"looks_switchbackdroptowait": {
|
763 |
+
"block_name": "switch backdrop to () and wait", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_switchbackdroptowait",
|
764 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop and pauses the script until any 'When backdrop switches to' scripts for that backdrop have finished.",
|
765 |
+
"inputs": {"BACKDROP": [1, "looks_backdrops"]}, "fields": {}, "shadow": False, "topLevel": True
|
766 |
+
},
|
767 |
+
"looks_nextbackdrop": {
|
768 |
+
"block_name": "next backdrop", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_nextbackdrop",
|
769 |
+
"functionality": "Switches the stage's backdrop to the next one in its backdrop list. If it's the last backdrop, it cycles back to the first.",
|
770 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
771 |
+
},
|
772 |
+
"looks_changesizeby": {
|
773 |
+
"block_name": "change size by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changesizeby",
|
774 |
+
"functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.",
|
775 |
+
"inputs": {"CHANGE": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
776 |
+
},
|
777 |
+
"looks_setsizeto": {
|
778 |
+
"block_name": "set size to () %", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_setsizeto",
|
779 |
+
"functionality": "Sets the sprite's size to a specific percentage of its original size.",
|
780 |
+
"inputs": {"SIZE": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True
|
781 |
+
},
|
782 |
+
"looks_changeeffectby": {
|
783 |
+
"block_name": "change () effect by ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_changeeffectby",
|
784 |
+
"functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).",
|
785 |
+
"inputs": {"CHANGE": [1, [4, "25"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True
|
786 |
+
},
|
787 |
+
"looks_seteffectto": {
|
788 |
+
"block_name": "set () effect to ()", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_seteffectto",
|
789 |
+
"functionality": "Sets a visual effect on the sprite to a specific value.",
|
790 |
+
"inputs": {"VALUE": [1, [4, "0"]]}, "fields": {"EFFECT": ["COLOR", None]}, "shadow": False, "topLevel": True
|
791 |
+
},
|
792 |
+
"looks_cleargraphiceffects": {
|
793 |
+
"block_name": "clear graphic effects", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_cleargraphiceffects",
|
794 |
+
"functionality": "Removes all visual effects applied to the sprite.",
|
795 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
796 |
+
},
|
797 |
+
"looks_show": {
|
798 |
+
"block_name": "show", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_show",
|
799 |
+
"functionality": "Makes the sprite visible on the stage.",
|
800 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
801 |
+
},
|
802 |
+
"looks_hide": {
|
803 |
+
"block_name": "hide", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_hide",
|
804 |
+
"functionality": "Makes the sprite invisible on the stage.",
|
805 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
806 |
+
},
|
807 |
+
"looks_gotofrontback": {
|
808 |
+
"block_name": "go to () layer", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_gotofrontback",
|
809 |
+
"functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.",
|
810 |
+
"inputs": {}, "fields": {"FRONT_BACK": ["front", None]}, "shadow": False, "topLevel": True
|
811 |
+
},
|
812 |
+
"looks_goforwardbackwardlayers": {
|
813 |
+
"block_name": "go () layers", "block_type": "Looks", "block_shape": "Stack Block", "op_code": "looks_goforwardbackwardlayers",
|
814 |
+
"functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.",
|
815 |
+
"inputs": {"NUM": [1, [7, "1"]]}, "fields": {"FORWARD_BACKWARD": ["forward", None]}, "shadow": False, "topLevel": True
|
816 |
+
},
|
817 |
+
"looks_costumenumbername": {
|
818 |
+
"block_name": "(costume ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_costumenumbername",
|
819 |
+
"functionality": "Reports the current costume's number or name.",
|
820 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True
|
821 |
+
},
|
822 |
+
"looks_backdropnumbername": {
|
823 |
+
"block_name": "(backdrop ())", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_backdropnumbername",
|
824 |
+
"functionality": "Reports the current backdrop's number or name.",
|
825 |
+
"inputs": {}, "fields": {"NUMBER_NAME": ["number", None]}, "shadow": False, "topLevel": True
|
826 |
+
},
|
827 |
+
"looks_size": {
|
828 |
+
"block_name": "(size)", "block_type": "Looks", "block_shape": "Reporter Block", "op_code": "looks_size",
|
829 |
+
"functionality": "Reports the current size of the sprite as a percentage.",
|
830 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
831 |
+
},
|
832 |
+
|
833 |
+
# operator_block.json
|
834 |
+
"operator_add": {
|
835 |
+
"block_name": "(() + ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_add",
|
836 |
+
"functionality": "Adds two numerical values.",
|
837 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
838 |
+
},
|
839 |
+
"operator_subtract": {
|
840 |
+
"block_name": "(() - ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_subtract",
|
841 |
+
"functionality": "Subtracts the second numerical value from the first.",
|
842 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
843 |
+
},
|
844 |
+
"operator_multiply": {
|
845 |
+
"block_name": "(() * ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_multiply",
|
846 |
+
"functionality": "Multiplies two numerical values.",
|
847 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
848 |
+
},
|
849 |
+
"operator_divide": {
|
850 |
+
"block_name": "(() / ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_divide",
|
851 |
+
"functionality": "Divides the first numerical value by the second.",
|
852 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
853 |
+
},
|
854 |
+
"operator_random": {
|
855 |
+
"block_name": "(pick random () to ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_random",
|
856 |
+
"functionality": "Generates a random integer within a specified inclusive range.",
|
857 |
+
"inputs": {"FROM": [1, [4, "1"]], "TO": [1, [4, "10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
858 |
+
},
|
859 |
+
"operator_gt": {
|
860 |
+
"block_name": "<() > ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_gt",
|
861 |
+
"functionality": "Checks if the first value is greater than the second.",
|
862 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
863 |
+
},
|
864 |
+
"operator_lt": {
|
865 |
+
"block_name": "<() < ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_lt",
|
866 |
+
"functionality": "Checks if the first value is less than the second.",
|
867 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
868 |
+
},
|
869 |
+
"operator_equals": {
|
870 |
+
"block_name": "<() = ()>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_equals",
|
871 |
+
"functionality": "Checks if two values are equal.",
|
872 |
+
"inputs": {"OPERAND1": [1, [10, ""]], "OPERAND2": [1, [10, "50"]]}, "fields": {}, "shadow": False, "topLevel": True
|
873 |
+
},
|
874 |
+
"operator_and": {
|
875 |
+
"block_name": "<<> and <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_and",
|
876 |
+
"functionality": "Returns 'true' if both provided Boolean conditions are 'true'.",
|
877 |
+
"inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
878 |
+
},
|
879 |
+
"operator_or": {
|
880 |
+
"block_name": "<<> or <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_or",
|
881 |
+
"functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.",
|
882 |
+
"inputs": {"OPERAND1": [2, None], "OPERAND2": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
883 |
+
},
|
884 |
+
"operator_not": {
|
885 |
+
"block_name": "<not <>>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_not",
|
886 |
+
"functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.",
|
887 |
+
"inputs": {"OPERAND": [2, None]}, "fields": {}, "shadow": False, "topLevel": True
|
888 |
+
},
|
889 |
+
"operator_join": {
|
890 |
+
"block_name": "(join ()())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_join",
|
891 |
+
"functionality": "Concatenates two strings or values into a single string.",
|
892 |
+
"inputs": {"STRING1": [1, [10, "apple "]], "STRING2": [1, [10, "banana"]]}, "fields": {}, "shadow": False, "topLevel": True
|
893 |
+
},
|
894 |
+
"operator_letterof": {
|
895 |
+
"block_name": "letter () of ()", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_letterof",
|
896 |
+
"functionality": "Reports the character at a specific numerical position within a string.",
|
897 |
+
"inputs": {"LETTER": [1, [6, "1"]], "STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True
|
898 |
+
},
|
899 |
+
"operator_length": {
|
900 |
+
"block_name": "(length of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_length",
|
901 |
+
"functionality": "Reports the total number of characters in a given string.",
|
902 |
+
"inputs": {"STRING": [1, [10, "apple"]]}, "fields": {}, "shadow": False, "topLevel": True
|
903 |
+
},
|
904 |
+
"operator_contains": {
|
905 |
+
"block_name": "<() contains ()?>", "block_type": "operator", "block_shape": "Boolean Block", "op_code": "operator_contains",
|
906 |
+
"functionality": "Checks if one string contains another string.",
|
907 |
+
"inputs": {"STRING1": [1, [10, "apple"]], "STRING2": [1, [10, "a"]]}, "fields": {}, "shadow": False, "topLevel": True
|
908 |
+
},
|
909 |
+
"operator_mod": {
|
910 |
+
"block_name": "(() mod ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mod",
|
911 |
+
"functionality": "Reports the remainder when the first number is divided by the second.",
|
912 |
+
"inputs": {"NUM1": [1, [4, ""]], "NUM2": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
913 |
+
},
|
914 |
+
"operator_round": {
|
915 |
+
"block_name": "(round ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_round",
|
916 |
+
"functionality": "Rounds a numerical value to the nearest integer.",
|
917 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {}, "shadow": False, "topLevel": True
|
918 |
+
},
|
919 |
+
"operator_mathop": {
|
920 |
+
"block_name": "(() of ())", "block_type": "operator", "block_shape": "Reporter Block", "op_code": "operator_mathop",
|
921 |
+
"functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).",
|
922 |
+
"inputs": {"NUM": [1, [4, ""]]}, "fields": {"OPERATOR": ["abs", None]}, "shadow": False, "topLevel": True
|
923 |
+
},
|
924 |
+
|
925 |
+
# sensing_block.json
|
926 |
+
"sensing_touchingobject": {
|
927 |
+
"block_name": "<touching [edge v]?>", "block_type": "Sensing", "op_code": "sensing_touchingobject", "block_shape": "Boolean Block",
|
928 |
+
"functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.",
|
929 |
+
"inputs": {"TOUCHINGOBJECTMENU": [1, "sensing_touchingobjectmenu"]}, "fields": {}, "shadow": False, "topLevel": True
|
930 |
+
},
|
931 |
+
"sensing_touchingobjectmenu": {
|
932 |
+
"block_name": "touching object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_touchingobjectmenu",
|
933 |
+
"functionality": "Menu for touching object block.",
|
934 |
+
"inputs": {}, "fields": {"TOUCHINGOBJECTMENU": ["_mouse_", None]}, "shadow": True, "topLevel": False
|
935 |
+
},
|
936 |
+
"sensing_touchingcolor": {
|
937 |
+
"block_name": "<touching color ()?>", "block_type": "Sensing", "op_code": "sensing_touchingcolor", "block_shape": "Boolean Block",
|
938 |
+
"functionality": "Checks whether its sprite is touching a specified color.",
|
939 |
+
"inputs": {"COLOR": [1, [9, "#55b888"]]}, "fields": {}, "shadow": False, "topLevel": True
|
940 |
+
},
|
941 |
+
"sensing_coloristouchingcolor": {
|
942 |
+
"block_name": "<color () is touching ()?>", "block_type": "Sensing", "op_code": "sensing_coloristouchingcolor", "block_shape": "Boolean Block",
|
943 |
+
"functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.",
|
944 |
+
"inputs": {"COLOR1": [1, [9, "#d019f2"]], "COLOR2": [1, [9, "#2b0de3"]]}, "fields": {}, "shadow": False, "topLevel": True
|
945 |
+
},
|
946 |
+
"sensing_askandwait": {
|
947 |
+
"block_name": "Ask () and Wait", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_askandwait",
|
948 |
+
"functionality": "Displays an input box with specified text at the bottom of the screen, allowing users to input text, which is stored in the 'Answer' block.",
|
949 |
+
"inputs": {"QUESTION": [1, [10, "What's your name?"]]}, "fields": {}, "shadow": False, "topLevel": True
|
950 |
+
},
|
951 |
+
"sensing_answer": {
|
952 |
+
"block_name": "(answer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_answer",
|
953 |
+
"functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.",
|
954 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
955 |
+
},
|
956 |
+
"sensing_keypressed": {
|
957 |
+
"block_name": "<key () pressed?>", "block_type": "Sensing", "op_code": "sensing_keypressed", "block_shape": "Boolean Block",
|
958 |
+
"functionality": "Checks if a specified keyboard key is currently being pressed.",
|
959 |
+
"inputs": {"KEY_OPTION": [1, "sensing_keyoptions"]}, "fields": {}, "shadow": False, "topLevel": True
|
960 |
+
},
|
961 |
+
"sensing_keyoptions": {
|
962 |
+
"block_name": "key options menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_keyoptions",
|
963 |
+
"functionality": "Menu for key pressed block.",
|
964 |
+
"inputs": {}, "fields": {"KEY_OPTION": ["space", None]}, "shadow": True, "topLevel": False
|
965 |
+
},
|
966 |
+
"sensing_mousedown": {
|
967 |
+
"block_name": "<mouse down?>", "block_type": "Sensing", "op_code": "sensing_mousedown", "block_shape": "Boolean Block",
|
968 |
+
"functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.",
|
969 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
970 |
+
},
|
971 |
+
"sensing_mousex": {
|
972 |
+
"block_name": "(mouse x)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousex",
|
973 |
+
"functionality": "Reports the mouse-pointer’s current X position on the stage.",
|
974 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
975 |
+
},
|
976 |
+
"sensing_mousey": {
|
977 |
+
"block_name": "(mouse y)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_mousey",
|
978 |
+
"functionality": "Reports the mouse-pointer’s current Y position on the stage.",
|
979 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
980 |
+
},
|
981 |
+
"sensing_setdragmode": {
|
982 |
+
"block_name": "set drag mode [draggable v]", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_setdragmode",
|
983 |
+
"functionality": "Sets whether the sprite can be dragged by the mouse on the stage.",
|
984 |
+
"inputs": {}, "fields": {"DRAG_MODE": ["draggable", None]}, "shadow": False, "topLevel": True
|
985 |
+
},
|
986 |
+
"sensing_loudness": {
|
987 |
+
"block_name": "(loudness)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_loudness",
|
988 |
+
"functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.",
|
989 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
990 |
+
},
|
991 |
+
"sensing_timer": {
|
992 |
+
"block_name": "(timer)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_timer",
|
993 |
+
"functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.",
|
994 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
995 |
+
},
|
996 |
+
"sensing_resettimer": {
|
997 |
+
"block_name": "Reset Timer", "block_type": "Sensing", "block_shape": "Stack Block", "op_code": "sensing_resettimer",
|
998 |
+
"functionality": "Sets the timer’s value back to 0.0.",
|
999 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1000 |
+
},
|
1001 |
+
"sensing_of": {
|
1002 |
+
"block_name": "(() of ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of",
|
1003 |
+
"functionality": "Reports a specified value (e.g., x position, direction, costume number) of a specified sprite or the Stage to be accessed in current sprite or stage.",
|
1004 |
+
"inputs": {"OBJECT": [1, "sensing_of_object_menu"]}, "fields": {"PROPERTY": ["backdrop #", None]}, "shadow": False, "topLevel": True
|
1005 |
+
},
|
1006 |
+
"sensing_of_object_menu": {
|
1007 |
+
"block_name": "of object menu", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_of_object_menu",
|
1008 |
+
"functionality": "Menu for of block.",
|
1009 |
+
"inputs": {}, "fields": {"OBJECT": ["_stage_", None]}, "shadow": True, "topLevel": False
|
1010 |
+
},
|
1011 |
+
"sensing_current": {
|
1012 |
+
"block_name": "(current ())", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_current",
|
1013 |
+
"functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.",
|
1014 |
+
"inputs": {}, "fields": {"CURRENTMENU": ["YEAR", None]}, "shadow": False, "topLevel": True
|
1015 |
+
},
|
1016 |
+
"sensing_dayssince2000": {
|
1017 |
+
"block_name": "(days since 2000)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_dayssince2000",
|
1018 |
+
"functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.",
|
1019 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1020 |
+
},
|
1021 |
+
"sensing_username": {
|
1022 |
+
"block_name": "(username)", "block_type": "Sensing", "block_shape": "Reporter Block", "op_code": "sensing_username",
|
1023 |
+
"functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.",
|
1024 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1025 |
+
},
|
1026 |
+
|
1027 |
+
# sound_block.json
|
1028 |
+
"sound_playuntildone": {
|
1029 |
+
"block_name": "play sound () until done", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_playuntildone",
|
1030 |
+
"functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.",
|
1031 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
1032 |
+
},
|
1033 |
+
"sound_sounds_menu": {
|
1034 |
+
"block_name": "sound menu", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_sounds_menu",
|
1035 |
+
"functionality": "Menu for sound blocks.",
|
1036 |
+
"inputs": {}, "fields": {"SOUND_MENU": ["Meow", None]}, "shadow": True, "topLevel": False
|
1037 |
+
},
|
1038 |
+
"sound_play": {
|
1039 |
+
"block_name": "start sound ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_play",
|
1040 |
+
"functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.",
|
1041 |
+
"inputs": {"SOUND_MENU": [1, "sound_sounds_menu"]}, "fields": {}, "shadow": False, "topLevel": True
|
1042 |
+
},
|
1043 |
+
"sound_stopallsounds": {
|
1044 |
+
"block_name": "stop all sounds", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_stopallsounds",
|
1045 |
+
"functionality": "Stops all currently playing sounds.",
|
1046 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1047 |
+
},
|
1048 |
+
"sound_changeeffectby": {
|
1049 |
+
"block_name": "change () effect by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changeeffectby",
|
1050 |
+
"functionality": "Changes the project's sound effect by a specified amount.",
|
1051 |
+
"inputs": {"VALUE": [1, [4, "10"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True
|
1052 |
+
},
|
1053 |
+
"sound_seteffectto": {
|
1054 |
+
"block_name": "set () effect to ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_seteffectto",
|
1055 |
+
"functionality": "Sets the sound effect to a specific value.",
|
1056 |
+
"inputs": {"VALUE": [1, [4, "100"]]}, "fields": {"EFFECT": ["PITCH", None]}, "shadow": False, "topLevel": True
|
1057 |
+
},
|
1058 |
+
"sound_cleareffects": {
|
1059 |
+
"block_name": "clear sound effects", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_cleareffects",
|
1060 |
+
"functionality": "Removes all sound effects applied to the sprite.",
|
1061 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1062 |
+
},
|
1063 |
+
"sound_changevolumeby": {
|
1064 |
+
"block_name": "change volume by ()", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_changevolumeby",
|
1065 |
+
"functionality": "Changes the project's sound volume by a specified amount.",
|
1066 |
+
"inputs": {"VOLUME": [1, [4, "-10"]]}, "fields": {}, "shadow": False, "topLevel": True
|
1067 |
+
},
|
1068 |
+
"sound_setvolumeto": {
|
1069 |
+
"block_name": "set volume to () %", "block_type": "Sound", "block_shape": "Stack Block", "op_code": "sound_setvolumeto",
|
1070 |
+
"functionality": "Sets the sound volume to a specific percentage (0-100).",
|
1071 |
+
"inputs": {"VOLUME": [1, [4, "100"]]}, "fields": {}, "shadow": False, "topLevel": True
|
1072 |
+
},
|
1073 |
+
"sound_volume": {
|
1074 |
+
"block_name": "(volume)", "block_type": "Sound", "block_shape": "Reporter Block", "op_code": "sound_volume",
|
1075 |
+
"functionality": "Reports the current volume level of the sprite.",
|
1076 |
+
"inputs": {}, "fields": {}, "shadow": False, "topLevel": True
|
1077 |
+
},
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
# Example input with opcodes for the initial generation
|
1081 |
+
initial_opcode_counts = [
|
1082 |
+
{"opcode":"event_whenflagclicked","count":1},
|
1083 |
+
{"opcode":"motion_gotoxy","count":1},
|
1084 |
+
{"opcode":"motion_glidesecstoxy","count":1},
|
1085 |
+
{"opcode":"motion_xposition","count":1},
|
1086 |
+
{"opcode":"motion_setx","count":1},
|
1087 |
+
{"opcode":"control_forever","count":1},
|
1088 |
+
{"opcode":"control_if","count":1},
|
1089 |
+
{"opcode":"control_stop","count":1},
|
1090 |
+
{"opcode":"operator_lt","count":1},
|
1091 |
+
{"opcode":"sensing_touchingobject","count":1}, # Changed from sensing_istouching
|
1092 |
+
{"opcode":"sensing_touchingobjectmenu","count":1},
|
1093 |
+
{"opcode":"event_broadcast","count":1},
|
1094 |
+
{"opcode":"data_setvariableto","count":2},
|
1095 |
+
{"opcode":"data_showvariable","count":2},
|
1096 |
+
]
|
1097 |
+
|
1098 |
+
# Generate the initial blocks and get the opcode_occurrences
|
1099 |
+
generated_output_json, initial_opcode_occurrences = generate_blocks_from_opcodes(initial_opcode_counts, all_block_definitions)
|
1100 |
+
|
1101 |
+
# Pseudo-code to interpret
|
1102 |
+
pseudo_code_input = """
|
1103 |
+
when green flag clicked
|
1104 |
+
go to x: (240) y: (-135)
|
1105 |
+
set [score v] to (1)
|
1106 |
+
set [speed v] to (1)
|
1107 |
+
show variable [score v]
|
1108 |
+
show variable [speed v]
|
1109 |
+
forever
|
1110 |
+
glide (2) seconds to x: (-240) y: (-135)
|
1111 |
+
if <((x position)) < (-235)> then
|
1112 |
+
set x to (240)
|
1113 |
+
end
|
1114 |
+
if <touching [Sprite1 v]?> then
|
1115 |
+
broadcast [Game Over v]
|
1116 |
+
stop [all v]
|
1117 |
+
end
|
1118 |
+
end
|
1119 |
+
end
|
1120 |
+
"""
|
1121 |
+
|
1122 |
+
# Interpret the pseudo-code and update the blocks, passing opcode_occurrences
|
1123 |
+
final_generated_blocks = interpret_pseudo_code_and_update_blocks(generated_output_json, pseudo_code_input, all_block_definitions, initial_opcode_occurrences)
|
1124 |
+
|
1125 |
+
print(initial_opcode_occurrences)
|
1126 |
+
|
1127 |
+
#print(json.dumps(final_generated_blocks, indent=2))
|
1128 |
+
# ```json
|
1129 |
+
# {
|
1130 |
+
# "event_whenflagclicked_1": {
|
1131 |
+
# "block_name": "when green flag pressed",
|
1132 |
+
# "block_type": "Events",
|
1133 |
+
# "op_code": "event_whenflagclicked",
|
1134 |
+
# "block_shape": "Hat Block",
|
1135 |
+
# "functionality": "This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.",
|
1136 |
+
# "inputs": {},
|
1137 |
+
# "fields": {},
|
1138 |
+
# "shadow": false,
|
1139 |
+
# "topLevel": true,
|
1140 |
+
# "parent": null,
|
1141 |
+
# "next": "motion_gotoxy_1"
|
1142 |
+
# },
|
1143 |
+
# "motion_gotoxy_1": {
|
1144 |
+
# "block_name": "go to x: () y: ()",
|
1145 |
+
# "block_type": "Motion",
|
1146 |
+
# "op_code": "motion_gotoxy",
|
1147 |
+
# "functionality": "Moves the sprite to the specified X and Y coordinates on the stage.",
|
1148 |
+
# "inputs": {
|
1149 |
+
# "X": [
|
1150 |
+
# 4,
|
1151 |
+
# "240"
|
1152 |
+
# ],
|
1153 |
+
# "Y": [
|
1154 |
+
# 4,
|
1155 |
+
# "-135"
|
1156 |
+
# ]
|
1157 |
+
# },
|
1158 |
+
# "fields": {},
|
1159 |
+
# "shadow": false,
|
1160 |
+
# "topLevel": false,
|
1161 |
+
# "parent": null,
|
1162 |
+
# "next": "data_setvariableto_1"
|
1163 |
+
# },
|
1164 |
+
# "motion_glidesecstoxy_1": {
|
1165 |
+
# "block_name": "glide () secs to x: () y: ()",
|
1166 |
+
# "block_type": "Motion",
|
1167 |
+
# "op_code": "motion_glidesecstoxy",
|
1168 |
+
# "functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.",
|
1169 |
+
# "inputs": {
|
1170 |
+
# "SECS": [
|
1171 |
+
# 4,
|
1172 |
+
# "2"
|
1173 |
+
# ],
|
1174 |
+
# "X": [
|
1175 |
+
# 4,
|
1176 |
+
# "-240"
|
1177 |
+
# ],
|
1178 |
+
# "Y": [
|
1179 |
+
# 4,
|
1180 |
+
# "-135"
|
1181 |
+
# ]
|
1182 |
+
# },
|
1183 |
+
# "fields": {},
|
1184 |
+
# "shadow": false,
|
1185 |
+
# "topLevel": false,
|
1186 |
+
# "parent": "control_forever_1",
|
1187 |
+
# "next": "control_if_1"
|
1188 |
+
# },
|
1189 |
+
# "motion_xposition_1": {
|
1190 |
+
# "block_name": "(x position)",
|
1191 |
+
# "block_type": "Motion",
|
1192 |
+
# "op_code": "motion_xposition",
|
1193 |
+
# "functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]",
|
1194 |
+
# "inputs": {},
|
1195 |
+
# "fields": {},
|
1196 |
+
# "shadow": true,
|
1197 |
+
# "topLevel": false,
|
1198 |
+
# "parent": "operator_lt_1",
|
1199 |
+
# "next": null
|
1200 |
+
# },
|
1201 |
+
# "motion_setx_1": {
|
1202 |
+
# "block_name": "set x to ()",
|
1203 |
+
# "block_type": "Motion",
|
1204 |
+
# "op_code": "motion_setx",
|
1205 |
+
# "functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.",
|
1206 |
+
# "inputs": {
|
1207 |
+
# "X": [
|
1208 |
+
# 4,
|
1209 |
+
# "240"
|
1210 |
+
# ]
|
1211 |
+
# },
|
1212 |
+
# "fields": {},
|
1213 |
+
# "shadow": false,
|
1214 |
+
# "topLevel": false,
|
1215 |
+
# "parent": "control_if_1",
|
1216 |
+
# "next": null
|
1217 |
+
# },
|
1218 |
+
# "control_forever_1": {
|
1219 |
+
# "block_name": "forever",
|
1220 |
+
# "block_type": "Control",
|
1221 |
+
# "op_code": "control_forever",
|
1222 |
+
# "functionality": "Continuously runs the blocks inside it.",
|
1223 |
+
# "inputs": {
|
1224 |
+
# "SUBSTACK": [
|
1225 |
+
# 2,
|
1226 |
+
# "motion_glidesecstoxy_1"
|
1227 |
+
# ]
|
1228 |
+
# },
|
1229 |
+
# "fields": {},
|
1230 |
+
# "shadow": false,
|
1231 |
+
# "topLevel": false,
|
1232 |
+
# "parent": null,
|
1233 |
+
# "next": null
|
1234 |
+
# },
|
1235 |
+
# "control_if_1": {
|
1236 |
+
# "block_name": "if <> then",
|
1237 |
+
# "block_type": "Control",
|
1238 |
+
# "op_code": "control_if",
|
1239 |
+
# "functionality": "Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]",
|
1240 |
+
# "inputs": {
|
1241 |
+
# "CONDITION": [
|
1242 |
+
# 2,
|
1243 |
+
# "operator_lt_1"
|
1244 |
+
# ],
|
1245 |
+
# "SUBSTACK": [
|
1246 |
+
# 2,
|
1247 |
+
# "motion_setx_1"
|
1248 |
+
# ]
|
1249 |
+
# },
|
1250 |
+
# "fields": {},
|
1251 |
+
# "shadow": false,
|
1252 |
+
# "topLevel": false,
|
1253 |
+
# "parent": "control_forever_1",
|
1254 |
+
# "next": "control_if_2"
|
1255 |
+
# },
|
1256 |
+
# "control_stop_1": {
|
1257 |
+
# "block_name": "stop [v]",
|
1258 |
+
# "block_type": "Control",
|
1259 |
+
# "op_code": "control_stop",
|
1260 |
+
# "functionality": "Halts all scripts, only the current script, or other scripts within the same sprite. Its shape can dynamically change based on the selected option.",
|
1261 |
+
# "inputs": {},
|
1262 |
+
# "fields": {
|
1263 |
+
# "STOP_OPTION": [
|
1264 |
+
# "all",
|
1265 |
+
# null
|
1266 |
+
# ]
|
1267 |
+
# },
|
1268 |
+
# "shadow": false,
|
1269 |
+
# "topLevel": false,
|
1270 |
+
# "parent": "control_if_2",
|
1271 |
+
# "mutation": {
|
1272 |
+
# "tagName": "mutation",
|
1273 |
+
# "children": [],
|
1274 |
+
# "hasnext": "false"
|
1275 |
+
# },
|
1276 |
+
# "next": null
|
1277 |
+
# },
|
1278 |
+
# "operator_lt_1": {
|
1279 |
+
# "block_name": "<() < ()>",
|
1280 |
+
# "block_type": "operator",
|
1281 |
+
# "op_code": "operator_lt",
|
1282 |
+
# "functionality": "Checks if the first value is less than the second.",
|
1283 |
+
# "inputs": {
|
1284 |
+
# "OPERAND1": [
|
1285 |
+
# 1,
|
1286 |
+
# "motion_xposition_1"
|
1287 |
+
# ],
|
1288 |
+
# "OPERAND2": [
|
1289 |
+
# 4,
|
1290 |
+
# "-235"
|
1291 |
+
# ]
|
1292 |
+
# },
|
1293 |
+
# "fields": {},
|
1294 |
+
# "shadow": true,
|
1295 |
+
# "topLevel": false,
|
1296 |
+
# "parent": "control_if_1",
|
1297 |
+
# "next": null
|
1298 |
+
# },
|
1299 |
+
# "sensing_touchingobject_1": {
|
1300 |
+
# "block_name": "<touching [edge v]?>",
|
1301 |
+
# "block_type": "Sensing",
|
1302 |
+
# "op_code": "sensing_touchingobject",
|
1303 |
+
# "functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.",
|
1304 |
+
# "inputs": {
|
1305 |
+
# "TOUCHINGOBJECTMENU": [
|
1306 |
+
# 1,
|
1307 |
+
# "sensing_touchingobjectmenu_1"
|
1308 |
+
# ]
|
1309 |
+
# },
|
1310 |
+
# "fields": {},
|
1311 |
+
# "shadow": true,
|
1312 |
+
# "topLevel": false,
|
1313 |
+
# "parent": "control_if_2",
|
1314 |
+
# "next": null
|
1315 |
+
# },
|
1316 |
+
# "sensing_touchingobjectmenu_1": {
|
1317 |
+
# "block_name": "touching object menu",
|
1318 |
+
# "block_type": "Sensing",
|
1319 |
+
# "op_code": "sensing_touchingobjectmenu",
|
1320 |
+
# "functionality": "Menu for touching object block.",
|
1321 |
+
# "inputs": {},
|
1322 |
+
# "fields": {
|
1323 |
+
# "TOUCHINGOBJECTMENU": [
|
1324 |
+
# "Sprite1",
|
1325 |
+
# null
|
1326 |
+
# ]
|
1327 |
+
# },
|
1328 |
+
# "shadow": true,
|
1329 |
+
# "topLevel": false,
|
1330 |
+
# "parent": "sensing_touchingobject_1",
|
1331 |
+
# "next": null
|
1332 |
+
# },
|
1333 |
+
# "event_broadcast_1": {
|
1334 |
+
# "block_name": "broadcast ()",
|
1335 |
+
# "block_type": "Events",
|
1336 |
+
# "op_code": "event_broadcast",
|
1337 |
+
# "functionality": "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.",
|
1338 |
+
# "inputs": {
|
1339 |
+
# "BROADCAST_INPUT": [
|
1340 |
+
# 10,
|
1341 |
+
# "Game Over"
|
1342 |
+
# ]
|
1343 |
+
# },
|
1344 |
+
# "fields": {},
|
1345 |
+
# "shadow": false,
|
1346 |
+
# "topLevel": false,
|
1347 |
+
# "parent": "control_if_2",
|
1348 |
+
# "next": "control_stop_1"
|
1349 |
+
# },
|
1350 |
+
# "data_setvariableto_1": {
|
1351 |
+
# "block_name": "set [my variable v] to ()",
|
1352 |
+
# "block_type": "Data",
|
1353 |
+
# "op_code": "data_setvariableto",
|
1354 |
+
# "functionality": "Assigns a specific value (number, string, or boolean) to a variable.",
|
1355 |
+
# "inputs": {
|
1356 |
+
# "VALUE": [
|
1357 |
+
# 4,
|
1358 |
+
# "1"
|
1359 |
+
# ]
|
1360 |
+
# },
|
1361 |
+
# "fields": {
|
1362 |
+
# "VARIABLE": [
|
1363 |
+
# "score",
|
1364 |
+
# "`var_score"
|
1365 |
+
# ]
|
1366 |
+
# },
|
1367 |
+
# "shadow": false,
|
1368 |
+
# "topLevel": false,
|
1369 |
+
# "parent": null,
|
1370 |
+
# "next": "data_setvariableto_2"
|
1371 |
+
# },
|
1372 |
+
# "data_setvariableto_2": {
|
1373 |
+
# "block_name": "set [my variable v] to ()",
|
1374 |
+
# "block_type": "Data",
|
1375 |
+
# "op_code": "data_setvariableto",
|
1376 |
+
# "functionality": "Assigns a specific value (number, string, or boolean) to a variable.",
|
1377 |
+
# "inputs": {
|
1378 |
+
# "VALUE": [
|
1379 |
+
# 4,
|
1380 |
+
# "1"
|
1381 |
+
# ]
|
1382 |
+
# },
|
1383 |
+
# "fields": {
|
1384 |
+
# "VARIABLE": [
|
1385 |
+
# "speed",
|
1386 |
+
# "`var_speed"
|
1387 |
+
# ]
|
1388 |
+
# },
|
1389 |
+
# "shadow": false,
|
1390 |
+
# "topLevel": false,
|
1391 |
+
# "parent": null,
|
1392 |
+
# "next": "data_showvariable_1"
|
1393 |
+
# },
|
1394 |
+
# "data_showvariable_1": {
|
1395 |
+
# "block_name": "show variable [my variable v]",
|
1396 |
+
# "block_type": "Data",
|
1397 |
+
# "op_code": "data_showvariable",
|
1398 |
+
# "functionality": "Makes a variable's monitor visible on the stage.",
|
1399 |
+
# "inputs": {},
|
1400 |
+
# "fields": {
|
1401 |
+
# "VARIABLE": [
|
1402 |
+
# "score",
|
1403 |
+
# "`var_score"
|
1404 |
+
# ]
|
1405 |
+
# },
|
1406 |
+
# "shadow": false,
|
1407 |
+
# "topLevel": false,
|
1408 |
+
# "parent": null,
|
1409 |
+
# "next": "data_showvariable_2"
|
1410 |
+
# },
|
1411 |
+
# "data_showvariable_2": {
|
1412 |
+
# "block_name": "show variable [my variable v]",
|
1413 |
+
# "block_type": "Data",
|
1414 |
+
# "op_code": "data_showvariable",
|
1415 |
+
# "functionality": "Makes a variable's monitor visible on the stage.",
|
1416 |
+
# "inputs": {},
|
1417 |
+
# "fields": {
|
1418 |
+
# "VARIABLE": [
|
1419 |
+
# "speed",
|
1420 |
+
# "`var_speed"
|
1421 |
+
# ]
|
1422 |
+
# },
|
1423 |
+
# "shadow": false,
|
1424 |
+
# "topLevel": false,
|
1425 |
+
# "parent": null,
|
1426 |
+
# "next": "control_forever_1"
|
1427 |
+
# },
|
1428 |
+
# "control_if_2": {
|
1429 |
+
# "block_name": "if <> then",
|
1430 |
+
# "block_type": "Control",
|
1431 |
+
# "op_code": "control_if",
|
1432 |
+
# "functionality": "Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]",
|
1433 |
+
# "inputs": {
|
1434 |
+
# "CONDITION": [
|
1435 |
+
# 2,
|
1436 |
+
# "sensing_touchingobject_1"
|
1437 |
+
# ],
|
1438 |
+
# "SUBSTACK": [
|
1439 |
+
# 2,
|
1440 |
+
# "event_broadcast_1"
|
1441 |
+
# ]
|
1442 |
+
# },
|
1443 |
+
# "fields": {},
|
1444 |
+
# "shadow": false,
|
1445 |
+
# "topLevel": false,
|
1446 |
+
# "parent": "control_forever_1",
|
1447 |
+
# "next": null
|
1448 |
+
# }
|
1449 |
+
# }
|
v2/utils/testing.ipynb
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:723bba2d91688a68ec24c08e909950748e0798ca288b015ab8ad32c19d852311
|
3 |
+
size 13761971
|