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
|